2015-04-10 3 views
1

Я пишу демо-версию кометы с торнадо и tornadoredis, демо может работать, но иногда происходит ошибка, я понятия не имею, как это исправить. кто-нибудь может мне помочь?tornado finish() называется дважды

Ошибка:

[E 150410 18:18:44 web:1421] Uncaught exception GET /comet?channelKey=channel%3Awx4g1nej7fuu&message_id=193 (127.0.0.1) 
    HTTPServerRequest(protocol='http', host='tornado.test.com', method='GET', uri='/comet?channelKey=channel%3Awx4g1nej7fuu&message_id=193', version='HTTP/1.1', remote_ip='127.0.0.1', headers={'Remote-Addr': 'xxx', 'Service': 'android', 'X-Forwarded-For': 'xxx', 'User-Agent': 'Apache-HttpClient/UNAVAILABLE (java 1.4)', 'Host': 'tornado.test.wolonge.com', 'X-Requested-With': 'XMLHttpRequest', 'X-Real-Ip': 'xxx', 'Cookie': 'logintoken=gsvimqqefr8f9duu66emjrbbe5_8be6AsNqW%2B3kwH7OsnT0OAKbZNNqnzabDIVcFDE8TvyozQ7h'}) 
    Traceback (most recent call last): 
     File "/usr/lib64/python2.6/site-packages/tornado/web.py", line 1302, in _stack_context_handle_exception 
     raise_exc_info((type, value, traceback)) 
     File "/usr/lib64/python2.6/site-packages/tornado/web.py", line 1489, in wrapper 
     result = method(self, *args, **kwargs) 
     File "/home/wwwroot/wolongge_mobile/app/tornado_push/models/ws_handle.py", line 53, in ready_finish 
     self.finish(res) 
     File "/usr/lib64/python2.6/site-packages/tornado/web.py", line 863, in finish 
     raise RuntimeError("finish() called twice. May be caused " 
    RuntimeError: finish() called twice. May be caused by using async operations without the @asynchronous decorator 

И мой код здесь:

class GroupChat(tornado.web.RequestHandler): 
    def initialize(self): 
     print 'GroupChat here' 
     self.c = tornadoredis.Client(host=CONFIG['REDIS_HOST'], port=CONFIG['REDIS_PORT'], password=CONFIG['REDIS_AUTH']) 
    @tornado.gen.coroutine 
    @tornado.web.asynchronous 
    def get(self): 
     try: 
      self.key = self.get_argument('channelKey') 
      # print 'key:%s' % self.key 
      self.key = url_unescape(self.key); 
      # print 'key:%s' % self.key 
      if(self.key): 
       yield tornado.gen.Task(self.c.subscribe, self.key) 
       self.c.listen(self.on_message) 
     except Exception, e: 
      self.c.disconnect() 
      self.ready_finish('Bad Request (Missing argument)') 
      print e 
     pass 


    @tornado.web.asynchronous 
    def on_message(self, msg): 
     if (msg.kind == 'message'): 
      print msg 
      message_id = int(self.get_argument('message_id')) 
      max_message_id = int(msg.body) 
      if(message_id < max_message_id): 
       self.ready_finish('1') 
      else: 
       self.ready_finish('0') 
     elif (msg.kind == 'unsubscribe'): 
      self.c.disconnect() 

    @tornado.web.asynchronous 
    def ready_finish(self, res): 
     if (self.c.subscribed): 
      self.c.unsubscribe(self.key) 
     self.finish(res) 


    def on_connection_close(self): 
     print 'on_connection_close here' 

другой Ques, @ tornado.web.asynchronous правильный способ использовать? каждый метод имеет @ tornado.web.асинхронный ...

ответ

0

Вам нужен @tornado.web.asynchronous только для get способ. Также удалите @tornado.gen.coroutine по методу get.

Как указано в документации для @tornado.web.asynchronous http://tornado.readthedocs.org/en/latest/web.html#tornado.web.asynchronous

Этот декоратор должен быть применен только к методам HTTP-глагола; его поведение не определено для любого другого метода. Этот декоратор не делает метод асинхронным; он сообщает структуре, что метод асинхронный.

Поэтому вы не можете использовать его для on_message и ready_finish, удалите его. Для on_message и ready_finish вы можете использовать @tornado.gen.coroutine, если у вас есть асинхронные звонки.

1

В общем, избегайте смешивания стилей сопроцессора и асинхронного обратного вызова. Здесь ваш метод get() является сопрограммой, которая автоматически завершит запрос при возврате, но также запустила listen() с обратным вызовом, который попытается завершить запрос позже.

Вам нужно либо добавить какую-то координацию, чтобы гарантировать, что getout() coroutine не вернется, пока обратный вызов не завершит запрос (toro.Event подходит для этого) или избавиться от yield gen.Task и использовать явный обратный вызов при подписке. В любом случае у вас должен быть только один из @asynchronous или @coroutine по методу get(), а на других нет декораторов.

0

Этот декоратор должен применяться только к методам глагола HTTP; его поведение не определено для любого другого метода. Этот декоратор не делает метод асинхронным; он сообщает структуре, что метод является асинхронным. Для того чтобы этот декоратор был полезным, метод должен (по крайней мере иногда) делать что-то асинхронное.