Reputation: 181
I write a comet demo with tornado and tornadoredis, demo can work but sometimes error happends, I have no idea how to fix it. anyone can help me ?
Error :
[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
And my code is here:
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'
another ques, @tornado.web.asynchronous is the right way to use ? every method has @tornado.web.asynchronous ...
Upvotes: 1
Views: 2743
Reputation: 730
This decorator should only be applied to the HTTP verb methods; its behavior is undefined for any other method. This decorator does not make a method asynchronous; it tells the framework that the method is asynchronous. For this decorator to be useful the method must (at least sometimes) do something asynchronous.
Upvotes: 0
Reputation: 22154
In general, avoid mixing the coroutine and asynchronous callback styles. Here, your get() method is a coroutine which will finish the request automatically when it returns, but it has also started the callback-based listen()
which will try to finish the request later.
You need to either add some sort of coordination to ensure that the get() coroutine does not return until the callback has finished the request (a toro.Event is good for this) or get rid of the yield gen.Task
and use an explicit callback when subscribing. In either case, you should only have one of @asynchronous
or @coroutine
on the get()
method, and no decorators on the others.
Upvotes: 2
Reputation: 2160
You need @tornado.web.asynchronous
only for get
method. Also remove @tornado.gen.coroutine
under get
method.
As stated in docs for @tornado.web.asynchronous
http://tornado.readthedocs.org/en/latest/web.html#tornado.web.asynchronous
This decorator should only be applied to the HTTP verb methods; its behavior is undefined for any other method. This decorator does not make a method asynchronous; it tells the framework that the method is asynchronous.
So you can't use it for on_message
and ready_finish
, remove it.
For on_message
and ready_finish
you could use @tornado.gen.coroutine
if you have any async calls there.
Upvotes: 0