[英]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()
問題:
如果使用線程安全隊列,則必須不使用IOLoop線程的阻塞操作。 而是在線程池中運行它們:
job = yield IOLoop.current().run_in_executor(None, lambda: q.get(block=True, timeout=20))
或者,您可以使用Tornado的異步(但線程不安全)隊列,並在需要與另一個線程的隊列進行交互時使用IOLoop.add_callback
。
AsyncHTTPClient
構造函數中有一些魔術,它盡可能地共享現有實例,但這意味着構造函數參數僅在第一次生效。 worker_routine
正在拾取heartbeat_routine
創建的默認實例。 添加force_instance=True
以確保您在worker_routine
獲得了一個新客戶端(並在完成后對其調用.close()
)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.