Python 如何提高gevent和tornado组合的性能?
我试图使用gevent作为wsgi服务器,并使用tornado WSGIApplication来处理请求。这是密码Python 如何提高gevent和tornado组合的性能?,python,wsgi,tornado,gevent,Python,Wsgi,Tornado,Gevent,我试图使用gevent作为wsgi服务器,并使用tornado WSGIApplication来处理请求。这是密码 #!/usr/bin/env python # coding=utf-8 import gevent from gevent import monkey monkey.patch_all(thread=False) from gevent.pywsgi import WSGIServer from tornado.wsgi import WSGIApplication imp
#!/usr/bin/env python
# coding=utf-8
import gevent
from gevent import monkey
monkey.patch_all(thread=False)
from gevent.pywsgi import WSGIServer
from tornado.wsgi import WSGIApplication
import tornado.web
import tornado.wsgi
import requests
class MainHandler(tornado.web.RequestHandler):
def get(self):
requests.get('http://google.com')
self.write('hello')
handlers = [
(r'/', MainHandler)
]
if __name__ == '__main__':
application = WSGIApplication(handlers)
server = WSGIServer(('', 9010), application)
server.serve_forever()
我使用apache基准测试性能。测试命令是
ab-n1000-c100http://127.0.0.1:9010/
这导致每秒100req,速度太慢。在上面的代码中,我只是将一个http请求放在另一端,我认为在这种情况下,gevent在阻塞时会切换到其他greenlet,它对性能的影响应该很小,但是gevent的性能从每秒1600req下降到每秒100req,我不明白为什么
有人能解释一下吗?您好,您的问题是,您并没有产生一个真正的greenlet,而且tornado.web.asynchronous decorator不支持WSGI服务器 但是主要的逻辑是有效的,我能够让它与HTTP服务器一起工作(我不知道您是否绑定到WSGI服务器,但我想不会,因为您也可以反转代理) 我发现很多人想把gevent和tornado一起使用,包括我(我们在使用tornado和gevent),所以我写了以下内容:
# Gevent monkeypath
from gevent import monkey
monkey.patch_all()
# Gevent imports
import gevent
# Python immports
import functools
# Tornado imports
import tornado.ioloop
import tornado.web
import tornado.httpserver
# Request imports
import requests
# Asynchronous gevent decorator
def gasync(func):
@tornado.web.asynchronous
@functools.wraps(func)
def f(self, *args, **kwargs):
#self._auto_finish = False
return gevent.spawn(func, self, *args, **kwargs)
return f
# Constants
URL_TO_FETCH = 'http://google.co.uk/'
# Global
I = 0
class MainHandler(tornado.web.RequestHandler):
@gasync
def get(self):
global I
r = requests.get(URL_TO_FETCH)
I += 1
print('Got page %d (length=%d)' % (I, len(r.content)))
self.write("Done")
self.finish()
# Our URL Mappings
handlers = [
(r"/", MainHandler),
]
def main():
# Setup app and HTTP server
application = tornado.web.Application(handlers)
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(9998)
# Start ioloop
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
在这个示例中有两个关键部分,一个是正确的猴子补丁部分,另一个是我编写的gasync装饰程序,它所做的只是将一个方法设置为异步(按照tornado的术语,这意味着该方法必须调用self.finish()自动将响应发送到客户端,因为tornado会在请求同步时自动调用它,但这不是异步中所需的
我希望这会有所帮助,代码在这里运行良好,我已经使用以下工具对其进行了测试:
$ ab -n 100 -c 100 http://localhost:9998/
其中:
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient).....done
Server Software: TornadoServer/2.3
Server Hostname: localhost
Server Port: 9998
Document Path: /
Document Length: 4 bytes
Concurrency Level: 100
Time taken for tests: 0.754 seconds
Complete requests: 100
Failed requests: 0
Write errors: 0
Total transferred: 15900 bytes
HTML transferred: 400 bytes
Requests per second: 132.67 [#/sec] (mean)
Time per request: 753.773 [ms] (mean)
Time per request: 7.538 [ms] (mean, across all concurrent requests)
Transfer rate: 20.60 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 2 4 0.8 4 5
Processing: 379 572 104.4 593 748
Waiting: 379 572 104.4 593 748
Total: 383 576 104.3 596 752
Percentage of the requests served within a certain time (ms)
50% 596
66% 640
75% 672
80% 679
90% 707
95% 722
98% 735
99% 752
100% 752 (longest request)
其中,n是请求数,一些开销是恒定开销
希望有帮助:)如果是真正异步的,请尝试仅使用gevent示例应用程序对chceck测试
请求
包。可能是因为gevent没有正确地修补请求所需的所有内容
但我认为您的解决方案与gevent站点不是异步的:您正在尝试运行2个事件循环。我想你现在的过程是这样的:
- Tornado IOLoop等待事件(对于http服务器)
- HTTP请求已处理
- 处理程序运行
方法,该方法运行gevent事件循环(gevent在专用的greenlet中隐式启动事件循环)get
- gevent的事件循环阻止Tornado greenlet(gevent的事件循环所在greenlet的父级
- 请求的greenlet等待完成
- 请求的greenlet完成,gevent的事件循环关闭,EndTornado IOLoop被释放
再看看Tornado和greenlets的一些成功组合:,。我也有同样的需求,但我正在与futures和gen.coroutine合作,所以我不得不稍微修改它以与我的代码兼容,如果其他人也需要它,我会把它放在这里:
#
# encoding: utf-8
from gevent import monkey
monkey.patch_all()
# Gevent imports
import gevent
# Python immports
import functools
# Tornado imports
import tornado.ioloop
import tornado.web
import tornado.gen
import tornado.httpserver
# Request imports
import requests
from tornado.concurrent import Future
# Asynchronous gevent decorator
def gfuture(func):
@functools.wraps(func)
def f(*args, **kwargs):
loop = tornado.ioloop.IOLoop.current()
future = Future()
def call_method():
try:
result = func(*args, **kwargs)
loop.add_callback(functools.partial(future.set_result, result))
except Exception, e:
loop.add_callback(functools.partial(future.set_exception, e))
gevent.spawn(call_method)
return future
return f
# Constants
URL_TO_FETCH = 'http://google.com/'
# Global
I = 0
@gfuture
def gfetch(url, i):
r = requests.get(url)
return i
class MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
@tornado.gen.coroutine
def get(self):
global I
I += 1
n = I
print "=> %s" % n
n = yield gfetch(URL_TO_FETCH, n)
print "<= %s" % n
self.write("Done %s" % n)
# Our URL Mappings
handlers = [(r"/", MainHandler)]
def main():
# Setup app and HTTP server
application = tornado.web.Application(handlers)
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(9998)
# Start ioloop
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
#
#编码:utf-8
从gevent导入猴子
猴子
#Gevent进口
导入gevent
#Python immports
导入功能工具
#龙卷风进口
导入tornado.ioloop
导入tornado.web
进口tornado.gen
导入tornado.httpserver
#请求导入
导入请求
从tornado.com导入未来
#异步gevent装饰器
def G未来(职能):
@functools.wrapps(func)
def f(*args,**kwargs):
loop=tornado.ioloop.ioloop.current()
future=future()
def call_method():
尝试:
结果=函数(*args,**kwargs)
loop.add_回调(functools.partial(future.set_result,result))
除例外情况外,e:
loop.add_回调(functools.partial(future.set_exception,e))
gevent.spawn(调用方法)
回归未来
返回f
#常数
URL\u至\uhttp://google.com/'
#全球的
I=0
@G未来
def gfetch(url,i):
r=请求。获取(url)
返回i
类MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
@tornado.gen.coroutine
def get(自我):
全球I
I+=1
n=I
打印“=>%s”%n
n=产生gfetch(URL\u到\u获取,n)
打印“龙卷风(扭曲)gevent也做同样的事情,为了获得最佳效果,你应该保持在相同的技术堆栈中,而不是将两者混合使用。或者使用WSGI web框架和gevent,比如瓶子或烧瓶,或者同时使用twisted和tornado。使用ab-n 10000-c 1000,你会得到更好的结果,因为tornado是异步的。为什么要使用gevent?@nikolayFominyh我想使用请求向其他网站发送异步请求,tornado的http客户端不适合我的需要,所以我想将gevent和请求组合在一起tornado@MartinLuo检查我的答案,它应该满足你的需要。它有点长,但我希望它是明确的。别忘了投票和批准:)答案太棒了!但仍然不是我想要的。无论如何,谢谢你!我很好奇,gevent没有运行自己的调度程序吗?它不会在某种程度上与iLoop调度冲突吗?
#
# encoding: utf-8
from gevent import monkey
monkey.patch_all()
# Gevent imports
import gevent
# Python immports
import functools
# Tornado imports
import tornado.ioloop
import tornado.web
import tornado.gen
import tornado.httpserver
# Request imports
import requests
from tornado.concurrent import Future
# Asynchronous gevent decorator
def gfuture(func):
@functools.wraps(func)
def f(*args, **kwargs):
loop = tornado.ioloop.IOLoop.current()
future = Future()
def call_method():
try:
result = func(*args, **kwargs)
loop.add_callback(functools.partial(future.set_result, result))
except Exception, e:
loop.add_callback(functools.partial(future.set_exception, e))
gevent.spawn(call_method)
return future
return f
# Constants
URL_TO_FETCH = 'http://google.com/'
# Global
I = 0
@gfuture
def gfetch(url, i):
r = requests.get(url)
return i
class MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
@tornado.gen.coroutine
def get(self):
global I
I += 1
n = I
print "=> %s" % n
n = yield gfetch(URL_TO_FETCH, n)
print "<= %s" % n
self.write("Done %s" % n)
# Our URL Mappings
handlers = [(r"/", MainHandler)]
def main():
# Setup app and HTTP server
application = tornado.web.Application(handlers)
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(9998)
# Start ioloop
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()