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



相关文章
|
11天前
|
缓存 监控 API
构建高效可扩展的RESTful API:后端开发的实践指南
【4月更文挑战第26天】在现代Web开发中,构建一个高效、可扩展且易于维护的RESTful API是后端工程师必须面对的挑战。本文将深入探讨如何利用最佳实践和流行技术,设计出符合REST架构原则的服务端接口。我们将重点讨论API版本控制、资源路由、数据库优化、缓存策略以及安全性考虑等方面,旨在为开发者提供一套综合性解决方案,帮助其提升API的性能与可靠性。
|
2月前
|
XML 开发框架 JSON
flea-jersey使用之Flea RESTful接口客户端接入
本篇介绍 Flea框架下的 flea-jersey-client模块,并提供客户端依赖管理及接入步骤
26 1
flea-jersey使用之Flea RESTful接口客户端接入
|
2月前
|
开发框架 API 网络架构
flea-jersey使用之Flea RESTful接口服务端接入
本篇介绍 Flea框架下的 flea-jersey-server模块,并提供服务端依赖管理及接入步骤
29 1
flea-jersey使用之Flea RESTful接口服务端接入
|
7天前
|
JSON API 数据处理
【Swift开发专栏】Swift中的RESTful API集成实战
【4月更文挑战第30天】本文探讨了在Swift中集成RESTful API的方法,涉及RESTful API的基础概念,如HTTP方法和设计原则,以及Swift的网络请求技术,如`URLSession`、`Alamofire`和`SwiftyJSON`。此外,还强调了数据处理、错误管理和异步操作的重要性。通过合理利用这些工具和策略,开发者能实现高效、稳定的API集成,提升应用性能和用户体验。
|
2月前
|
人工智能 运维 监控
构建高性能微服务架构:现代后端开发的挑战与策略构建高效自动化运维系统的关键策略
【2月更文挑战第30天】 随着企业应用的复杂性增加,传统的单体应用架构已经难以满足快速迭代和高可用性的需求。微服务架构作为解决方案,以其服务的细粒度、独立性和弹性而受到青睐。本文将深入探讨如何构建一个高性能的微服务系统,包括关键的设计原则、常用的技术栈选择以及性能优化的最佳实践。我们将分析微服务在处理分布式事务、数据一致性以及服务发现等方面的挑战,并提出相应的解决策略。通过实例分析和案例研究,我们的目标是为后端开发人员提供一套实用的指南,帮助他们构建出既能快速响应市场变化,又能保持高效率和稳定性的微服务系统。 【2月更文挑战第30天】随着信息技术的飞速发展,企业对于信息系统的稳定性和效率要求
|
7天前
|
前端开发 API 网络架构
RESTful接口与http协议状态表述
该文介绍了RESTful风格API的好处和设计原则。RESTful API以资源为中心,通过HTTP方法(GET, POST, PUT, DELETE)表示操作,利用状态码反馈操作结果。它简化了客户端与服务端的交互,提供了一种通用规范,减少沟通成本。设计要点包括:URI描述资源,HTTP方法体现操作,使用标准HTTP状态码,GET不改变数据,使用复数名词,支持复杂资源关系,可选实现HATEOAS,以及接口版本管理。
|
7天前
|
NoSQL Java API
java一行代码实现RESTFul接口
Spring Data REST是构建在Spring Data之上的库,可自动将repository转换为REST服务,支持JPA、MongoDB、Neo4j、GemFire和Cassandra。无需手动创建Service和Controller层。要开始,需配置JPA数据源,创建实体类和Repository接口。快速实现REST接口,只需引入spring-boot-starter-data-rest Maven依赖,并在Repository接口上添加@RepositoryRestResource注解。
|
7天前
|
存储 关系型数据库 Go
【Go语言专栏】基于Go语言的RESTful API开发
【4月更文挑战第30天】本文介绍了使用Go语言开发RESTful API的方法,涵盖了路由、请求处理、数据存储和测试关键点。RESTful API基于HTTP协议,无状态且使用标准方法表示操作。在Go中,通过第三方库如`gorilla/mux`进行路由映射,使用`net/http`处理请求,与数据库交互可选ORM库`gorm`,测试则依赖于Go内置的`testing`框架。Go的简洁性和并发性使得它成为构建高效API的理想选择。
|
7天前
|
机器学习/深度学习 算法 安全
深度学习在图像识别中的应用与挑战构建高效可扩展的RESTful API:后端开发的实战指南
【4月更文挑战第30天】 随着计算机视觉技术的飞速发展,深度学习在图像识别领域取得了显著的成果。本文将探讨深度学习技术在图像识别中的应用及其所面临的挑战。首先,我们将介绍深度学习的基本原理和关键技术,然后分析其在图像识别中的优势和应用案例。最后,我们将讨论当前深度学习在图像识别领域所面临的主要挑战和未来的发展趋势。
|
8天前
|
XML JSON API
【PHP开发专栏】PHP RESTful API设计与开发
【4月更文挑战第29天】本文探讨了在Web开发中流行的前后端分离模式,重点介绍了RESTful API的设计与实现。REST是一种基于HTTP协议的架构风格,核心概念包括资源、表述和状态转换。RESTful API设计遵循无状态、统一接口等原则,使用GET、POST、PUT、DELETE等HTTP方法执行操作,并通过状态码和JSON/XML传输数据。在PHP中实现RESTful API,可通过定义路由、创建控制器、处理请求和响应,同时注意安全性措施,如使用HTTPS。文中还提供了一个用户管理API的实战示例,以帮助读者更好地理解和应用RESTful API。