如何处理龙卷风的PeriodicCallback中的阻塞代码

时间:2016-04-27 21:44:11

标签: tornado

我正在使用带龙卷风的websockets。

一旦我收到一条消息(可以在下面的例子中“开始”和“停止”),start将每隔x秒轮询来自memcached的消息,并将json输出返回给websocket客户端。

我使用“tornado.ioloop.PeriodicCallback”每隔x秒轮询一次。所以我的问题是在这种情况下函数“poll_for_new_data”有阻塞代码,即memcached获取,json.dumps转换为json等。我如何处理这个阻塞代码,我认为当调用时阻塞主事件循环慢 ?如果您有不同设计的建议来处理这个问题,请告诉我。我打算使用线程池来阻塞功能,这是一个好主意(或)有更好的方法来做到这一点吗?

我必须为这个项目使用pylibmc(memcached客户端),因为它使用c ++ libmemcached来满足特定需求。我不能使用异步memcache库,除非它在引擎盖下使用c ++ libmemcached。

感谢您的时间

注意:我评论了我的CustomClass特定代码,因为我没有在这里共享代码。

from tornado import ioloop, websocket, httpserver, gen
import tornado
import json
# from custom_class import CustomClass


class CustomWebSocketHandler(tornado.websocket.WebSocketHandler):

    def check_origin(self, origin):
        # for CORS
        print(origin)
        return True

    def open(self):
        print("open " + self.get_argument("user"))

    def on_message(self, message):
        message = message.lower()
        print("on_message: " + message)
        if(message == "start"):
            self.request = {"user": self.get_argument("user")}
            #self.all_pids = set()
            #self.custom_class = CustomClass(self.request)
            # poll for data every x seconds
            self.periodic_callback = tornado.ioloop.PeriodicCallback(
                self.poll_for_new_data, 10 * 1000)
            self.periodic_callback.start()
        elif(message == "stop"):
            self.close()

    def on_close(self):
        print("on_close")
        self.periodic_callback.stop()

    def poll_for_new_data(self):
        if self.ws_connection is None: 
            return

        # check for new pids
        # [queries memcached servers using pylibmc (http://sendapatch.se/projects/pylibmc/) client]
        # new_pids = self.custom_class.get_new_pids(self.all_pids)

        # if(len(new_pids) > 0):
            # if self.ws_connection is not None:
                # self.write_message(json.dumps(new_pids))

        # there is more code here which I did not include 
        # i.e. this function pools for process that register themselves 
        # and each process has messages. so every time this method is called
        # we are looking for new PIDs and new messages for each PID
        # sending the result to websocket client in JSON format


def make_app():
    return tornado.web.Application([
        (r'/ws', CustomWebSocketHandler)
    ])

if __name__ == "__main__":
    app = make_app()
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(8888)
    # http://tornadokevinlee.readthedocs.org/en/latest/ioloop.html
    tornado.ioloop.IOLoop.current().start()

2 个答案:

答案 0 :(得分:1)

一般情况下,我建议使用循环协程而不是PeriodicCallback:http://www.tornadoweb.org/en/stable/guide/coroutines.html#running-in-the-background

这样可以更容易地使用线程池来管理阻塞调用;只需使用yield thread_pool.submit(f)。如果您尝试使用PeriodicCallback向线程池提交任务,那么确保超出不会导致多个请求并行运行可能会非常棘手。

答案 1 :(得分:0)

通常,将新的定期轮询器绑定到打开的每个websocket实例听起来是个坏主意。为什么不在服务器上实现单独的轮询器作为单独的工作进程,并让它在发生时发布更新。

您可以使用Redis的pubsub将每个websocket实例订阅到Redis频道,并在每次更新时从投票工作者发布。