2016-08-31 2 views
1

일부 쉘 명령을 실행하는 웹 서버가 있습니다. 이 명령은 보통 2 초가 걸리지 만 어떤 경우에는 더 많은 시간이 걸리고 클라이언트 (웹 브라우저 나 컬이 아닙니다)의 연결이 끊어집니다.토네이도 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 호출이 완료 될 때까지 토네이도가 차단되어 타이머를 포함한 코드를 실행할 수 없습니다. 그러나,

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를 사용하지 않는 : 당신은 스레드에 대한 호출을 연기한다 토네이도의 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