高性能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,如需转载请自行联系原作者



相关文章
|
1月前
|
XML 开发框架 JSON
flea-jersey使用之Flea RESTful接口客户端接入
本篇介绍 Flea框架下的 flea-jersey-client模块,并提供客户端依赖管理及接入步骤
25 1
flea-jersey使用之Flea RESTful接口客户端接入
|
1月前
|
开发框架 API 网络架构
flea-jersey使用之Flea RESTful接口服务端接入
本篇介绍 Flea框架下的 flea-jersey-server模块,并提供服务端依赖管理及接入步骤
29 1
flea-jersey使用之Flea RESTful接口服务端接入
|
1月前
|
缓存 安全 测试技术
构建高效的RESTful API:后端开发的实践指南
【2月更文挑战第17天】在数字化转型的浪潮中,RESTful API已成为连接不同软件组件、实现数据交互的核心桥梁。本文将深入探讨如何构建一个高效、可扩展且安全的RESTful API,涉及设计原则、开发流程以及性能优化等关键方面。我们将透过实际案例,展示如何在保证简洁性和灵活性的同时,满足日益增长的业务需求和技术挑战。
|
1月前
|
人工智能 运维 监控
构建高性能微服务架构:现代后端开发的挑战与策略构建高效自动化运维系统的关键策略
【2月更文挑战第30天】 随着企业应用的复杂性增加,传统的单体应用架构已经难以满足快速迭代和高可用性的需求。微服务架构作为解决方案,以其服务的细粒度、独立性和弹性而受到青睐。本文将深入探讨如何构建一个高性能的微服务系统,包括关键的设计原则、常用的技术栈选择以及性能优化的最佳实践。我们将分析微服务在处理分布式事务、数据一致性以及服务发现等方面的挑战,并提出相应的解决策略。通过实例分析和案例研究,我们的目标是为后端开发人员提供一套实用的指南,帮助他们构建出既能快速响应市场变化,又能保持高效率和稳定性的微服务系统。 【2月更文挑战第30天】随着信息技术的飞速发展,企业对于信息系统的稳定性和效率要求
|
13天前
|
小程序 前端开发 API
小程序全栈开发中的RESTful API设计
【4月更文挑战第12天】本文探讨了小程序全栈开发中的RESTful API设计,旨在帮助开发者理解和掌握相关技术。RESTful API基于REST架构风格,利用HTTP协议进行数据交互,遵循URI、客户端-服务器架构、无状态通信、标准HTTP方法和资源表述等原则。在小程序开发中,通过资源建模、设计API接口、定义资源表述及实现接口,实现前后端高效分离,提升开发效率和代码质量。小程序前端利用微信API与后端交互,确保数据流通。掌握这些实践将优化小程序全栈开发。
|
23天前
|
前端开发 Java API
构建RESTful API:Java中的RESTful服务开发
【4月更文挑战第3天】本文介绍了在Java环境中构建RESTful API的重要性及方法。遵循REST原则,利用HTTP方法处理资源,实现CRUD操作。在Java中,常用框架如Spring MVC简化了RESTful服务开发,包括定义资源、设计表示层、实现CRUD、考虑安全性、文档和测试。通过Spring MVC示例展示了创建RESTful服务的步骤,强调了其在现代Web服务开发中的关键角色,有助于提升互操作性和用户体验。
构建RESTful API:Java中的RESTful服务开发
|
1月前
|
人工智能 JSON 运维
AI大模型运维开发探索第三篇:深入浅出运维智能体
大模型出现伊始,我们就在SREWorks开源社区征集相关的实验案例。玦离同学提供了面向大数据HDFS集群的智能体案例,非常好地完成了运维诊断的目标。于是基于这一系列的实验和探索。本文详细介绍智能体在运维诊断中的应用探索。
|
1月前
|
缓存 前端开发 API
构建高效可扩展的RESTful API:后端开发的最佳实践
【2月更文挑战第30天】 在现代Web应用和服务端架构中,RESTful API已成为连接前端与后端、实现服务间通信的重要接口。本文将探讨构建一个高效且可扩展的RESTful API的关键步骤和最佳实践,包括设计原则、性能优化、安全性考虑以及错误处理机制。通过这些实践,开发者可以确保API的健壮性、易用性和未来的可维护性。
|
1月前
|
JSON Java API
Springboot项目中如何设计一个规范的统一的Restful API 响应框架?
Springboot项目中如何设计一个规范的统一的Restful API 响应框架?
23 1
|
1月前
|
前端开发 API 网络架构
Python 如何开发出RESTful Web接口,DRF框架助力灵活实现!
Python 如何开发出RESTful Web接口,DRF框架助力灵活实现!