2017-01-01 14 views
1

私はAJAX経由で "コマンド"を収集し、長いポーリングでクライアントにコマンドを配布するWebサーバーを構築しようとしています。Python Tornado - 長いポーリングサーバーを実装してキューから読み取る方法

目標は、誰かが/ addコマンドにデータをPOSTすることです。

もう1つのクライアントは、コマンドの実行を待機しているロングポーリングクライアントを実装しています。

キューは、注意を待っているコマンドを保持するために使用する正しいデータ構造です。私はコマンドを基本的にすぐに任意の長いポーリングクライアントに配布するが、クライアントが現在ポーリングしていない場合は保持することを望む。

ここは私の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がロックされているように見えます。

私はキューに参加する必要があると思うが、私はコードでそれを行うための適切な時間を見つけることができないようだ。

UPDATE - ここでは、ブロックは、入力ストリームからの読み込みいるので、私の最終的なコードの答えのおかげで

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はあなたのコードでは悪です。 、トルネード4.xの以降で使用可能モジュールtornado.queues SI、あなたは古いものを使用している場合:

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 

注:tornado.queue.Queueと非同期getを使用 - また、私は最善の方法は、竜巻の(非同期インターフェイスで)キューを使用することであると思いますToroが役に立ちます。

+0

これは正しい方向に私を指摘しました。ここにはいくつかの小さな修正があります。それは竜巻です。キュー.Queue。あなたはコルーチンから戻ることもできないので、代わりにtornado.gen.Return(cmd)を立ち上げなければなりませんでした。ありがとう、私はとてもこだわっていた。 – OneCleverMonkey

+0

私はあなたのコメントに応じて更新しました。 'return'はpython 3.2以降のコルーチン(ジェネレータ関数)で有効です。 – kwarunek

1

あなたは、リスナーで利用眠ることができないのです。 time.sleep(_commandPollInterval)。あなたがを使用する必要がありますyield gen.sleep(_commandPollInterval)

関連する問題