2017-01-01 57 views
1

我試圖通過長輪詢建立一個Web服務器通過AJAX收集「命令」,然後分發的命令來客戶端讀取。Python的龍捲風 - 如何實現長輪詢服務器從隊列

目標是某人將某些數據POST到/ add-command。

另一個客戶實現長輪詢客戶擊球/輪詢等待執行的命令。

我想隊列是正確的數據結構,用來保持命令等待關注。我希望這些命令基本上可以立即分發給任何長輪詢客戶端,但如果沒有客戶端正在輪詢,則保留這些命令。

這是我的python腳本。

import os 
import time 
import tornado.httpserver 
import tornado.ioloop 
import tornado.web 
import tornado.gen 
import Queue 
import multiprocessing.pool 
import mysql.connector 
import urlparse 
import uuid 
import json 

_commandQueue = Queue.Queue() 
_commandPollInterval = 0.2 
_commandPollTimeout = 10 

class HomeHandler(tornado.web.RequestHandler): 
    def get(self): 
     self.render("home.htm") 

class AddCommandHandler(tornado.web.RequestHandler): 
    def post(self): 
     d = urlparse.parse_qs(self.request.body) 
     _commandQueue.put(d) 
     self.write(str(True)) 

class PollHandler(tornado.web.RequestHandler): 
    @tornado.gen.coroutine 
    def get(self): 
     self.write("start") 
     d = 1 
     d = yield self.getCommand() 
     self.write(str(d)) 
     self.write("end") 
     self.finish() 
    @tornado.gen.coroutine 
    def getCommand(self): 
     start = time.time() 
     while (time.time() - start) < _commandPollTimeout * 1000: 
      if not _commandQueue.empty: 
       return _commandQueue.get() 
      else: 
       time.sleep(_commandPollInterval) 
     return None 

def main(): 
    application = tornado.web.Application(
     [ 
      (r"/", HomeHandler), 
      (r"/add-command", AddCommandHandler), 
      (r"/poll", PollHandler), 
     ], 
     debug=True, 
     template_path=os.path.join(os.path.dirname(__file__), "templates"), 
     static_path=os.path.join(os.path.dirname(__file__), "static"), 
    ) 
    tornado.httpserver.HTTPServer(application).listen(int(os.environ.get("PORT", 5000))) 
    tornado.ioloop.IOLoop.instance().start() 

if __name__ == "__main__": 
    main() 

AddCommandHandler工作正常,把項目的_commandQueue

PollHandler請求只是超時。如果我打電話給PollHandler,它似乎鎖定了_commandQueue,我無法擺脫它。

我懷疑我需要加入隊列,但我似乎無法找到合適的時間在代碼中執行此操作。

更新 - 這是我的最終代碼感謝答案

import os 
import time 
import datetime 
import tornado.httpserver 
import tornado.ioloop 
import tornado.web 
import tornado.gen 
import tornado.queues 
import urlparse 
import json 

_commandQueue = tornado.queues.Queue() 
_commandPollInterval = 0.2 
_commandPollTimeout = 10 

class HomeHandler(tornado.web.RequestHandler): 
    def get(self): 
     self.render("home.htm") 

class AddCommandHandler(tornado.web.RequestHandler): 
    def get(self): 
     cmd = urlparse.parse_qs(self.request.body) 
     _commandQueue.put(cmd) 
     self.write(str(cmd)) 
    def post(self): 
     cmd = urlparse.parse_qs(self.request.body) 
     _commandQueue.put(cmd) 
     self.write(str(cmd)) 

class PollHandler(tornado.web.RequestHandler): 
    @tornado.gen.coroutine 
    def get(self): 
     cmd = yield self.getCommand() 
     self.write(str(cmd)) 
    @tornado.gen.coroutine 
    def getCommand(self): 
     try: 
      cmd = yield _commandQueue.get(
       timeout=datetime.timedelta(seconds=_commandPollTimeout) 
      ) 
      raise tornado.gen.Return(cmd) 
     except tornado.gen.TimeoutError: 
      raise tornado.gen.Return() 

def main(): 
    application = tornado.web.Application(
     [ 
      (r"/", HomeHandler), 
      (r"/add-command", AddCommandHandler), 
      (r"/poll", PollHandler), 
     ], 
     debug=True, 
     template_path=os.path.join(os.path.dirname(__file__), "templates"), 
     static_path=os.path.join(os.path.dirname(__file__), "static"), 
    ) 
    tornado.httpserver.HTTPServer(application).listen(int(os.environ.get("PORT", 5000))) 
    tornado.ioloop.IOLoop.instance().start() 

if __name__ == "__main__": 
    main() 

回答

1

在異步模式,你應該忽略阻塞操作,time.sleep是在你的代碼的邪惡。此外,我認爲最好的方法是使用龍捲風(在異步接口)隊列 - tornado.queue.Queue,並使用異步獲取:

import datetime 
import tornado.gen 
import tornado.queues 

_commandQueue = tornado.queues.Queue() 


    # ...rest of the code ... 

    @tornado.gen.coroutine 
    def getCommand(self): 
     try: 
      # wait for queue item if cannot obtain in timeout raise exception 
      cmd = yield _commandQueue.get(
       timeout=datetime.timedelta(seconds=_commandPollTimeout) 
      ) 
      return cmd 
     except tornado.gen.Timeout: 
      return None 

注:由於龍捲風4.x的可用模塊tornado.queues SI,如果你使用舊的一個, Toro將有所幫助。

+0

這指出我在正確的方向。這裏有一些小的更正。它應該是tornado.queues.Queue。你也不能從協程中返回,所以我不得不提高tornado.gen.Return(cmd)。非常感謝,我很困難。 – OneCleverMonkey

+0

我已根據您的評論進行更新。自Python 3.2以來,'return'在協程(生成器函數)中是有效的。 – kwarunek

1

不能使用睡眠監聽器,因爲它阻止輸入流中讀取數據。 time.sleep(_commandPollInterval)。你應該使用yield gen.sleep(_commandPollInterval)