先上第一部分:
https://blog.csdn.net/csdniter/article/details/98500422
第一部分是从支持协议出发,接着第二部分
从工作模式出发:
这里指的是单线程下的工作模式,分为同步模式和异步非阻塞模式,
两者的公共点是都能同时接受多个请求并处理,只是处理方式不同
Django只支持同步模式
Tornado两种都支持
先说什么是同步模式:
指的是一个请求到达之后,服务器会开始处理,只有处理完成之后,才能处理下一个请求。也就是说一个一个顺序地处理请求。我们知道,一般的请求都属于IO密集型,不占用CPU,导致服务器CPU闲置,也会造成请求缓慢的结果
那什么是异步非阻塞模式呢?
异步非阻塞模式就是为了解决同步模式性能问题而产生的。一个请求到达之后,如果是IO请求,服务器不阻塞,能够立即处理下一个请求。在上一个请求有结果之后再去处理,如此性能上有很大的提升
同步模式的实现原理
主要是socket非阻塞加上事件循环(select/epoll)
这里不再详细阐述,可参考
https://blog.csdn.net/csdniter/article/details/97709752 中的第二中方法
异步非阻塞模式的实现原理
在说实现原理之前,先看看在tornado中是如何用的和原理
Tornado中的异步非阻塞
以超时型为例做个简单例子,详见注释
首先需要导入Future和gen
from tornado.concurrent import Future
from tornado import gen
class MainHandler(tornado.web.RequestHandler):
'第一次请求'
@gen.coroutine
def get(self):
future = Future()
tornado.ioloop.IOLoop.current().add_timeout(time.time() + 10, self.doing) #模拟等待10秒,生产环境不会用
yield future
def doing(self, *args, **kwargs):
self.write('Hello, world')
self.finish()
class IndexHandler(tornado.web.RequestHandler):
'第二次请求,紧接着第一次请求,不需要等待10s'
def get(self):
self.write("Index")
一共有三种应用方法:
- 超时型,请求来了超过时间,自动处理返回
- 再请求型:请求来了之后,服务器有向别处发送请求,请求回来之后再处理返回
- 手动返回型:请求来了之后,一直阻塞,知道手动填充内容,再返回处理
在异步非阻塞模式中最重要的就是Future对象(),httpclient实现异步也是因为里面封装了Future
Future实现异步的原理:
‘’'请求到达,创建一个future对象,当futrue有内容时,则执行回调函数,如设置超时,时间过后会自动
在future中添加一个值,返回执行回调函数,或者是发送http请求,请求回来结果放入feture,返回执行,再或者是手动向future中添加内容
future对象的作用:
1.挂起当前请求,线程可以处理其他请求
2.future设置了值,当前挂起的请求返回,执行回调函数
标识作用,如果没有值,连接不断开不返回,有值代表请求完成,返回,断开请求
总结就是,torado会通过feture有无内容,判断是否执行回调函数
‘’’
在Tornado中是通过非阻塞socket和IO多路复用epoll和Future对象实现的异步非阻塞
知道了以上,可自己实现一个异步非阻塞框架(返回信息没有封装处理):
import socket
import select
import re
import time
class Future():
def __init__(self, timeout):
self.result = None # future对象内容,有无决定是否返回
self.timeout = timeout # 超时终止挂起时间
self.start = time.time() # 创建future时间
def index(request): # 同步请求
return "index"
F = None
def main(request): # 异步请求
global F
F = Future(5) # 设置超时挂起时间
return F
def stop(request): # 终止异步请求挂起
global F
F.result = b'xxxxx'
return "stop"
routers = [
('/index/', index),
('/main/', main),
('/stop/', stop),
]
class HttpRequest(object):
"""
封装处理用户请求信息
"""
def __init__(self, content):
self.content = content # content就是客户端发来的数据
self.header_bytes = bytes() # 请求头信息
self.header_dict = {}
self.body_bytes = bytes() # 请求体信息
self.method = ""
self.url = ""
self.protocol = ""
self.initialize()
self.initialize_headers()
def initialize(self):
temp = self.content.split(b'\r\n\r\n', 1)
if len(temp) == 1:
self.header_bytes += temp
else:
h, b = temp
self.header_bytes += h
self.body_bytes += b
@property
def header_str(self):
return str(self.header_bytes, encoding='utf-8')
def initialize_headers(self):
headers = self.header_str.split('\r\n')
first_line = headers[0].split(' ')
if len(first_line) == 3:
self.method, self.url, self.protocol = headers[0].split(' ')
for line in headers:
kv = line.split(':')
if len(kv) == 2:
k, v = kv
self.header_dict[k] = v
def run():
"""
事件循环
:param host:
:param port:
:return:
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8080,))
sock.setblocking(False)
sock.listen(128)
inputs = []
inputs.append(sock)
async_request_dicy = {}
while True:
readable_list, writeable_list, error_list = select.select(inputs, [], [], 0.05)
for conn in readable_list:
if sock == conn:
'''新请求'''
print("xxxxxx")
client, address = sock.accept()
print("pk")
client.setblocking(False)
inputs.append(client)
else:
'''客户端发来数据'''
recv_data = b"" # 空字节变量
while True:
try:
chunk = conn.recv(8096)
recv_data += chunk
except Exception as e:
break
request = HttpRequest(recv_data)
print(request.url)
print(request.method)
print(request.header_dict)
print(request.body_bytes)
# recv_data进行处理,请求头和请求体
# 1.请求头中获取url
# 2.取路由中匹配,获取指定函数
flag = False
func = None
for route in routers:
if re.match(route[0], request.url): # 匹配路由成功
flag = True
func = route[1]
break
if flag:
reslute = func(recv_data)
if isinstance(reslute, Future):
async_request_dicy[conn] = reslute
else:
conn.sendall(bytes(reslute, encoding='utf-8'))
inputs.remove(conn)
conn.close()
else:
print(404)
conn.send(b'404')
# 3.执行函数,获取返回值
# 4,send返回值 conn.send(b'fsdf')
inputs.remove(conn)
conn.close()
for ck in async_request_dicy:
future = async_request_dicy[ck]
start = future.start
timeout = future.timeout
ctime = time.time()
if start + timeout <= ctime: # 超时
future.result = b'timeout'
if future.result: # 有内容
ck.sendall(future.result)
ck.close()
del async_request_dicy[ck]
inputs.remove(ck)
else:
pass
if __name__ == '__main__':
run()