Tornado websocket客户端丢失响应消息?

时间:2017-05-13 11:14:58

标签: python websocket tornado

我需要处理来自网络摄像头的帧并将一些选定的帧发送到远程websocket服务器。服务器立即回复确认消息(很像回显服务器)。 帧处理速度慢且CPU密集,因此我想使用单独的线程池(生产者)来使用所有可用的核心。所以客户端(消费者)只是闲置,直到池有东西要发送。 我当前的实现,见下文,只有在我在生产者测试循环中添加一个小睡眠时才能正常工作。如果我删除此延迟,我将停止从服务器(echo服务器和我的真实服务器)接收任何应答。即使第一个答案丢失了,所以我认为这不是一个防洪机制。 我做错了什么?

client.useXpath().click("//*[matches(@*, '/\b[19]{1,2}.([May]{3,}|[05]{1,2}).[2017]{4}\b/g')]");

我真正的问题

在我的真实节目中,我使用"窗口,如"保护消费者的机制(autobahn.twisted.websocket服务器):生产者可以发送最多数量的未确认消息(网络摄像头帧),然后停止等待一半的窗口释放。 消费者发送" PROCESSED"消息回复确认一条或多条消息(只是一个计数器,而不是id)。 我在消费者日志中看到的是消息被处理并且答案被发回,但这些消息在网络中的某处消失了。

我对asynchio的经验很少,所以我想确保我不会错过任何收益,注释或其他内容。

这是消费者方面的日志:

import tornado
from tornado.websocket import websocket_connect
from tornado import gen, queues

import time

class TornadoClient(object):

    url = None
    onMessageReceived = None
    onMessageSent = None

    ioloop = tornado.ioloop.IOLoop.current()
    q = queues.Queue()

    def __init__(self, url, onMessageReceived, onMessageSent):
        self.url = url
        self.onMessageReceived = onMessageReceived
        self.onMessageSent = onMessageSent

    def enqueueMessage(self, msgData, binary=False):
        print("TornadoClient.enqueueMessage")

        self.ioloop.add_callback(self.addToQueue, (msgData, binary))

        print("TornadoClient.enqueueMessage done")

    @gen.coroutine
    def addToQueue(self, msgTuple):
        yield self.q.put(msgTuple)

    @gen.coroutine
    def main_loop(self):

        connection = None
        try:
            while True:

              while connection is None:
                try:
                    print("Connecting...")
                    connection = yield websocket_connect(self.url)
                    print("Connected " + str(connection))
                except Exception, e:
                    print("Exception on connection " + str(e))
                    connection = None
                    print("Retry in a few seconds...")
                    yield gen.Task(self.ioloop.add_timeout, time.time() + 3)

              try:
                  print("Waiting for data to send...")
                  msgData, binaryVal  = yield self.q.get()
                  print("Writing...")
                  sendFuture = connection.write_message(msgData, binary=binaryVal)
                  print("Write scheduled...")
              finally:
                self.q.task_done()

              yield sendFuture
              self.onMessageSent("Sent ok")

              print("Write done. Reading...")
              msg = yield connection.read_message()
              print("Got msg.")
              self.onMessageReceived(msg)

              if msg is None:
                print("Connection lost")
                connection = None

            print("main loop completed")
        except Exception, e:
            print("ExceptionExceptionException")
            print(e)
            connection = None

        print("Exit main_loop function")

    def start(self):
        self.ioloop.run_sync(self.main_loop)
        print("Main loop completed")


######### TEST METHODS #########

def sendMessages(client):
    time.sleep(2)   #TEST only: wait for client startup
    while True:
        client.enqueueMessage("msgData", binary=False)
        time.sleep(1)  # <--- comment this line to break it

def testPrintMessage(msg):
    print("Received: " + str(msg))

def testPrintSentMessage(msg):
    print("Sent: " + msg)


if __name__=='__main__':

    from threading import Thread

    client = TornadoClient("ws://echo.websocket.org", testPrintMessage, testPrintSentMessage)

    thread = Thread(target = sendMessages, args = (client, ))
    thread.start()

    client.start()

1 个答案:

答案 0 :(得分:0)

这是一个很好的代码。我相信你需要在sendMessages线程中休眠的原因是因为它会一直尽可能快地调用enqueueMessage,每秒数百万次。由于enqueueMessage 等待处理排队的消息,因此它会尽可能快地调用IOLoop.add_callback,而不会给循环足够的机会来执行回调。< / p>

循环可能会在主线程上运行某些进度,因为您实际上并未阻止它。但是sendMessages线程添加回调比循环可以处理它们快得多。当循环从队列中弹出一条消息并开始处理它时,已经添加了数百万个新的回调,循环必须在它进入下一个消息处理阶段之前执行。

因此,对于您的测试代码,我认为在线程上调用enqueueMessage之间休眠是正确的。