2

geventをwsgiサーバーとして使用し、tornadoWSGIApplicationを使用してリクエストを処理しようとしています。これがコードです

#!/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 -n 1000 -c 100 http://127.0.0.1:9010/

この結果、毎秒100reqになり、遅すぎます。上記のコードでは、httpリクエストを反対側に置くだけです。この状況では、geventはブロックすると他のグリーンレットに切り替わり、パフォーマンスにはほとんど影響しないはずですが、geventのパフォーマンスは1秒あたり1600reqから100に低下します。毎秒のリクエスト、理由がわかりません。

誰かがこれを説明できますか?

4

4 に答える 4

3

こんにちは、あなたの問題は、実際のグリーンレットを生成していないことと、 tornado.web.ascynhronous デコレーターが WSGI サーバーをサポートしていないことです。

しかし、主なロジックは機能し、HTTP サーバーで動作させることができました (WSGI サーバーに関連付けられているかどうかはわかりませんが、リバース プロキシも同様に使用できるため、そうではないと思います)。

多くの人が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()

このサンプルには 2 つの重要な部分があります。あなたが正しく理解したサルのパッチ適用部分と、私が書いたgasyncデコレータです。メソッドを非同期に設定するだけです (トルネードの用語によると、メソッドは self を呼び出さなければならないことを意味します)。 .finish() 単独でクライアントに応答を送信します。これは、トルネードが要求が同期のときに自動的に呼び出すためですが、非同期で必要なものではありません)。

私はそれが役立つことを願っています.コードはここでうまく動作します.

$ 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)

合計時間が最長のリクエストの時間とほぼ同じであることがわかるように、 async の場合は次のことを覚えておいてください。

total_time = max(all_individual_times) + n*some_overhead

ここで、 nはリクエスト数、some_overheadは一定のオーバーヘッドです。

それが役立つことを願っています:)

于 2012-11-08T22:36:09.980 に答える
2

私は同じ必要性を持っていましたが、私は先物と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()
于 2013-08-21T13:36:10.017 に答える