python学习笔记-异步非阻塞web框架
一、异步非阻塞框架介绍
1、介绍
支持异步非阻塞web框架:tornado , node js
2、定义对比
异步IO模块:我们作为客户端向服务端“并发”请求
异步非阻塞web框架:针对服务端,希望一个线程处理更多的请求
二、tornado异步非阻塞
【要点提炼】
使用装饰器@gen.coroutine
模拟等待,使用特殊的形式等待5秒,写法
future=Future() tornado.ioloop.IOLoop.current().add_timeout(time.time()+5,self.done) yield future
1、场景实例1
程序1,之前写法,同步阻塞(先访问main会进入等待,此时访问index也到等main处理完了才有返回)
import tornado.ioloop
import tornado.web
import time
class MainHandler(tornado.web.RequestHandler):
def get(self):
time.sleep(10)
self.write("main")
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("index")
application=tornado.web.Application([
(r"/main",MainHandler),
(r"/index", IndexHandler),])
if __name__=="__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
程序2,此时要用到future,实现异步不阻塞
from tornado import gen
from tornado.concurrent import Future
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
future = Future()
tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5, self.done)
yield future
def done(self):
self.write("Main")
self.finish()
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("index")
application=tornado.web.Application([
(r"/main",MainHandler),
(r"/index", IndexHandler),])
if __name__=="__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
2、场景实例2 (请求外部url时引起的阻塞)
【要点提炼】
通过tornado发请求,实现异步非阻塞
from tornado import httpclient http=httpclient.AsyncHTTPClient() yield http.fetch("http://www.google.com",self.done)
future的作用
1、挂起当前请求,线程可以处理其他请求
2、future设置值,当前挂起的请求返回
future.set_result(None)
程序3,同步阻塞场景
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
import requests
ret=requests.get("http://www.google.com")
self.write("main")
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("index")
application=tornado.web.Application([
(r"/main",MainHandler),
(r"/index", IndexHandler),])
if __name__=="__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
程序4、异步非阻塞
import tornado.ioloop
import tornado.web
from tornado import gen
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
from tornado import httpclient
http=httpclient.AsyncHTTPClient()
yield http.fetch("http://www.google.com",self.done)
def done(self,*args,**kwargs):
self.write("Main")
self.finish()
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("index")
application=tornado.web.Application([
(r"/main",MainHandler),
(r"/index", IndexHandler),])
if __name__=="__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
3、实例3,(自己设置future的值,将挂起的请求释放)
future=None
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):
self.write("Main")
self.finish()
class IndexHandler(tornado.web.RequestHandler):
def get(self):
global future
future.set_result(None) #设置值,将挂起的请求释放
self.write("index")
application=tornado.web.Application([
(r"/main",MainHandler),
(r"/index", IndexHandler),])
if __name__=="__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
三、自定义web框架
1、使用socket实现同步的web框架
示例:
import socket
import select
class HttpRequest(object):
"""
用来封装用户请求信息
"""
def __init__(self, content):
self.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
header_flag = True
@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 index(requet):
return "index"
def main(request):
return 'main'
routers=[('/index',index),
('/main',main),
]
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',8002,))
sock.listen(128)
inputs=[]
inputs.append(sock)
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
#dada进行处理:请求头和请求体
#1、请求头中获取url
#2、去路由中匹配,获取指定的函数
#3、执行函数,获取返回值
#4、发送给客户端
request=HttpRequest(data)
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)
r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
r.sendall(bytes(result, 'utf8'))
else:
r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
r.sendall(b'404')
inputs.remove(r)
r.close()
if __name__=="__main__":
run()
2、自定义web框架支持异步
【要点提炼】
编写实现future功能,如访问的main,不执行操作
》示例2
import socket
import select
class HttpRequest(object):
"""
用来封装用户请求信息
"""
def __init__(self, content):
self.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
header_flag = True
@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):
self.result=None
F=None
def stop(request):
global F
F.result=b'future-xxxxx'
return 'stop'
def index(requet):
return "index"
def main(request):
global F
F=Future()
return F
routers=[('/index',index),
('/main',main),
('/stop',stop),
]
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',8002,))
sock.listen(128)
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
request=HttpRequest(data)
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("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
r.sendall(bytes(result,encoding='utf8'))
inputs.remove(r)
r.close()
else:
r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
r.sendall(b'404')
inputs.remove(r)
r.close()
for conn in list(async_request_dict.keys()):
future=async_request_dict[conn]
if future.result:
conn.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
conn.sendall(future.result)
conn.close()
del async_request_dict[conn]
inputs.remove(conn)
if __name__=="__main__":
run()
示例2是手动写的方法停止阻塞,可以实现利用超时时间自动停止
》示例3
import socket
import select
import time
class HttpRequest(object):
"""
用来封装用户请求信息
"""
def __init__(self, content):
self.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
header_flag = True
@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()
F=None
def index(requet):
return "index"
def main(request):
global F
F=Future(5)
return F
routers=[('/index',index),
('/main',main),
]
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',8002,))
sock.listen(128)
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
request=HttpRequest(data)
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("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
r.sendall(bytes(result,encoding='utf8'))
inputs.remove(r)
r.close()
else:
r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
r.sendall(b'404')
inputs.remove(r)
r.close()
for conn in list(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(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
conn.sendall(future.result)
conn.close()
del async_request_dict[conn]
inputs.remove(conn)
if __name__=="__main__":
run()
浙公网安备 33010602011771号