WEB后端服务第10天
一、WEB异步服务编程
1.1 概念
1.1.1 并行与并发的概念
并行指定多个任务同时在运行,一般指的是多进程(多核CPU) ,当然多线程也可以并行运行(受GIL全局解释器锁, 即同一时间点只能有一个线程在运行)。
并发指定在一定的时间段内,多个任务需要同时运行, 一般指的是多线程。特别是C10K, 解决办法是异步+消息队列。
1.1.2 同步与异步的概念
同步是指程序调用某一任务时,要等待这个任务完成并返回后,程序再继续向下执行。
异步是指程序调用某一任务时,不需要等待这个任务完成,程序继续向下执行。异步操作时,可以指定回调接口(函数),并任务完成后,调用回调接口回传任务完成后的数据。
1.1.3 协程
协程是"微线程", 不需要CPU调度,由事件循环器EventLoop(来源于IO多路复用)来监督,由用户自己调度。Python从3.4之后,提供协程包, asyncio库,声明某一函数是协程则需要@asyncio.coroutine修饰 或 async 标识, 如果在协程中设用哪一个协程则使用 yield from 或 await标识。
#!/usr/bin/python3
# coding: utf-8
import asyncio
import requests
async def download(url):
print('%s 下载中' % url)
await asyncio.sleep(1)
resp = requests.get(url)
return resp.content, resp.status_code
@asyncio.coroutine
def write_file(filename, content):
with open(filename, 'wb') as f:
f.write(content)
print(filename, ' Write OK')
@asyncio.coroutine
def save(url, filename):
content, code = yield from download(url)
print(url, code)
yield from write_file(filename, content)
print(url, filename, '保存成功!')
if __name__ == '__main__':
# 获取事件循环器对象
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait([
save('https://www.baidu.com', 'baidu.html'),
save('https://jd.com', 'jd.html'),
save('https://mail.qq.com', 'qq_mail.html'),
]))
1.2 发起同步请求
from tornado.httpclient import HTTPClient
from tornado.web import RequestHandler
class DownloadHandler(RequestHandler):
def get(self):
url = "http://www.baidu.com"
client = HTTPClient() # requests.get()
# 同步发送请求
resp = client.fetch(url, validata_cert=False)
# resp.body
# resp.code
# resp.headers
with open('index.html', 'wb') as f:
f.write(resp.body)
self.write('下载成功')
HTTPClient 是HTTP请求的客户端类。
client.fetch(request) 发送请求, request可以是str字符类型的URL, 也可以HTTPRequest类对象。
1.3 发起异步请求
from tornado.httpclient import AsyncHTTPClient
from tornado.web import RequestHandler
class DownloadHandler(RequestHandler):
def save(self, response):
# 声明回调函数, 参数中必须存在response对象
with open('index.html', 'wb') as f:
f.write(resp.body)
self.write('下载成功')
self.finish() # 关闭连接
@tornado.web.asynchronous # 保持连接
def get(self):
url = "http://www.baidu.com"
client = AsyncHTTPClient() # requests.get()
# 异步发送请求
client.fetch(url,callback=self.save, validata_cert=False)
@tornado.web.asynchronous 让请求方法变成长连接,等待finish()出现,才会关闭连接。
1.4 协程式请求
import tornado.web
from tornado.web import gen
class DownloadHandler(RequestHandler):
def save(self, response):
# 声明回调函数, 参数中必须存在response对象
with open('index.html', 'wb') as f:
f.write(resp.body)
self.write('下载成功')
self.finish() # 关闭连接
@tornado.web.asynchronous # 保持连接
@gen.coroutine
def get(self):
url = "http://www.baidu.com"
client = AsyncHTTPClient() # requests.get()
# 同步方式发送请求
response = yield client.fetch(url, validata_cert=False)
self.save(response)
可以将 @gen.coroutine 改成 async 标识, 将yield 改为await。
二、WebSocket聊天室
2.1 原生的socket通信
Server端的代码如下:
#!/usr/bin/python3
# coding: utf-8
import socket
# 1. 创建socket(实现网络之间的通信、还可以实现进程间通信)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定host和port端口
server.bind(('', 8011))
# 3. 监听
server.listen()
# 4. 等待接收客户端的连接
print('服务器已启动,等待连接...')
client, address = server.accept() # 阻塞的方法
print('%s 已连接' % address[0])
# 5. 向客户端发送消息
client.send('您好,我是小AI同学,很高兴认识您!'.encode('utf-8'))
# 6. 待着客户端发来消息
# 4096 表示接收的字节大小
msg = client.recv(4096) # 阻塞方法
print(address, '说: ', msg.decode())
client.close()
server.close()
Client客户端连接Server, 代码如下:
#!/usr/bin/python3
# coding: utf-8
import socket
# 1. 创建socket
socket = socket.socket()
# 2. 连接服务器
socket.connect(('localhost', 8011))
# 3. 接收数据
msg = socket.recv(4096) # 阻塞
print('Server: ', msg.decode('utf-8'))
# 4. 向服务端发送数据
socket.send('您好, 我想听首张学友的歌!'.encode('utf-8'))
# 关闭
socket.close()
三、其它Tornado知识点
Tornado之源码解析参考: https://blog.csdn.net/fenglei0415/article/details/84029012
3.1 三种启动方式
tornado采用多进程+异步+epoll的模型,可以提供比较强大的网络响应性能。通过Nginx+tornado一起部署,可以同时支持多个实例的运行,从而支持加倍的请求响应,可达数千并发连接。
3.1.1 单进程 single-process
server = HTTPServer(app)
server.listen(8888)
IOLoop.current().start()
3.1.2 简单的多进程 simple multi-process
server = HTTPServer(app)
server.bind(8888)
server.start(0) # Forks multiple sub-processes // Forks 多个单进程
IOLoop.current().start()
3.1.3 高级的多进程 advanced multi-process:
sockets = tornado.netutil.bind_sockets(8888)
tornado.process.fork_processes(0) # 10
server = HTTPServer(app)
server.add_sockets(sockets)
IOLoop.current().start()
3.2 封装RequestHandler和Application
3.3 安全Cookie及XSRF(跨站请求伪造)
配置:
settings = {
'cookie_secret': 'aadkakkkda',
'xsrf_cookies': True
}
用法
value = self.get_secure_cookie(name)
self.set_secure_cookie(name, value)