2014-04-07 11 views
8

Tôi muốn tìm ví dụ máy chủ async đơn giản. tôi đã có một số chức năng với rất nhiều chờ đợi, giao dịch cơ sở dữ liệu ... vv:Ví dụ đơn giản không đồng bộ với python lốc xoáy

def blocking_task(n): 
    for i in xrange(n): 
     print i 
     sleep(1) 
    return i 

tôi cần chạy nó chức năng trong quá trình tách ra mà không ngăn chặn. Có thể không?

Trả lời

16

Tornado được thiết kế để chạy tất cả các hoạt động của bạn trong một chuỗi duy nhất, nhưng sử dụng I/O không đồng bộ để tránh bị chặn càng nhiều càng tốt. Nếu DB bạn đang sử dụng có các ràng buộc Python không đồng bộ (những cái lý tưởng cho Tornado cụ thể, như Motor cho MongoDB hoặc momoko cho Postgres), thì bạn sẽ có thể chạy truy vấn DB mà không chặn máy chủ; không cần quy trình hoặc chủ đề riêng biệt.

Để giải quyết ví dụ chính xác mà bạn đã cung cấp, nơi time.sleep(1) được gọi, bạn có thể sử dụng phương pháp này để làm điều đó không đồng bộ qua coroutines cơn lốc xoáy:

#!/usr/bin/python 

import tornado.web 
from tornado.ioloop import IOLoop 
from tornado import gen 
import time 

@gen.coroutine 
def async_sleep(seconds): 
    yield gen.Task(IOLoop.instance().add_timeout, time.time() + seconds) 

class TestHandler(tornado.web.RequestHandler): 
    @gen.coroutine 
    def get(self): 
     for i in xrange(100): 
      print i 
      yield async_sleep(1) 
     self.write(str(i)) 
     self.finish() 


application = tornado.web.Application([ 
    (r"/test", TestHandler), 
    ]) 

application.listen(9999) 
IOLoop.instance().start() 

Phần thú vị là async_sleep. Phương thức đó đang tạo một Tác vụ không đồng bộ, đang gọi phương thức ioloop.add_timeout. add_timeout sẽ chạy một cuộc gọi lại cụ thể sau một số giây nhất định, mà không chặn ioloop trong khi chờ thời gian chờ hết hạn. Hãng này hy vọng hai đối số:

add_timeout(deadline, callback) # deadline is the number of seconds to wait, callback is the method to call after deadline. 

Như bạn có thể thấy trong ví dụ trên, chúng tôi chỉ thực sự cung cấp một tham số để add_timeout một cách rõ ràng trong các mã, có nghĩa là chúng tôi kết thúc này này:

add_timeout(time.time() + seconds, ???) 

Chúng tôi không cung cấp tham số gọi lại dự kiến. Thực tế, khi gen.Task thực hiện add_timeout, nó sẽ thêm đối số từ khóa callback vào cuối các tham số được cung cấp rõ ràng. Vì vậy, đây:

yield gen.Task(loop.add_timeout, time.time() + seconds) 

Kết quả trong việc này được thực hiện bên trong gen.Task():

loop.add_timeout(time.time() + seconds, callback=gen.Callback(some_unique_key)) 

Khi gen.Callback được thực hiện sau thời gian chờ, nó báo hiệu rằng gen.Task hoàn tất, và thực hiện chương trình sẽ tiếp tục sang dòng tiếp theo. Dòng chảy này rất khó để hiểu đầy đủ, ít nhất là lúc đầu (chắc chắn là đối với tôi khi lần đầu tiên tôi đọc về nó). Có thể sẽ hữu ích khi đọc qua số Tornado gen module documentation một vài lần.

4
import tornado.web 
from tornado.ioloop import IOLoop 
from tornado import gen 

from tornado.concurrent import run_on_executor 
from concurrent.futures import ThreadPoolExecutor # `pip install futures` for python2 

MAX_WORKERS = 16 

class TestHandler(tornado.web.RequestHandler): 
    executor = ThreadPoolExecutor(max_workers=MAX_WORKERS) 

    """ 
    In below function goes your time consuming task 
    """ 

    @run_on_executor 
    def background_task(self): 
     sm = 0 
     for i in range(10 ** 8): 
      sm = sm + 1 

     return sm 

    @tornado.gen.coroutine 
    def get(self): 
     """ Request that asynchronously calls background task. """ 
     res = yield self.background_task() 
     self.write(str(res)) 

class TestHandler2(tornado.web.RequestHandler): 
    @gen.coroutine 
    def get(self): 
     self.write('Response from server') 
     self.finish() 


application = tornado.web.Application([ 
    (r"/A", TestHandler), 
    (r"/B", TestHandler2), 
    ]) 

application.listen(5000) 
IOLoop.instance().start() 

Khi bạn chạy trên mã, bạn có thể chạy một hoạt động tính toán đắt tiền tại http://127.0.0.1:5000/A, mà không chặn thực hiện, nhìn thấy bằng cách truy cập http://127.0.0.1:5000/B ngay lập tức sau khi bạn truy cập http://127.0.0.1:5000/A.

0

Ở đây tôi cập nhật thông tin về Tornado 5.0. Tornado 5.0 thêm phương thức mới IOLoop.run_in_executor. Trong "Chức năng chặn cuộc gọi" của Coroutine patterns Chương:

Cách đơn giản nhất để gọi chức năng chặn từ coroutine là sử dụng IOLoop.run_in_executor, mà trả về tương lai tương thích với coroutines:

@gen.coroutine def call_blocking(): yield IOLoop.current().run_in_executor(blocking_func, args)

Ngoài ra, trong documeng của run_on_executor, là nói:

trang trí này không nên nhầm lẫn với tên tương tự IOLoop.run_in_executor. Nói chung, sử dụng run_in_executor khi gọi phương thức chặn được khuyến nghị thay vì sử dụng trình trang trí này khi xác định phương thức. Nếu cần phải tương thích với các phiên bản Tornado cũ hơn, hãy cân nhắc việc xác định một người thi hành và sử dụng executor.submit() tại trang gọi.

Trong phiên bản 5.0, IOLoop.run_in_executor được đề xuất trong trường hợp sử dụng chức năng Chặn cuộc gọi.

Các vấn đề liên quan