自定义异步非阻塞框架
tornado的异步非阻塞
tornado支持同步和异步两种模式,一般的写法是同步方式,想要支持异步就需要一点特殊的写法。
web框架的异步模式是为了让程序的效率更高,如果http请求让线程干的事情都是计算密集型任务,那么单个线程无论怎么做都不可能提高效率,但是如果任务是不消耗CPU但是需要耗时的任务,那么一个非阻塞+IO多路复用+一个线程能帮我们提升效率。
在tornado中的的编程中,非计算型任务一般可以分为三种:1. 在视图里主动 sleep(当然一般不会这么傻,无缘无故就sleep,测试环境可能需要用到) 2. 在这个视图中又往别的地方发http请求,这时可以利用等待的这段时间去做点别的事 3. 返回一个future对象,暂停住请求
等future对象的_result属性被赋值再返回请求所需要的响应
主动sleep
import tornado.web
import tornado.ioloop
from tornado import gen
import time
from tornado.concurrent import Future
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self, *args, **kwargs):
future = Future()
tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5, self.done)
yield future
def done(self):
self.write('hahah')
self.finish()
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('index')
settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
}
application = tornado.web.Application([
(r"/main", MainHandler),
(r"/index", IndexHandler),
], **settings)
if __name__ == "__main__":
application.listen(8009)
tornado.ioloop.IOLoop.instance().start()
发http请求
import tornado.web
import tornado.ioloop
from tornado import gen
from tornado import httpclient
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
http = httpclient.AsyncHTTPClient()
yield http.fetch("http://www.google.com", self.done)
def done(self, response):
self.finish('main')
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('index')
settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
}
application = tornado.web.Application([
(r"/main", MainHandler),
(r"/index", IndexHandler),
], **settings)
if __name__ == "__main__":
application.listen(8009)
tornado.ioloop.IOLoop.instance().start()
返回单纯的future等待被_result赋值
import tornado.web
import tornado.ioloop
from tornado import gen
from tornado.concurrent import Future
future = ''
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
global future
future = Future()
future.add_done_callback(self.done)
yield future
def done(self,*args, **kwargs):
print(args, kwargs)
self.write('Main')
self.finish()
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
global future
future.set_result('xx')
self.write('index')
settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
}
application = tornado.web.Application([
(r"/main", MainHandler),
(r"/index", IndexHandler),
], **settings)
if __name__ == "__main__":
application.listen(8009)
tornado.ioloop.IOLoop.instance().start()
自定义web框架
import socket
import select
import time
class HttpRequest(object):
"""
用户封装用户请求信息
"""
def __init__(self, content):
"""
:param content:用户发送的请求数据:请求头和请求体
"""
self.content = content
self.header_bytes = bytes()
self.body_bytes = bytes()
self.header_dict = {}
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
class Future(object):
def __init__(self,timeout=0):
self.result = None
self.timeout = timeout
self.start = time.time()
def main(request):
f = Future(5)
return f
def index(request):
return "index"
routers = [
('/main',main),
('/index',index),
]
def run():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 9999,))
sock.setblocking(False)
sock.listen(5)
inputs = []
inputs.append(sock)
async_request_dict = {
}
while True:
rlist,wlist,elist = select.select(inputs,[],[],0.05)
for r in rlist:
if r == sock:
"""新请求到来"""
conn,addr = sock.accept()
conn.setblocking(False)
inputs.append(conn)
else:
"""客户端发来数据"""
data = b""
while True:
try:
chunk = r.recv(1024)
data = data + chunk
except Exception as e:
chunk = None
if not chunk:
break
# data进行处理:请求头和请求体
request = HttpRequest(data)
# 1. 请求头中获取url
# 2. 去路由中匹配,获取指定的函数
# 3. 执行函数,获取返回值
# 4. 将返回值 r.sendall(b'xx')
import re
flag = False
func = None
for route in routers:
if re.match(route[0],request.url):
flag = True
func = route[1]
break
if flag:
result = func(request)
if isinstance(result,Future):
async_request_dict[r] = result
else:
r.sendall(bytes(result,encoding='utf-8'))
inputs.remove(r)
r.close()
else:
r.sendall(b"404")
inputs.remove(r)
r.close()
del_con_list = []
for conn in async_request_dict.keys():
future = async_request_dict[conn]
start = future.start
timeout = future.timeout
ctime = time.time()
if (start + timeout) <= ctime :
future.result = b"timeout"
if future.result:
conn.sendall(future.result)
conn.close()
del_con_list.append(conn)
inputs.remove(conn)
for conn in del_con_list:
async_request_dict.pop(conn)
if __name__ == '__main__':
run()