Tornado实现多进程/多线程的HTTP服务详解
作者:ZhiChao& 发布时间:2023-11-19 23:40:20
用tornado web服务的基本流程
1.实现处理请求的Handler,该类继承自tornado.web.RequestHandler,实现用于处理请求的对应方法如:get、post等。返回内容用self.write方法输出。
2.实例化一个Application。构造函数的参数是一个Handlers列表,通过正则表达式,将请求与Handler对应起来。通过dict将Handler需要的其他对象以参数的方式传递给Handler的initialize方法。
3.初始化一个tornado.httpserver.HTTPServer对象,构造函数的参数是上一步的Application对象。
4.为HTTPServer对象绑定一个端口。
5.开始IOLoop。
需要用到的特性
由于tornado的亮点是异步请求,所以这里首先想到的是将所有请求都改造为异步的。但是这里遇到一个问题,就是异步函数内一定不能有阻塞调用出现,否则整个IOLoop都会被卡住。这就要求彻底地去改造服务,将所有IO或是用时较长的请求都改造为异步函数。这个工程量是非常大的,需要去修改已有的代码。因此,我们考虑用线程池的方式去实现。当一个线程阻塞在某个请求或IO时,其他线程或IOLoop会继续执行。
另外一个瓶颈就是GIL限制了CPU的并发数量,因此考虑用子进程的方式增加进程数,提高服务能力上限。
综合上面的分析,大致用以下方案:
通过子进程的方式复制多个进程,使子进程中的只读页指向同一个物理页。
线程池。回避异步改造的工作量,增加IO的并发量。
测试代码
首先测试线程池,测试用例为:
对sleep页面同时发出两个请求:
在线程池中运行的函数(这里是self.block_task)能够同时执行。表现为在控制台交替打印出数字。
两个get请求几乎同时返回,在浏览器上显示返回的内容。
线程池的测试代码如下:
import os
import sys
import time
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.gen
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
from tornado.options import define, options
class HasBlockTaskHandler(tornado.web.RequestHandler):
executor = ThreadPoolExecutor(20) #起线程池,由当前RequestHandler持有
@tornado.gen.coroutine
def get(self):
strTime = time.strftime("%Y-%m-%d %H:%M:%S")
print "in get before block_task %s" % strTime
result = yield self.block_task(strTime)
print "in get after block_task"
self.write("%s" % (result))
@run_on_executor
def block_task(self, strTime):
print "in block_task %s" % strTime
for i in range(1, 16):
time.sleep(1)
print "step %d : %s" % (i, strTime)
return "Finish %s" % strTime
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/sleep", HasBlockTaskHandler)], autoreload=False, debug=False)
http_server = tornado.httpserver.HTTPServer(app)
http_server.bind(8888)
tornado.ioloop.IOLoop.instance().start()
整个代码里有几个位置值得关注:
1.executor = ThreadPoolExecutor(20)。这是给Handler类初始化了一个线程池。其中concurrent.futures不属于tornado,是python的一个独立模块,在python3中是内置模块,python2.7需要自己安装。
2.修饰符@run_on_executor。这个修饰符将同步函数改造为在executor(这里是线程池)上运行的异步函数,内部实现是将被修饰的函数submit到executor,返回一个Future对象。
3.修饰符@tornado.gen.coroutine。被这个修饰符修饰的函数,是一个以同步函数方式编写的异步函数。原本通过callback方式编写的异步代码,有了这个修饰符,可以通过yield一个Future的方式来写。被修饰的函数在yield了一个Future对象后将会被挂起,Future对象的结果返回后继续执行。
运行代码后,在两个不同浏览器上访问sleep页面,得到了想要的效果。这里有一个小插曲,就是如果在同一浏览器的两个tab上进行测试,是无法看到想要的效果。第二个get请求会被block,直到第一个get请求返回,服务端才开始处理第二个get请求。这让我一度觉得多线程没有生效,用了半天时间查了很多资料,才看到是浏览器把相同的第二个请求block了,具体链接参考这里。
由于tornado很方便地支持多进程模型,多进程的使用要简单很多,在以上例子中,只需要对启动部分稍作改动即可。具体代码如下所示:
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/sleep", HasBlockTaskHandler)], autoreload=False, debug=False)
http_server = tornado.httpserver.HTTPServer(app)
http_server.bind(8888)
print tornado.ioloop.IOLoop.initialized()
http_server.start(5)
tornado.ioloop.IOLoop.instance().start()
需要注意的地方有两点:
1.app = tornado.web.Application(handlers=[(r"/sleep", HasBlockTaskHandler)], autoreload=False, debug=False),在生成Application对象时,要将autoreload和debug两个参数至为False。也就是需要保证在fork子进程之前IOLoop是未被初始化的。这个可以通过tornado.ioloop.IOLoop.initialized()函数来跟。
2.http_server.start(5)在启动IOLoop之前通过start函数设置进程数量,如果设置为0表示每个CPU都启动一个进程。
最后的效果是可以看到n+1个进程在运行,且公用同一个端口。
来源:https://www.cnblogs.com/zhichaoma/p/9667738.html


猜你喜欢
- 本文实例讲述了wxpython中Textctrl回车事件无效的解决方法。分享给大家供大家参考,具体如下:今天使用wxptyhon的Textc
- 【摘 要】 我只是提供我几个我认为有助于提高写高性能的asp.net应用程序的技巧,本文提到的提高asp.net性能的技巧只是一个起步,更多
- 简介testing是 Go 语言标准库自带的测试库。在 Go 语言中编写测试很简单,只需要遵循 Go 测试的几个约定,与编写正常的 Go 代
- 一.图像加法运算1.Numpy库加法其运算方法是:目标图像 = 图像1 + 图像2,运算结果进行取模运算。当像素值<=255时,结果为
- 创建python文件fling.py,代码如下:#!/usr/bin/env monkeyrunnerimport timefrom com
- 本文实例为大家分享了js瀑布流加载效果,动态加载图片,供大家参考,具体内容如下鼠标滚动事件,当鼠标滚动到下边,动态加载图片。1. HTML代
- 在给blog加上无刷新搜索和即时验证检测后,又看了下代码,感觉太过麻烦,就把XMLHttpRequest请求封装到一个类里面,用起来方便多了
- 本文实例讲述了Laravel框架实现利用 * 进行sql语句记录功能。分享给大家供大家参考,具体如下:利用 * 进行sql语句记录1、监听s
- 类常量是PHP面向对象程序设计中非常重要的一个概念,牢固掌握类常量有助于进一步提高PHP面向对象程序设计的水平。本文即以实例形式描述了PHP
- 创建Spring Boot应用在基于Spring来重构JSP应用之前,我们先引入Spring Boot,使之成为一个Spring Boot应
- 前言序锦很多时候,当我们在使用编辑器的时候,遇到复制粘贴,修改别人代码等等的时候,都会遇到这个让人头疼的问题,那就是如何解决格式问题,今天就
- 前言最新需要做一个小工具,让协作部门能够获取到服务器上的一些资源讯息,因为工具是pyqt写的所以牵扯到用python链接linux的问题,这
- 本文实例为大家分享了python树莓派红外反射传感器的程序,供大家参考,具体内容如下1、工具rpi3,微雪ARPI600,Infrared
- 传统Python语言的主要控制结构是for循环。然而,需要注意的是for循环在Pandas中不常用,因此Python中for循环的有效执行并
- 导语《我的世界》是一款自由度极高的游戏,每个新存档的开启,就像是作为造物主的玩家在虚拟空间开辟了一个全新的宇宙。方块连接世界,云游大好河山。
- 直接上码:select 'bigint unsigned' as type ,'8' as bytes ,~
- 我就废话不多说了,直接上代码吧!import syssys.path.append("..")from datetime
- 在观看唐宇迪老师图像处理的课程中,其中有一个答题卡识别的小项目,在此结合自己理解做一个简单的总结。1. 项目分析首先在拿到项目时候,分析项目
- 本文使用pygame实现播放mp3,文中用到pygame及mutagen库,安装:pip install pygamepip install
- 本文跟大家谈谈为什么要学python以及如何学好python。一、作为初学者,应该如何学python?很多人对python缩进试的简洁表达不