Python Tornado websocket客户端丢失响应消息?
我需要处理来自网络摄像头的帧,并将一些选定的帧发送到远程websocket服务器。服务器会立即回复一条确认消息(很像echo服务器)。 帧处理速度慢且cpu密集,因此我希望使用单独的线程池(生产者)来使用所有可用的内核。因此,客户机(消费者)在池有东西要发送之前一直处于空闲状态。 我当前的实现(见下文)只有在producer测试循环中添加一个小的sleep时才能正常工作。如果我删除此延迟,我将停止从服务器(echo服务器和我的真实服务器)接收任何应答。连第一个答案都没有了,所以我不认为这是一个防洪机制。 我做错了什么Python Tornado websocket客户端丢失响应消息?,python,websocket,tornado,Python,Websocket,Tornado,我需要处理来自网络摄像头的帧,并将一些选定的帧发送到远程websocket服务器。服务器会立即回复一条确认消息(很像echo服务器)。 帧处理速度慢且cpu密集,因此我希望使用单独的线程池(生产者)来使用所有可用的内核。因此,客户机(消费者)在池有东西要发送之前一直处于空闲状态。 我当前的实现(见下文)只有在producer测试循环中添加一个小的sleep时才能正常工作。如果我删除此延迟,我将停止从服务器(echo服务器和我的真实服务器)接收任何应答。连第一个答案都没有了,所以我不认为这是一个防
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()
这是一个简洁的代码。我相信在
sendmages
线程中需要睡眠的原因是,否则,它会以每秒数百万次的速度不断调用enqueueMessage
。由于enqueueMessage
不会等待处理已排队的消息,因此它会继续调用IOLoop.add_callback
尽可能快,而不会给循环足够的机会执行回调
循环可能会在主线程上运行一些,因为您实际上并没有阻止它。但是sendMessages
线程添加回调的速度比循环处理回调的速度快得多。当循环从队列中弹出一条消息并开始处理它时,已经添加了数以百万计的新回调,循环必须先执行这些回调,然后才能进入消息处理的下一阶段
因此,对于您的测试代码,我认为在线程上调用
enqueueMessage
之间休眠是正确的。我相信在sendmages
线程中需要睡眠的原因是,否则,它会以每秒数百万次的速度不断调用enqueueMessage
。由于enqueueMessage
不会等待处理已排队的消息,因此它会继续调用IOLoop.add_callback
尽可能快,而不会给循环足够的机会执行回调
循环可能会在主线程上运行一些,因为您实际上并没有阻止它。但是sendMessages
线程添加回调的速度比循环处理回调的速度快得多。当循环从队列中弹出一条消息并开始处理它时,已经添加了数以百万计的新回调,循环必须先执行这些回调,然后才能进入消息处理的下一阶段
因此,对于您的测试代码,我认为在线程上调用
enqueueMessage
之间休眠是正确的。好的,我看到了。这个最小的例子有点极端:)但为什么我在实际代码中看到了相同的行为?我将在我的问题中添加一些额外的信息。好的,我看到了。这个最小的例子有点极端:)但为什么我在实际代码中看到了相同的行为?我将在我的问题中添加一些额外的信息。
2017-05-13 18:59:54+0200 [-] TX Frame to tcp4:192.168.0.5:48964 : fin = True, rsv = 0, opcode = 1, mask = -, length = 21, repeat_length = None, chopsize = None, sync = False, payload = {"type": "PROCESSED"}
2017-05-13 18:59:54+0200 [-] TX Octets to tcp4:192.168.0.5:48964 : sync = False, octets = 81157b2274797065223a202250524f434553534544227d