tornado是一个异步非阻塞的web框架,由Facebook开源,源代码用的python语言。之前也用tornado做过几个项目,实习的时候公司也是用的tornado来做工业物联网的后端。空余时间研究一下tornado的源码,毕竟是Facebook的代码,还是有学习的价值。以下为学习的一点笔记。


一.socket编程

现代计算机网络中使用TCP/IP协议架构:物理链路层,ip层,TCP/UDP层,应用层。

socket层则是对TCP/UDP层的一层简要封装,能够方便开发者定义自己的高层应用协议。在python中,使用内置的socket模块能够进行socket编程。该模块提供的api有:

利用提供的这些基础api,能够很方便的自行编写高层协议,比如,你可以自己实现一个HTTP server。 HTTP协议基于TCP协议,是面向连接的,构造一条虚电路,保证数据包的顺序,不丢包,可靠的发送。基于TCP的socket逻辑架构如下:

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端的输出为:

可以看见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()由于我的机器有四个核,所以输出如下:

在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事件循环机制与多进程相关推荐

  1. tornado源码分析

    tornado源码分析 本源码为tornado1.0版本 源码附带例子helloworld import tornado.httpserver import tornado.ioloop import ...

  2. Tornado源码分析 --- 静态文件处理模块

    每个web框架都会有对静态文件的处理支持,下面对于Tornado的静态文件的处理模块的源码进行分析,以加强自己对静态文件处理的理解. 先从Tornado的主要模块 web.py 入手,可以看到在App ...

  3. flink设置watermark以及事件时间字段源码分析

    flink设置watermark以及事件时间字段源码分析 背景 1.1.提取时间戳字段,用于事件时间语义处理数据 1.2.设置水位线(水印)watermark TimestampAssigner 核心 ...

  4. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  5. tornado源码分析(四)之future、gen.coroutine

    future是什么 在事件驱动编程模型中,会有很多的事件循环,各事件循环在创建异步事件时可以同时创建一个future对象,并将创建的异步事件与该future对象存储在一起,并将所有传入的callbac ...

  6. tornado源码分析系列一

    先来看一个简单的示例: #!/usr/bin/env python #coding:utf8import socketdef run():sock = socket.socket(socket.AF_ ...

  7. tornado源码分析-Application

    tornado.web包含web框架的大部分主要功能,Application是其中一个重要的类 Application类的作用是实现 URI 转发,将 Application 的实例传递给 https ...

  8. [原]tornado源码分析系列(三)[网络层 IOLoop类]

    引言:由于都是在工作当中抽出时间看源代码,所以更新速度比较慢,但是还是希望通过对好的源码的分析和探讨,大家相互学习,发现不好的地方共同讨论. 上次讲了IOLoop中的几个重要的方法,inistance ...

  9. jQuery源码分析系列:事件模块概述

    jQuery的事件模块是较复杂的,前面仅仅提到了对事件对象的包装.即统一了一些兼容性的问题.这篇会综述下jQuery的整个事件模块.后面会详细分析jQuery.event.add/jQuery.eve ...

最新文章

  1. 从零开始学ASP.NET
  2. Atitit.atiJsBridge 新特性v7q329
  3. 彻底的卸载SQL Server2005
  4. android手机定位p适配,Android 9(P)版本适配指南
  5. mysql optimizer mrr_[转] MySQL 的 MRR 到底是什么?
  6. linux 版本信息 64位,Centos查看版本信息
  7. 双线性插值函数的形状
  8. 血压预测常用数据集整理
  9. TCC解决分布式事务问题
  10. cmd运行javac解析中文乱码
  11. 对称网络的电路分析方法
  12. windows的hosts文件在哪?
  13. POI使用详解 java 复杂excel导出(笔记)
  14. 【CLAA系列】CLAA 通讯过程
  15. 定时器中断实验和PWM输出实验(寄存器)
  16. Image Pyramid
  17. PMP考试六大管理学定律
  18. Itester系列之 HGU_ONU PON性能测试指导
  19. 提醒。选择变色镜片的几大理由
  20. 云进销存源码|云ERP进销存源码开源php支持手机版

热门文章

  1. Xilinx发布实时视频编码服务器
  2. 数据结构与算法之反转单向链表和双向链表
  3. 如何设计一个 A/B test?
  4. 数据存储四种常见方式
  5. Kubernetes 无法删除pod实例的排查过程
  6. Flink 流式计算在节省资源方面的简单分析
  7. leetcode 79. Word Search | 79. 单词搜索(回溯+DFS)
  8. 【MySQL查询】复杂查询:别名、外键join
  9. 架构之:REST和RESTful
  10. Lombok常用注解和功能