msdn windows server 按电源事件api_【tornado源码分析】I/O事件循环机制与多进程
tornado是一个异步非阻塞的web框架,由Facebook开源,源代码用的python语言。之前也用tornado做过几个项目,实习的时候公司也是用的tornado来做工业物联网的后端。空余时间研究一下tornado的源码,毕竟是Facebook的代码,还是有学习的价值。以下为学习的一点笔记。
一.socket编程
现代计算机网络中使用TCP/IP协议架构:物理链路层,ip层,TCP/UDP层,应用层。
![](/assets/blank.gif)
socket层则是对TCP/UDP层的一层简要封装,能够方便开发者定义自己的高层应用协议。在python中,使用内置的socket模块能够进行socket编程。该模块提供的api有:
![](/assets/blank.gif)
![](/assets/blank.gif)
利用提供的这些基础api,能够很方便的自行编写高层协议,比如,你可以自己实现一个HTTP server。 HTTP协议基于TCP协议,是面向连接的,构造一条虚电路,保证数据包的顺序,不丢包,可靠的发送。基于TCP的socket逻辑架构如下:
![](/assets/blank.gif)
socket在类unix系统中均可以看成是一个特殊文件,
sock=socket.socket()
返回一个句柄sock,sock.fileno()即为该句柄的编号,为int类型。考虑一下在tornado中tcpserver如何在ioloop注册事件。在ioloop中提供了一个方法:
def add_handler( # noqa: F811self, fd: Union[int, _Selectable], handler: Callable[..., None], events: int) -> None
add_handler函数用来绑定socket对应的回调函数,具体来说:当服务器端socket对应的’读事件‘(IOOP.READ,即为参数event)被触发时,将调用handler函数进行处理。以上理解比较抽象,下面写个例子,理解下:
首先写个server:
from tornado import httpserver, ioloop, tcpserver
import socketdef a(A, B):print("====开始测试=====")print(A)print(B)print("我是服务器socket的回调函数")if __name__ == '__main__':loop = ioloop.IOLoop.current()server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server.bind(('127.0.0.1', 8881))loop.add_handler(server.fileno(), a, ioloop.IOLoop.READ)server.listen()#conn, addr = server.accept()#a = conn.recv(1024)#print("服务器收到的数据", repr(a))#loop.spawn_callback(c, conn)loop.start()
以上server用socket编程来实现,功能为:首先定义一个socket,变量名为server,该socket绑定在8881端口上。然后我们利用add_handler函数将该socket注册到ioloop上,注册的这一步在平时的开发中的写法为:
server = TCPServer()server.bind(8881)server.start(0) # Forks multiple sub-processesIOLoop.current().start()
在注册该socket之后,ioloop.start()开始事件循环。
再写一个client:
import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', 8881))
sock.sendall(b"hello,worldfwefewfwefwefwefwefwefwefw")
print(sock.fileno())
# a = sock.recv(1024)
# print(repr(a))
sock.close()
该client,依然是创建一个socket,然后请求连接8881端口的服务(模拟发送HTTP请求)。当该数据被发送之后,可以看见server端的输出为:
![](/assets/blank.gif)
可以看见ioloop监听到了client对server发送的请求,进行了回调。具体来说:client向server发送了一个请求,server端的socket在收到该请求后,会触发READ事件,此时ioloop会触发该socket对应的回调函数。
二.tornado中的多进程
对于tornado这种单线程的web框架,如果遇到cpu bound类型的任务,虽然及时能够通过coroutine模式保证不会丢失服务,但是由于CPU被占用,所以响应时间一点也不会减少。此时可以考虑使用多进程tornado,或者多tornado实例,然后再用nginx做负载均衡(这个还没试过)。
我们通过time.sleep(20)来模拟CPU的计算过程,原因是在这20s内,server不能响应其他用户的请求(cpu一直在计算)。
代码如下:
class MainHandler(tornado.web.RequestHandler):async def get(self):print("睡眠10s")time.sleep(20)print("pid", os.getpid())print("睡眠结束")self.write(str(os.getpid()))class WebHandler(tornado.web.RequestHandler):async def get(self):print("hello,world123")def make_app():return tornado.web.Application([tornado.web.url(r"/123", MainHandler, name="hello"),tornado.web.url(r"/456", WebHandler, name="web"),])
if __name__ == "__main__":app = make_app()# server = tornado.httpserver.HTTPServer(app)# server.bind(8888, '127.0.0.1')# server.start(0)sockets = tornado.netutil.bind_sockets(8888)tornado.process.fork_processes(2)server = tornado.httpserver.HTTPServer(app)server.add_sockets(sockets)loop1 = tornado.ioloop.IOLoop()loop = loop1.current()# loop.add_handler(1, a, 0x001)# loop.add_handler(2, b, 0x004)# loop.add_callback(d)# print(loop1._ioloop_for_asyncio)loop.start()由于我的机器有四个核,所以输出如下:
![](/assets/blank.gif)
在tornado中,多进程是通过os.fork函数来实现(windows无法实现)。父进程在fork子进程之后,子进程会和父进程同时执行剩下的代码,需要注意os.fork有两个返回值,如果返回值为0,则表明当前这个进程是子进程,如果返回值为int,则int是子进程的pid号。那么通过这种方式在执行cpubound的任务时,可以充分利用多核。在tornado中的内部实现为tornado.netutil.add_accept_handler函数:
def add_accept_handler(sock: socket.socket, callback: Callable[[socket.socket, Any], None]
) -> Callable[[], None]:io_loop = IOLoop.current()removed = [False]def accept_handler(fd: socket.socket, events: int) -> None:for i in range(_DEFAULT_BACKLOG):if removed[0]:# The socket was probably closedreturntry:connection, address = sock.accept()except socket.error as e:if errno_from_exception(e) in _ERRNO_WOULDBLOCK:returnif errno_from_exception(e) == errno.ECONNABORTED:continueraiseset_close_exec(connection.fileno())callback(connection, address)def remove_handler() -> None:io_loop.remove_handler(sock)removed[0] = Trueio_loop.add_handler(sock, accept_handler, IOLoop.READ)return remove_handler
关键在于内部函数accept_handler,当使用多进程时会产生惊群效应,那么tornado是如何防止这种情况的呢?关键在:
if errno_from_exception(e) in _ERRNO_WOULDBLOCK:
在异步函数中(errno.EWOULDBLOCK,errno.EAGAIN)可以不被当做错误,用来避免客户端的socket被重复处理。
欢迎关注微信公众号:生物信息与python,及时更新与分享~
msdn windows server 按电源事件api_【tornado源码分析】I/O事件循环机制与多进程相关推荐
- tornado源码分析
tornado源码分析 本源码为tornado1.0版本 源码附带例子helloworld import tornado.httpserver import tornado.ioloop import ...
- Tornado源码分析 --- 静态文件处理模块
每个web框架都会有对静态文件的处理支持,下面对于Tornado的静态文件的处理模块的源码进行分析,以加强自己对静态文件处理的理解. 先从Tornado的主要模块 web.py 入手,可以看到在App ...
- flink设置watermark以及事件时间字段源码分析
flink设置watermark以及事件时间字段源码分析 背景 1.1.提取时间戳字段,用于事件时间语义处理数据 1.2.设置水位线(水印)watermark TimestampAssigner 核心 ...
- View的Touch事件分发(二.源码分析)
Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...
- tornado源码分析(四)之future、gen.coroutine
future是什么 在事件驱动编程模型中,会有很多的事件循环,各事件循环在创建异步事件时可以同时创建一个future对象,并将创建的异步事件与该future对象存储在一起,并将所有传入的callbac ...
- tornado源码分析系列一
先来看一个简单的示例: #!/usr/bin/env python #coding:utf8import socketdef run():sock = socket.socket(socket.AF_ ...
- tornado源码分析-Application
tornado.web包含web框架的大部分主要功能,Application是其中一个重要的类 Application类的作用是实现 URI 转发,将 Application 的实例传递给 https ...
- [原]tornado源码分析系列(三)[网络层 IOLoop类]
引言:由于都是在工作当中抽出时间看源代码,所以更新速度比较慢,但是还是希望通过对好的源码的分析和探讨,大家相互学习,发现不好的地方共同讨论. 上次讲了IOLoop中的几个重要的方法,inistance ...
- jQuery源码分析系列:事件模块概述
jQuery的事件模块是较复杂的,前面仅仅提到了对事件对象的包装.即统一了一些兼容性的问题.这篇会综述下jQuery的整个事件模块.后面会详细分析jQuery.event.add/jQuery.eve ...
最新文章
- 从零开始学ASP.NET
- Atitit.atiJsBridge 新特性v7q329
- 彻底的卸载SQL Server2005
- android手机定位p适配,Android 9(P)版本适配指南
- mysql optimizer mrr_[转] MySQL 的 MRR 到底是什么?
- linux 版本信息 64位,Centos查看版本信息
- 双线性插值函数的形状
- 血压预测常用数据集整理
- TCC解决分布式事务问题
- cmd运行javac解析中文乱码
- 对称网络的电路分析方法
- windows的hosts文件在哪?
- POI使用详解 java 复杂excel导出(笔记)
- 【CLAA系列】CLAA 通讯过程
- 定时器中断实验和PWM输出实验(寄存器)
- Image Pyramid
- PMP考试六大管理学定律
- Itester系列之 HGU_ONU PON性能测试指导
- 提醒。选择变色镜片的几大理由
- 云进销存源码|云ERP进销存源码开源php支持手机版