2016-08-31 13 views
1

私はいくつかのシェルコマンドを実行するWebサーバーを持っています。このコマンドには通常数秒かかりますが、場合によってはそれ以上の時間がかかります。クライアント(Webブラウザやカールではありません)が切断されます。竜巻with_timeout正しい使用法

私はクライアントを修正する可能性がないので、サーバーを修正することを考えました。それは竜巻のフレームワークに基づいています。私はtornado.gen.with_timeout関数を使って書き換えましたが、何らかの理由で期待通りに動作しません。タイムアウトを5秒に設定したので、サーバに問い合わせるときに「X秒で終了」(X < 5)または「すでに5秒以上...まだ実行中」のいずれかを取得する予定です。どちらの場合も、私は5秒以内に応答を得ることを期待しています。ここで

はコードです:

import os 
import json 
import datetime 
import random 

from tornado.ioloop import IOLoop 
from tornado.web import RequestHandler, Application 
from tornado.gen import coroutine, with_timeout, TimeoutError, Return 

@coroutine 
def run_slow_command(command): 
    res = os.system(command) 
    raise Return(res) 


class MainHandler(RequestHandler): 
    @coroutine 
    def get(self): 
     TIMEOUT = 5 
     duration = random.randint(1, 15) 

     try: 
      yield with_timeout(datetime.timedelta(seconds=TIMEOUT), run_slow_command('sleep %d' % duration)) 
      response = {'status' : 'finished in %d seconds' % duration} 
     except TimeoutError: 
      response = {'status' : 'already took more than %d seconds... still running' % TIMEOUT} 

     self.set_header("Content-type", "application/json") 
     self.write(json.dumps(response) + '\n') 


def make_app(): 
    return Application([ 
     (r"/", MainHandler), 
    ]) 

if __name__ == "__main__": 
    app = make_app() 
    app.listen(8080) 
    IOLoop.current().start() 

そして、ここにカールから出力さ:

for i in `seq 1 5`; do curl http://127.0.0.1:8080/; done 
{"status": "finished in 15 seconds"} 
{"status": "finished in 12 seconds"} 
{"status": "finished in 3 seconds"} 
{"status": "finished in 11 seconds"} 
{"status": "finished in 13 seconds"} 

私が間違っているのは何?

答えて

1

にはcoroutineが装飾されていますが、それでもos.systemの呼び出しが完了するまで、Tornadoはブロックされており、タイマーを含むコードは実行できません。しかし、

def run_slow_command(command): 
    return thread_pool.submit(os.system, command) 

を代わりにos.systemを使用する:

from concurrent.futures import ThreadPoolExecutor 

thread_pool = ThreadPoolExecutor(4) 

@coroutine 
def run_slow_command(command): 
    res = yield thread_pool.submit(os.system, command) 
    raise Return(res) 

をそれとも、あなただけsubmitで返されるの未来を望んでいるので、まったくcoroutineを使用しないでください:あなたは、スレッドへの呼び出しを延期すべきですあなたはTornadoのown Subprocess supportを使うべきです。すべてをまとめると、この例ではサブプロセスが5秒間待ってからタイムアウトします。

from datetime import timedelta 
from functools import partial 

from tornado import gen 
from tornado.ioloop import IOLoop 
from tornado.process import Subprocess 

@gen.coroutine 
def run_slow_command(command): 
    yield gen.with_timeout(
     timedelta(seconds=5), 
     Subprocess(args=command.split()).wait_for_exit()) 

IOLoop.current().run_sync(partial(run_slow_command, 'sleep 10')) 
+1

これは機能しました。 'run_slow_command'コルーチンを完全に削除しました。(やはり' os.system'を囲むラッパーだけだったのですが)try ... except節で私は元のyieldを 'yield with_timeout(datetime.timedelta(seconds = TIMEOUT)サブプロセス(args =( 'sleep%d'%duration).split())。wait_for_exit()) ' – Graf

関連する問題