0

我需要在一个线程中启动服务器,在另一个线程中启动值生产者(此处由 模拟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条线,但以防万一。

4

1 回答 1

2

Eventlet 使用协作多任务处理。每当您使用标准库中的潜在阻塞函数(例如线程、同步或套接字中的函数)时,您就有可能阻塞整个服务器。

Eventlet 提供了库中大多数阻塞函数的替代版本,因此您应该使用它们来避免此类问题。切换到对 eventlet 友好的函数的最简单方法是对标准库进行猴子补丁

于 2019-12-03T07:08:05.453 回答