簡體   English   中英

與Tornado服務器的多個異步HTTP連接

[英]Multiple Async HTTP connections to Tornado Server

我有一個試圖使之同步的龍卷風服務器。 我有一個客戶端,它同時向服務器發出異步請求。 它每5秒鍾使用一次心跳ping服務器一次,其次,它會在可能的情況下發出GET請求。

在服務器端,有一個包含作業的線程安全隊列。 如果隊列為空,它將阻塞20秒。 我希望它保持該連接並阻塞20秒鍾,當它返回時,它會向客戶端寫入“無作業”。 作業可用后,應立即將其寫入客戶端,因為queue.get()將返回。 我希望在此請求被阻止的同時,心跳繼續在后台發生。 在這里,我正在從同一客戶端向服務器發出兩個異步請求。

這是我構建的一個示例項目,可以模擬我的問題。

服務器:

import tornado.ioloop
import tornado.web
from queue import Queue
from tornado import gen

q = Queue()


class HeartBeatHandler(tornado.web.RequestHandler):
    @gen.coroutine
    def post(self):
        print("Heart beat")


class JobHandler(tornado.web.RequestHandler):
    @gen.coroutine
    def get(self):
        print("Job")
        try:
            job = yield q.get(block=True, timeout=20)
            self.write(job)
        except Exception as e:
            self.write("No job")


def make_app():
    return tornado.web.Application([
        (r"/heartbeat", HeartBeatHandler),
        (r"/job", JobHandler),
    ])


if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    try:
        tornado.ioloop.IOLoop.current().start()
    except KeyboardInterrupt:
        tornado.ioloop.IOLoop.current().stop()

客戶:

import asyncio
from tornado import httpclient, gen


@gen.coroutine
def heartbeat_routine():
    while True:
        http_client = httpclient.AsyncHTTPClient()
        heartbeat_request = httpclient.HTTPRequest("http://{}/heartbeat".format("127.0.0.1:8888"), method="POST",
                                                   body="")
        try:
            yield http_client.fetch(heartbeat_request)
            yield asyncio.sleep(5)
        except httpclient.HTTPError as e:
            print("Heartbeat failed!\nError: {}".format(str(e)))

        http_client.close()


@gen.coroutine
def worker_routine():
    while True:
        http_client = httpclient.AsyncHTTPClient(defaults=dict(request_timeout=180))
        job_request = httpclient.HTTPRequest("http://{}/job".format("127.0.0.1:8888"), method="GET")
        try:
            response = yield http_client.fetch(job_request)
            print(response.body)
        except httpclient.HTTPError as e:
            print("Heartbeat failed!\nError: {}".format(str(e)))

        http_client.close()


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    asyncio.ensure_future(heartbeat_routine())
    asyncio.ensure_future(worker_routine())
    loop.run_forever()

問題:

  1. 問題在於,當queue.get()阻塞時,心跳也會阻塞這20秒。 我不要。
  2. 如您所見,在我的客戶端中,我將請求超時設置為180秒。 但這似乎與龍卷風無關。 如果將queue.get()超時增加到20秒以上,它將返回錯誤代碼,說明請求已超時。
  1. 如果使用線程安全隊列,則必須不使用IOLoop線程的阻塞操作。 而是在線程池中運行它們:

     job = yield IOLoop.current().run_in_executor(None, lambda: q.get(block=True, timeout=20)) 

    或者,您可以使用Tornado的異步(但線程不安全)隊列,並在需要與另一個線程的隊列進行交互時使用IOLoop.add_callback

  2. AsyncHTTPClient構造函數中有一些魔術,它盡可能地共享現有實例,但這意味着構造函數參數僅在第一次生效。 worker_routine正在拾取heartbeat_routine創建的默認實例。 添加force_instance=True以確保您在worker_routine獲得了一個新客戶端(並在完成后對其調用.close()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM