目录:Tornado其他篇

01: tornado基础篇

02: tornado进阶篇

03: 自定义异步非阻塞tornado框架

04: 打开tornado源码剖析处理过程

目录:

1.1 源码返回顶部

  1. Python的Web框架中Tornado以异步非阻塞而闻名。本篇将使用200行代码完成一个微型异步非阻塞Web框架:Snow。

  2. 本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞的Web框架,其中便是众多异步非阻塞Web框架内部原理。

  3. 参考博客: http://www.cnblogs.com/wupeiqi/p/6536518.html

import re
import socket
import select
import time class HttpResponse(object):
"""
封装响应信息
"""
def __init__(self, content=''):
self.content = content self.headers = {}
self.cookies = {} def response(self):
return bytes(self.content, encoding='utf-8') class HttpNotFound(HttpResponse):
"""
404时的错误提示
"""
def __init__(self):
super(HttpNotFound, self).__init__('404 Not Found') class HttpRequest(object):
"""
用户封装用户请求信息
"""
def __init__(self, conn):
self.conn = conn 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): header_flag = False
while True:
try:
received = self.conn.recv(8096)
except Exception as e:
received = None
if not received:
break
if header_flag:
self.body_bytes += received
continue
temp = received.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, callback):
self.callback = callback
self._ready = False
self.value = None def set_result(self, value=None):
self.value = value
self._ready = True @property
def ready(self):
return self._ready class TimeoutFuture(Future):
"""
异步非阻塞超时
"""
def __init__(self, timeout):
super(TimeoutFuture, self).__init__(callback=None)
self.timeout = timeout
self.start_time = time.time() @property
def ready(self):
current_time = time.time()
if current_time > self.start_time + self.timeout:
self._ready = True
return self._ready class Snow(object):
"""
微型Web框架类
"""
def __init__(self, routes):
self.routes = routes
self.inputs = set()
self.request = None
self.async_request_handler = {} def run(self, host='localhost', port=9999):
"""
事件循环
: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((host, port,))
sock.setblocking(False)
sock.listen(128)
sock.setblocking(0)
self.inputs.add(sock)
try:
while True:
readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs,0.005)
for conn in readable_list:
if sock == conn:
client, address = conn.accept()
client.setblocking(False)
self.inputs.add(client)
else:
gen = self.process(conn)
if isinstance(gen, HttpResponse):
conn.sendall(gen.response())
self.inputs.remove(conn)
conn.close()
else:
yielded = next(gen)
self.async_request_handler[conn] = yielded
self.polling_callback() except Exception as e:
pass
finally:
sock.close() def polling_callback(self):
"""
遍历触发异步非阻塞的回调函数
:return:
"""
for conn in list(self.async_request_handler.keys()):
yielded = self.async_request_handler[conn]
if not yielded.ready:
continue
if yielded.callback:
ret = yielded.callback(self.request, yielded)
conn.sendall(ret.response())
self.inputs.remove(conn)
del self.async_request_handler[conn]
conn.close() def process(self, conn):
"""
处理路由系统以及执行函数
:param conn:
:return:
"""
self.request = HttpRequest(conn)
func = None
for route in self.routes:
if re.match(route[0], self.request.url):
func = route[1]
break
if not func:
return HttpNotFound()
else:
return func(self.request)

snow.py 源码

1.2 tornado框架核心代码分析(Snow类注释)返回顶部

    1.每个请求过来就会创建一个socket对象,并调用select去监听连接,select会将所有请求放到readable_list列表中

    2.使用while不断执行for循环遍历readable_list,如果是新连接请求过来就加入inputs列表中

    3.如果已经连接就调用self.process来获取请求头和请求体,如果已经获取到了正常的返回内容,就会返回一个

        HttpResponse类型,直接返回response返回值即可

    4.如果没有处理完成就会返回一个future对象,将这个future对象加入async_request_handler字典中,程序会继续

        向下走,不会阻塞

    5.每次执行完for循环后就会调用self.polling_callback()方法,在这个方法中再使用for循环遍历async_request_handler

        字典中的future对象,只要future对象有response,就将response返回,并将这个future对象从字典中删除

class Snow(object):
def __init__(self, routes):
self.routes = routes
self.inputs = set()
self.request = None
self.async_request_handler = {} def run(self, host='localhost', port=9999):
"""
事件循环
:param host:
:param port:
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port,))
sock.setblocking(False)
sock.listen(128)
sock.setblocking(0)
self.inputs.add(sock)
try:
while True:
readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs,0.005)
for conn in readable_list:
if sock == conn: #1.表示有新连接请求过来
client, address = conn.accept() #接收请求对象
client.setblocking(False)
self.inputs.add(client) #加入inputs中
else: #2. 连上后执行这里并判断连接类型
gen = self.process(conn) # 获取请求头请求体
if isinstance(gen, HttpResponse): #2.1如果返回的HttpResponse类型,就直接返回response
conn.sendall(gen.response())
self.inputs.remove(conn)
conn.close()
else: #2.2如果返回的是一个future类型,加入字典,并hold住
yielded = next(gen)
# 将future对象放到字典中,hold住这个请求,就继续向下执行
self.async_request_handler[conn] = yielded
self.polling_callback() #3. 每次for循环结束就会调用这个方法 except Exception as e:
pass
finally:
sock.close() def polling_callback(self):
"""
遍历触发异步非阻塞的回调函数
:return:
"""
for conn in list(self.async_request_handler.keys()):
# conn是: socket对象
# yield是: future对象
yielded = self.async_request_handler[conn]
# 若果future对象有返回值就会执行future.set_result()
# 如果有人执行future.set_result()就会自动将ready改成true,才会向下走
if not yielded.ready:
continue
if yielded.callback:
ret = yielded.callback(self.request, yielded)
conn.sendall(ret.response()) #返回数据
self.inputs.remove(conn) #将inputs中删除这个链接
del self.async_request_handler[conn] #在字典中删除这个future对象
conn.close() def polling_callback(self):
"""
4. 遍历触发异步非阻塞的回调函数,当future对象有返回就结束,并从字典中删除
:return:
"""
for conn in list(self.async_request_handler.keys()):
# conn是: socket对象
# yield是: future对象
yielded = self.async_request_handler[conn]
# 若果future对象有返回值就会执行future.set_result()
# 如果有人执行future.set_result()就会自动将ready改成true,才会向下走
if not yielded.ready:
continue
if yielded.callback:
ret = yielded.callback(self.request, yielded)
conn.sendall(ret.response()) # 返回数据
self.inputs.remove(conn) # 将inputs中删除这个链接
del self.async_request_handler[conn] # 在字典中删除这个future对象
conn.close() def process(self, conn):
"""
处理路由系统以及执行函数
:param conn:
:return:
"""
self.request = HttpRequest(conn)
func = None
for route in self.routes:
if re.match(route[0], self.request.url):
func = route[1]
break
if not func:
return HttpNotFound()
else:
return func(self.request)

tornado核心处理类 Snow 代码注释

1.3 剖析Future()对象 实现异步非阻塞原理返回顶部

  1.原理说明
    1、当发送GET请求时,由于方法被@gen.coroutine装饰且yield 一个 Future对象,那么Tornado会等待
    2、等待用户向future对象中放置数据或者发送信号,如果获取到数据或信号之后,就开始执行doing方法。
    3、等待用户向future对象中放置数据或者发送信号,如果获取到数据或信号之后,就开始执行doing方法。
    4、注意:在等待用户向future对象中放置数据或信号时,此连接是不断开的。
  2.验证方法
    1、首先访问:http://127.0.0.1:8888/async 会阻塞,并且不断开,页面一直在转,说明非阻塞
    2、然后访问:http://127.0.0.1:8888/login 可以直接访问,证明可以实现异步
    3、最后访问:http://127.0.0.1:8888/stop /index页面立刻就会返回了

import tornado.ioloop
import tornado.web
from tornado import gen
from tornado.concurrent import Future '''1. 访问:http://127.0.0.1:8888/async 会阻塞'''
future = None class AsyncHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
global future
future = Future()
future.add_done_callback(self.doing)
yield future # from tornado import httpclient
# http = httpclient.AsyncHTTPClient()
# # 下载完成后,自动执行 future.set_result()
# yield http.fetch("http://www.google.com", self.doing)
def doing(self, *args, **kwargs):
self.write('async')
self.finish() '''2. 但是访问http://127.0.0.1:8888/login可以访问'''
class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.write('login') '''3. 当我们去访问http://127.0.0.1:8888/stop时就可以结束/async的阻塞'''
class StopHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
future.set_result('...')
self.write('结束阻塞') settings = {}
application = tornado.web.Application([
(r"/login", LoginHandler),
(r"/async", AsyncHandler),
(r"/stop", StopHandler),
],**settings)
if __name__ == "__main__":
application.listen(8888)
print('直接访问会阻塞:http://127.0.0.1:8888/async')
print('阻塞时还能访问login证明实现异步:http://127.0.0.1:8888/login')
print('访问sotp会结束async的阻塞:http://127.0.0.1:8888/stop')
tornado.ioloop.IOLoop.instance().start()

future对象实现阻塞 与 结束阻塞

1.4 自定义框架使用返回顶部

  1、基本使用

      1. 在Linux下执行: python3 app.py

      2. 使用Linux中的Firefox访问:http://1.1.1.3:8888/index/

from snow import Snow
from snow import HttpResponse def index(request):
print('aafdsfsfdf')
return HttpResponse('OK') routes = [
(r'/index/', index),
] app = Snow(routes)
app.run(host='1.1.1.3',port=8888)

app.py

  2、异步非阻塞:超时

      1. 访问: http://127.0.0.1:8888/home/ 可以直接返回

      2. 访问:http://127.0.0.1:8888/async/  页面会转5s后超时

from snow import Snow
from snow import HttpResponse
from snow import TimeoutFuture request_list = [] def async(request):
obj = TimeoutFuture(5)
yield obj def home(request):
return HttpResponse('home') routes = [
(r'/home/', home),
(r'/async/', async),
] app = Snow(routes)
app.run(port=8888)

app.py

  3、异步非阻塞:等待

      1. 访问: http://127.0.0.1:8888/req/ 会一直处于长连接的阻塞状态,由于异步所以可以阻塞也可以处理其他请求

      2. 访问:http://127.0.0.1:8888/stop/ 会执行 obj.set_result('done') 结束阻塞

from snow import Snow
from snow import HttpResponse
from snow import Future request_list = [] def callback(request, future):
return HttpResponse(future.value) def req(request):
obj = Future(callback=callback)
request_list.append(obj)
yield obj def stop(request):
obj = request_list[0]
del request_list[0]
obj.set_result('done')
return HttpResponse('stop') routes = [
(r'/req/', req),
(r'/stop/', stop),
] app = Snow(routes)
app.run(port=8888)

app.py

03: 自定义异步非阻塞tornado框架的更多相关文章

  1. 200行自定义异步非阻塞Web框架

    Python的Web框架中Tornado以异步非阻塞而闻名.本篇将使用200行代码完成一个微型异步非阻塞Web框架:Snow. 一.源码 本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞 ...

  2. Tornado----自定义异步非阻塞Web框架:Snow

    Python的Web框架中Tornado以异步非阻塞而闻名.本篇将使用200行代码完成一个微型异步非阻塞Web框架:Snow. 一.源码 本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞 ...

  3. 150行代码搭建异步非阻塞Web框架

    最近看Tornado源码给了我不少启发,心血来潮决定自己试着只用python标准库来实现一个异步非阻塞web框架.花了点时间感觉还可以,一百多行的代码已经可以撑起一个极简框架了. 一.准备工作 需要的 ...

  4. Tornado之自定义异步非阻塞的服务器和客户端

    一.自定义的异步非阻塞的客户端 #!/usr/bin/env python # -*- coding: utf8 -*- # __Author: "Skiler Hao" # da ...

  5. python-自定义异步非阻塞爬虫框架

    api import socket import select class MySock: def __init__(self, sock, data): self.sock = sock self. ...

  6. Tornado异步非阻塞的使用以及原理

    Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快.得利于其 非阻塞的方式和对 epoll 的运用,Tornado ...

  7. 异步非阻塞IO的Python Web框架--Tornado

    Tornado的全称是Torado Web Server,从名字上就可知它可用作Web服务器,但同时它也是一个Python Web的开发框架.最初是在FriendFeed公司的网站上使用,FaceBo ...

  8. Python web框架 Tornado(二)异步非阻塞

    异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案:多线程,多进程 异步非阻塞(存在IO请求): Torna ...

  9. Python web框架 Tornado异步非阻塞

    Python web框架 Tornado异步非阻塞   异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案: ...

随机推荐

  1. URAL 1277 - Cops and Thieves - [无向图点带权的最小点割]

    题目链接:https://cn.vjudge.net/problem/URAL-1277 The Galaxy Police (Galaxpol) found out that a notorious ...

  2. Hive之import和export使用详解

    在hive-0.8.0后引入了import/export命令. Export命令可以导出一张表或分区的数据和元数据信息到一个输出位置,并且导出数据可以被移动到另一个hadoop集群或hive实例,并且 ...

  3. This version of the rendering library is more recent than your version of ADT plug-in. Please update ADT plug-in

    地址:http://stackoverflow.com/questions/18852983/eclipse-reports-rendering-library-more-recent-than-ad ...

  4. 代码参数里的 payload 是什么意思???

    代码参数里的 payload 是什么意思???

  5. deque Comparison of Queue and Deque methods Comparison of Stack and Deque methods

    1. 队列queue和双端队列deque的转换 Queue Method Equivalent Deque Methodadd(e) addLast(e)offer(e) offerLast(e)re ...

  6. php Only variables can be passed by reference

    最近做项目,发现了一个报错  Only variables can be passed by reference,  意思是"只有变量能通过'引用'" 就是在代码中 使用了一个方法 ...

  7. In MySQL, a zero number equals any string

    最近在做项目的过程中发现了一个问题 数据库表 test  有个字段是 target_id  int(11),这个字段可能为零 使用如下查询 select * from test where targe ...

  8. 新入职如何快速去了解一个Javaweb项目!

    1.从代码结构入手,搞清楚Java源码所在目录以及jsp代码所在目录. 2.从Web.xml入手,搞清楚首页,上下文及配置情况. 3.从lib目录里初步查看系统使用的框架和工具包. 4.从页面大体浏览 ...

  9. MySQL 同一实例不同库之间表同步(Otter 应用)

    1 需求 在同一台服务器同一MySQL实例中的source库和target库都存在student表.如果source库中该表发生增删改操作时,也需要体现到target库的student表中: 2 解决 ...

  10. jquery ajax contentType设置

    默认get方法没有contentType,post方法的contentType为:application/x-www-form-urlencoded; charset=UTF-8 (1) 设置成app ...