我需要在一个线程中启动服务器,在另一个线程中启动值生产者(此处由 模拟mock_producer
),并且服务器的后台线程应该从队列中获取每个值并将其发送给客户端。同时,WSGI 服务器应该index.html
在请求时提供服务。这是迄今为止最好的尝试:
# pip install eventlet python-socketio
from threading import Thread
from Queue import Queue
import eventlet
import socketio
def mock_producer(queue):
import time
import itertools
for count in itertools.count():
queue.put(count)
time.sleep(5)
def background():
while True:
if not queue.empty():
value = queue.get()
sio.emit('value', value);
sio.sleep(0.1)
sio = socketio.Server(logger=True)
app = socketio.WSGIApp(sio, static_files={
'/': 'index.html',
})
queue = Queue()
prod_thread = Thread(target=mock_producer, args=(queue,))
prod_thread.start()
ws_server = eventlet.listen(('', 5000))
ws_thread = sio.start_background_task(background)
eventlet.wsgi.server(ws_server, app)
附带玩具index.html
:
<!doctype html>
<html>
<head>
<title>Test</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.slim.js"></script>
<script>
const socket = io.connect();
socket.on('value', value => console.log(value));
</script>
</head>
<body></body>
</html>
让我烦恼的是sio.sleep(0.1)
线路。这显然会在放入队列的对象和提供给客户端的对象之间引入延迟(无论多么小)。但这不起作用:
def background():
while True:
value = queue.get()
sio.emit('value', value);
原因是queue.get()
块,它不允许 WSGI 服务器为index.html
页面提供服务(这显然发生在同一个线程上)。
当我尝试为 queue.get-emit 循环启动一个新线程时(例如,使用Thread(target=background).start()
而不是sio.start_background_task(background)
),调试输出声称正在发生发射,但没有任何东西到达客户端,所以这也是一个失败。
理想情况下,我希望代码处于空闲状态,直到需要处理请求或队列有值,并立即对任何一个做出反应。
有没有办法干净地写这个?
注意:不幸的是,由于关键的依赖关系,这个项目停留在 Python 2 中。我相信唯一的后果就是这import Queue from Queue
条线,但以防万一。