如何使用tornado进行异步ping?

时间:2016-05-22 11:15:47

标签: tornado ping os.system

一个非常简单的龙卷风应用程序,当服务器收到HTTP get请求时,它ping -c 2 www.google.com,然后返回结果。我想要使​​用龙卷风。以下是文章中的代码。

class AsyncTaskHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self, *args, **kwargs):
        response = yield tornado.gen.Task(self.ping, ' www.google.com')
        print 'response', response
        self.finish('hello')

    @tornado.gen.coroutine
    def ping(self, url):
        os.system("ping -c 2 {}".format(url))
        return 'after'

并且作者说ab测试结果很棒。 ab -c 5 -n 5 http://127.0.0.1:5000/async

Document Path:          /async
Document Length:        5 bytes

Concurrency Level:      5
Time taken for tests:   0.009 seconds
Complete requests:      5
Failed requests:        0
Total transferred:      985 bytes
HTML transferred:       25 bytes
Requests per second:    556.92 [#/sec] (mean)
Time per request:       8.978 [ms] (mean)
Time per request:       1.796 [ms] (mean, across all concurrent requests)
Transfer rate:          107.14 [Kbytes/sec] received

但实际上我只使用相同的代码,在我的测试中,每秒请求数为0.77! 我寻找原因。我找到了这个版本:

class IndexHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(10)

    @tornado.gen.coroutine
    def get(self):
        print "begin"
        response = yield self.pin()
        print response
        self.finish()

    @run_on_executor
    def pin(self):
        return os.system("ping -c 2 www.google.com")

测试结果,每秒请求数为0.85。 我想使用龙卷风协程来使1000个或更多的ping命令无阻塞。我该如何编码呢?非常感谢!

2 个答案:

答案 0 :(得分:1)

而不是ThreadPoolExecutoros.system,使用tornado.process.Subprocess效率更高:

class IndexHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        print "begin"
        response = yield self.ping()
        print response
        self.finish()

    @tornado.gen.coroutine
    def ping(self):
        proc = tornado.process.Subprocess("ping -c 2 www.google.com")
        return yield proc.wait_for_exit()

但是,由于ping仍在启动一个单独的进程,因此在这种情况下它不比线程池好多少,并且线程池可用于限制并发ping进程的数量。

答案 1 :(得分:0)

代码的第一个版本一次只运行一个“ping”。 IOLoop被阻止,直到os.system调用返回。

第二个版本使用run_on_executoros.system调用推迟到线程池中的线程,使其成为非阻塞并允许并发调用。

如果(出于某种奇怪的原因?)你想运行1000个并发ping,你需要扩展ThreadPoolExecutor的默认线程数:

thread_pool = ThreadPoolExecutor(1000)

def ping_blocking():
    os.system("ping -c 2 www.google.com")

@gen.coroutine
def ping_many():
    futures = [thread_pool.submit(ping_blocking)
               for _ in range(1000)]
    yield futures

有关详细信息,请参阅the Tornado documentation on calling blocking functions。关于并行执行的文档也在该页面上。