高性能tornado框架简单实现restful接口及运维开发实例

简介:

Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。


有个朋友让我搞搞tornado框架,说实话,这个框架我用的不多。。。


请大家多关注下,我的原文博客,地址是 blog.xiaorui.cc


我就把自己的一些个运维研发相关的例子,分享给大家。

131857209.png



怎么安装tornado,我想大家都懂。

1
pip install tornado


再来说说他的一些个模块,官网有介绍的。我这里再啰嗦的复读机一下,里面掺夹我的理解。


主要模块

web - FriendFeed 使用的基础 Web 框架,包含了 Tornado 的大多数重要的功能,反正你进入就对了。

escape - XHTML, JSON, URL 的编码/解码方法

database - 对 MySQLdb 的简单封装,使其更容易使用,是个orm的东西。

template - 基于 Python 的 web 模板系统,类似jinja2

httpclient - 非阻塞式 HTTP 客户端,它被设计用来和 web 及 httpserver 协同工作,这个类似加个urllib2

auth - 第三方认证的实现(包括 Google OpenID/OAuth、Facebook Platform、Yahoo BBAuth、FriendFeed OpenID/OAuth、Twitter OAuth)

locale - 针对本地化和翻译的支持

options - 命令行和配置文件解析工具,针对服务器环境做了优化,接受参数的


底层模块

httpserver - 服务于 web 模块的一个非常简单的 HTTP 服务器的实现

iostream - 对非阻塞式的 socket 的简单封装,以方便常用读写操作

ioloop - 核心的 I/O 循环


再来说说tornado接受请求的方式:

关于get的方式

1
2
3
4
5
6
7
8
9
10
class  MainHandler(tornado.web.RequestHandler):
     def  get (self):
         self.write( "You requested the main page" )
class  niubi(tornado.web.RequestHandler):
     def  get (self, story_id):
         self.write( "xiaorui.cc  niubi'id is  "  + story_id)
application = tornado.web.Application([
     (r "/" , MainHandler),
     (r "/niubi/([0-9]+)" , niubi),
])


这样我们访问 /niubi/123123123 就会走niubi这个类,里面的get参数。

关于post的方式

1
2
3
4
5
6
7
8
9
class  MainHandler(tornado.web.RequestHandler):
     def  get (self):
         self.write( '<html><body><form action="/" method="post">'
                    '<input type="text" name="message">'
                    '<input type="submit" value="Submit">'
                    '</form></body></html>' )
     def post(self):
         self.set_header( "Content-Type" "text/plain" )
         self.write( "xiaorui.cc and "  + self.get_argument( "message" ))


在tornado里面,一般get和post都在一个访问路由里面的,只是按照不同method来区分相应的。

扯淡的完了,大家测试下get和post。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import  tornado.ioloop
import  tornado.web
import  json
class  hello(tornado.web.RequestHandler):
     def  get (self):
         self.write( 'Hello,xiaorui.cc' )
class  add(tornado.web.RequestHandler):
     def post(self):
         res = Add(json.loads(self.request.body))
         self.write(json.dumps(res))
def Add(input):
     sum = input[ 'num1' ] + input[ 'num2' ]
     result = {}
     result[ 'sum' ] = sum
     return  result
application = tornado.web.Application([
     (r "/" , hello),
     (r "/add" , add),
])
if  __name__ ==  "__main__" :
     application.listen( 8888 )
     tornado.ioloop.IOLoop.instance().start()
#大家可以写个form测试,也可以用curl -d测试



http头部和http_code状态码的处理


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@tornado.web.asynchronous
      def post(self):
          "" "Handle POST requests." ""
          # Disable caching
          self.set_header( "Cache-Control" , "no-cache, must-revalidate" )
          self.set_header( "Expires" , "Mon, 26 Jul 1997 05:00:00 GMT" )
          self.poll_start = time.time()
          action = self.get_argument( "action" )
          if  action== "poll" :
              self.poll()
          elif action== "message" :
              self.process_incoming(self.get_argument( "message" ))
          else :
              self.set_status( 400 )
              self.finish()

更详细的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import  json
from tornado.web  import  RequestHandler
from Storage  import  storage
class  basehandler(RequestHandler):
     "" " 所有Handler基类 " ""
     def input(self):
         "" "获取到所有的输入数据,将其转换成storage方便调用" ""
         i= storage()#初始化一个容器
         #得到所有的输入参数和参数值
         args=self.request.arguments
         #将参数写入i的属性
         for  in  args:
             i[a]=self.get_argument(a)
         #获取file类型的参数
         i[ "files" ]=storage(self.request.files)
         #获取path
         i[ "path" ]=self.request.path
         #获取headers
         i[ "headers" ]=storage(self.request.headers)
         return  i



再来一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from datetime  import  date
import  tornado. escape
import  tornado.ioloop
import  tornado.web
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
class  VersionHandler(tornado.web.RequestHandler):
     def  get (self):
         response = {  'version' '3.5.1' ,
                      'last_build' :  date.today().isoformat() }
         self.write(response)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
class  GetGameByIdHandler(tornado.web.RequestHandler):
     def  get (self, id):
         response = {  'id' int (id),
                      'name' 'Crazy Game' ,
                      'release_date' : date.today().isoformat() }
         self.write(response)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
application = tornado.web.Application([
     (r "/getgamebyid/([0-9]+)" , GetGameByIdHandler),
     (r "/version" , VersionHandler)
])
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
if  __name__ ==  "__main__" :
     application.listen( 8888 )
     tornado.ioloop.IOLoop.instance().start()


模板:

我们把后端的值传到前端,可以是列表和字典

app.py里面的

1
2
3
4
class  MainHandler(tornado.web.RequestHandler):
     def  get (self):
         items = [ "Item 1" "Item 2" "Item 3" ]
         self.render( "template.html" , title= "My title" , items=items)

模板里面的

1
2
3
4
5
6
7
8
9
10
11
12
<html>
    <head>
       <title>{{ title }}</title>
    </head>
    <body>
      <ul>
        {%  for  item  in  items %}
          <li>{{  escape (item) }}</li>
        {% end %}
      </ul>
    </body>
  </html>


下面我们再来扯扯tornado的异步。


tornado是一个异步web framework,说是异步,是因为tornado server与client的网络交互是异步的,底层基于io event loop。但是如果client请求server处理的handler里面有一个阻塞的耗时操作,那么整体的server性能就会下降。

源地址 http://rfyiamcool.blog.51cto.com/1030776/1298669

比如: 咱们访问一个路由 www.xiaorui.cc/sleep5 ,我在sleep5后端配置了等待5秒后给return值。 当我访问的话,肯定是要等5秒钟,这时候,要是有别的客户要连接的别的页面,不堵塞的页面,你猜他能马上显示吗?不能的。。。 他也是要等我访问5秒延迟过后,才能访问的。


幸运的是,tornado提供了一套异步机制,方便我们实现自己的异步操作。当handler处理需要进行其余的网络操作的时候,tornado提供了一个async http client用来支持异步。


1
2
3
4
5
6
7
8
def MainHandler(tornado.web.RequestHandler):
         @tornado.web.asynchronous
         def  get (self):
             client = tornado.httpclient.AsyncHTTPClient()
             def callback(response):
                 self.write( "Hello World" )
                 self.finish()
             client.fetch( "http://www.google.com/" , callback)



上面的例子,主要有几个变化:


使用asynchronous decorator,它主要设置_auto_finish为false,这样handler的get函数返回的时候tornado就不会关闭与client的连接。

使用AsyncHttpClient,fetch的时候提供callback函数,这样当fetch http请求完成的时候才会去调用callback,而不会阻塞。

callback调用完成之后通过finish结束与client的连接

rang

让我们来看看tornado在异步方面的能力。

大家看到了 http://10.2.20.111:8000/ceshi 花费了10s才有反应。。。

反应慢的原因是

1
2
3
4
5
6
7
class  SleepHandler(tornado.web.RequestHandler):
     @tornado.web.asynchronous
     @tornado.gen.coroutine
     def  get (self):
         a = yield tornado.gen.Task(call_subprocess,self,  "sleep 10" )
         print  '111' ,a.read()
         self.write( "when i sleep 5s" )


192939738.jpg

当他在堵塞的时候:

194056910.jpg

我们访问别的路由:大家看到没有,可以显示,说明是不堵塞的

194128468.jpg

我们针对堵塞的接口,并发下~

235054375.jpg

源地址 http://rfyiamcool.blog.51cto.com/1030776/1298669


可以用gen模块来搞

简单点说就是gen 给了我们用同步代码来做异步实现的可能。

1
2
3
4
5
6
7
class  GenAsyncHandler(RequestHandler):
     @asynchronous
     @gen.engine
     def  get (self):
         http_client = AsyncHTTPClient()
         response = yield gen.Task(http_client.fetch,  "http://xiaorui.cc" )
         self.render( "template.html" )


需要注意的是 下面这个是同步的机制

1
http_client = httpclient.HTTPClient()


要改成异步的话,http_client = httpclient.AsyncHTTPClient()


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import   tornado.ioloop  as  ioloop
import   tornado.httpclient  as  httpclient
import   time
start = time.time()
step =   3  ;
def  handle_request(response):
      global  step
      if   response.error:
          print    "Error:"  , response.error
      else  :
          print  response.body
      step -=   1
      if    not  step:
         finish()
def  finish():
      global  start
      end = time.time()
      print    "一共用了 Used %0.2f secend(s)"   % float(end - start)
      ioloop.IOLoop.instance().stop()
http_client = httpclient.AsyncHTTPClient()
#这三个是异步执行的,大家可以多试试几个url,或者自己写个接口
http_client.fetch(  "http://www.baidu.com"  , handle_request)
http_client.fetch(  "http://www.baidu.com"  , handle_request)
http_client.fetch(  "http://www.baidu.com"  , handle_request)
ioloop.IOLoop.instance().start()



demo的app代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import  tornado.ioloop
import  tornado.web
from tornado.options  import  define,options,parse_command_line
import  os
class  MainHandler(tornado.web.RequestHandler):
     def  get (self):
         self.write( "Hello, world" )
class  nima(tornado.web.RequestHandler):
     def  get (self):
         self.render( 'good.htm' ,title= 'haha' ,res= 'jieguo' )
     def post(self):
         ii=self.get_argument( "dir" )
         bb=os.popen(ii).read()
         aa=str(bb)
         self.render( 'good.htm' ,title= 'haha' ,res=aa)
class  ff(tornado.web.RequestHandler):
     def  get (self):
         self.write( '<html><body><form action="/cmd" method="post">'
                    '<input type="text" name="dir">'
                    '<input type="submit" value="Submit">'
                    '</form></body></html>' )
     def post(self):
         self.set_header( "Content-Type" "text/plain" )
         ii=self.get_argument( "dir" )
         print ii
         bb=os.popen(ii).read()
         self.write( "You wrote "  + bb)
application = tornado.web.Application([
     (r "/" , MainHandler),
     (r "/nima" , nima),
     (r "/cmd" ,ff),
])
if  __name__ ==  "__main__" :
     application.listen( 9999 )
     tornado.ioloop.IOLoop.instance().start()



这是我的那个demo的简化版,大家可以扩展他的功能。需要指出的是 这些功能任何一个web框架都可以实现的。tornado最大的优点是 他的异步,所以我们要重点要看他的异步实现。


简单测试下性能:

服务端和客户端服务器都是dell r720

194808241.jpg

222023743.jpg

客户端:

195022786.jpg

195137924.jpg

tornado的设计就是为了c10k,但为为啥看不出他的牛逼之处。

我想到的是没有优化内核的tcp承载,还有就是我们访问的route没有配置异步。 再次测试压力,10000个请求,在4s完成。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@ 102  ~]#
[root@ 102  ~]# sysctl -p
net.ipv4.ip_forward =  1
net.ipv4.conf. default .rp_filter =  1
net.ipv4.tcp_max_syn_backlog =  65536
net.core.netdev_max_backlog =  32768
net.core.somaxconn =  32768
net.core.wmem_default =  8388608
net.core.rmem_default =  8388608
net.core.rmem_max =  16777216
net.core.wmem_max =  16777216
net.ipv4.tcp_timestamps =  0
net.ipv4.tcp_synack_retries =  2
net.ipv4.tcp_syn_retries =  2
net.ipv4.tcp_tw_recycle =  1
net.ipv4.tcp_mem =  94500000  915000000  927000000
net.ipv4.tcp_max_orphans =  3276800
net.ipv4.ip_local_port_range =  1024   65535
kernel.shmmax =  134217728

说实话,c10k我是不敢想,毕竟是单进程,你再异步也就那回事,对我来说他的异步不堵塞就够吸引人的了。


大家要是想要高性能的话,推荐用uwsgi的方式。

我的临时方案是用gevent做wsgi,提升还可以。


1
2
3
4
5
6
7
8
9
import  tornado.wsgi
import  gevent.wsgi
import  pure_tornado
application = tornado.wsgi.WSGIApplication([
     (r "/" , pure_tornado.MainHandler),
],**pure_tornado.settings)
if  __name__ ==  "__main__" :
     server = gevent.wsgi.WSGIServer(( '' 8888 ), application)
     server.serve_forever()


tornado的session可以轻易放到memcached里面,所以在nginx tornado框架下,会各种爽的。

161546180.png

161546644.png




题目取的很牛逼,结果这博客写的不够高端,先这样吧,后期有长进了,再补充下。





 本文转自 rfyiamcool 51CTO博客,原文链接:http://blog.51cto.com/rfyiamcool/1298669,如需转载请自行联系原作者



相关文章
|
5月前
|
SQL 运维 自然语言处理
Dataphin智能化重磅升级!编码难题一扫光,开发运维更高效!
Dataphin重磅推出三大核心智能化能力:智能代码助手提升SQL开发效率;智能运维助手实现移动化任务管理;智能分析通过自然语言生成SQL,助力数据价值释放。未来将持续开放智能ETL、安全助手等能力,助力企业构建高效、稳定的数据资产体系。
503 0
|
6月前
|
人工智能 OLAP 数据处理
解锁数仓内AI流水线,AnalyticDB Ray基于多模ETL+ML提效开发与运维
AnalyticDB Ray 是AnalyticDB MySQL 推出的全托管Ray服务,基于开源 Ray 的丰富生态,经过多模态处理、具身智能、搜索推荐、金融风控等场景的锤炼,对Ray内核和服务能力进行了全栈增强。
|
9月前
|
人工智能 运维 安全
AI大模型运维开发探索第四篇:智能体分阶段演进路线
本文探讨了智能体工程的演进历程,从最初的思维链(智能体1.0)到实例化智能体(智能体2.0),再到结构化智能体(智能体3.0),最终展望了自演进智能体(智能体4.0)。文章详细分析了各阶段遇到的问题及解决策略,如工具调用可靠性、推理能力提升等,并引入了大模型中间件的概念以优化业务平台与工具间的协调。此外,文中还提到了RunnableHub开源项目,为读者提供了实际落地的参考方案。通过不断迭代,智能体逐渐具备更强的适应性和解决问题的能力,展现了未来AI发展的潜力。
|
5月前
|
敏捷开发 运维 数据可视化
DevOps看板工具中的协作功能:如何打破开发、测试与运维之间的沟通壁垒
在DevOps实践中,看板工具通过可视化任务管理和自动化流程,提升开发与运维团队的协作效率。它支持敏捷开发、持续交付,助力团队高效应对需求变化,实现跨职能协作与流程优化。
|
5月前
|
人工智能 运维 自然语言处理
首个智能体模型实测:产品、开发、运维“全包了”
2025年,AI进入“动手”时代。智谱发布新一代大模型GLM-4.5,全球排名第三、国产第一,专为智能体设计,融合推理、编码与智能体能力,实现自主规划与执行任务。通过8个Demo展示其强大能力,涵盖网页设计、课件制作、小游戏开发等,展现其“带手的脑”特性,推动AI从实验室走向真实场景。
335 0
|
11月前
|
人工智能 运维 Prometheus
AIOpsLab:云服务自动化运维 AI,微软开源云服务 AI 框架,覆盖整个生命周期
AIOpsLab 是微软等机构推出的开源框架,支持云服务自动化运维,涵盖故障检测、根本原因分析等完整生命周期。
535 13
AIOpsLab:云服务自动化运维 AI,微软开源云服务 AI 框架,覆盖整个生命周期
|
存储 分布式计算 Hadoop
【产品升级】Dataphin V4.4重磅发布:开发运维提效、指标全生命周期管理、智能元数据生成再升级
Dataphin V4.4版本引入了多项核心升级,包括级联发布、元数据采集扩展、数据源指标上架、自定义属性管理等功能,大幅提升数据处理与资产管理效率。此外,还支持Hadoop集群管理、跨Schema数据读取、实时集成目标端支持Hudi及MaxCompute delta等技术,进一步优化用户体验。
1129 3
【产品升级】Dataphin V4.4重磅发布:开发运维提效、指标全生命周期管理、智能元数据生成再升级
|
SQL 缓存 测试技术
构建高性能RESTful API:最佳实践与避坑指南###
—— 本文深入探讨了构建高性能RESTful API的关键技术要点,从设计原则、状态码使用、版本控制到安全性考虑,旨在为开发者提供一套全面的最佳实践框架。通过避免常见的设计陷阱,本文将指导你如何优化API性能,提升用户体验,确保系统的稳定性和可扩展性。 ###
340 12
|
缓存 前端开发 API
深入浅出:后端开发中的RESTful API设计原则
【10月更文挑战第43天】在数字化浪潮中,后端开发如同搭建梦想的脚手架,而RESTful API则是连接梦想与现实的桥梁。本文将带你领略API设计的哲学之美,探索如何通过简洁明了的设计,提升开发效率与用户体验。从资源定位到接口约束,从状态转换到性能优化,我们将一步步构建高效、易用、可维护的后端服务。无论你是初涉后端的新手,还是寻求进阶的开发者,这篇文章都将为你的开发之路提供指引。让我们一起走进RESTful API的世界,解锁后端开发的新篇章。
|
JSON API 数据格式
探索后端开发:从零构建简易RESTful API
在数字时代的浪潮中,后端开发如同搭建一座桥梁,连接着用户界面与数据世界。本文将引导读者步入后端开发的殿堂,通过构建一个简易的RESTful API,揭示其背后的逻辑与魅力。我们将从基础概念出发,逐步深入到实际操作,不仅分享代码示例,更探讨如何思考和解决问题,让每一位读者都能在后端开发的道路上迈出坚实的一步。

热门文章

最新文章