将数据从服务器上运行的python线程推送到远程客户端

时间:2018-04-06 16:11:08

标签: python multithreading websocket

我正在尝试实现这个简单的工作流程:

  • python脚本运行守护程序线程。该线程使用多线程队列,并进行一些繁重的计算
  • 当一些数据准备就绪时,线程应该能够告诉它连接到互联网的另一个应用程序(在我的特定情况下,浏览器中的js应用程序)。

现在,这听起来很简单。但我完全坚持第二部分。我无法让我的线程在准备就绪时将数据推送到客户端。

我试过了:

  • ZeroRPC流。我设法流式传输数据,但是......我无法将计算数据传递给此流。

  • 的WebSockets。使用Python设置ws似乎需要高级技能......我设法设置了websocket服务器。它在一个线程中运行,并侦听客户端消息。 但我无法将此服务器传递给我的计算线程,以便它可以通过它发送数据。

以下是一些示例代码:

    def init_socket_server(self, host='localhost', port='8765'):

    # the socket handler
    async def hello(websocket, path):
        # that websocket object is what I need, but it lives in 
        # its own thread, I can't figure how to get it in other threads
        name = await websocket.recv()
        print("< {}".format(name))
        greeting = "Hello {}!".format(name)
        await websocket.send(greeting)
        print("> {}".format(greeting))

    # this object does not seem to be my actual server, just some init function?
    start_server = websockets.serve(hello, host, port)
    self.socket_server = start_server

    # creating the thread
    loop = asyncio.get_event_loop()
    def start():
        loop.run_until_complete(start_server)
        loop.run_forever()
    self.socket_thread = Thread(target=start, name="socket_server")
    self.socket_thread.start()

这很棘手,因为我需要服务器在自己的线程中运行,否则会阻塞我的整个代码(我必须为其他目的运行多个线程,所以它们都必须在后台)。

然而,似乎我的self.socket_server = start_server错了。 start_server似乎不是服务器本身,而是一些Serve对象。

必须发送数据并在另一个线程中运行的队列:

 # this lives in another thread
 def _consume_view_queue(self):
    while True:
        if not VIEW_REQUEST_QUEUE.empty():
            request = VIEW_REQUEST_QUEUE.get()
            if request:
                data = self.doSmth(request)
                # does not work as I expect
                # I do not seem to have the right object
                self.socket_server.send('DATA READY ' + view_id)
                VIEW_REQUEST_QUEUE.task_done()

失败了,因为我的self.socket_server听起来不对。这是错误:

AttributeError: 'Serve' object has no attribute 'send'

很抱歉,如果它是一个欺骗或noob问题,但我发现没有有用的资源(并且websockets / asyncio docs在这里并没有真正帮助......)。

编辑:我设法使用全局共享websocketServer对象。虽然不起作用,但我仍然有AttributeError: 'WebSocketServer' object has no attribute 'send'。我不明白为什么我的ws服务器没有send方法。

编辑2:所以你有更多的背景,我正在构建一个电子应用程序。电子应用程序是一个chrome客户端,类似于任何Web应用程序和本地节点服务器。这个应用程序还启动python进程进行计算。

到目前为止,Python和JS代码以“同步”方式与RPC调用进行通信:客户端请求数据,服务器接收请求并立即对其进行处理,然后回答请求。当计算量很大时,它是不好的,你不能按优先级排序它们。所以我实现了一个等待队列:计算请求堆叠在这个队列中。这样更健壮,可以很好地扩展,并允许按优先级对计算进行排序。

但是现在计算存在于一个独立的线程中,该线程运行一个消耗队列的无限循环。据我所知,这是消耗队列的常用模式。但我从未设法将这些数据传递回客户端。我希望这个计算线程能够将事件发送到我的JS客户端,我尝试了ZeroRPC流,我尝试了websockets但没有成功。我可以将数据存储在另一个队列中,以便我可以从我的websocket服务器所在的线程访问它们。但是,似乎你无法监听队列更改来触发事件,所以你需要一个无限循环来消耗它,因此另一个线程......我无法弄明白:)

2 个答案:

答案 0 :(得分:2)

通常,在事件&#34;浏览器中执行此类&#34;唤醒JS的阻力最小的路径是让JS向您的服务器发布XMLHttpRequest,并且具有 您的服务器保持挂起的请求(通常是GET,但可以根据您的REST设计使用任何HTTP操作)。当你有数据时,那么你 用数据完成该请求。客户端可以处理/显示它,然后发布新请求。

查看:http://en.wikipedia.org/wiki/Long_polling

你通常会想要一个&#34;代#34;请求中的计数器,通常是一个URL参数,如&#34; http://myserver.com/nextdata.json?gen=1234&#34;所以你的 服务器知道何时立即发送数据(例如,来自第0代客户端的第一个请求)或保持直到有新数据。

正如此处暗示的那样,通常您将数据编码为JSON,因为它是在REST / WWW世界中移动数据的一种非常简单的方法,并且Python具有 优秀的JSON解析模块。

答案 1 :(得分:0)

我最终找到了一个有效的工作流程。我的答案并不遥远,我的错误是尝试将套接字服务器从其线程中移出,并且我无法编写正确的协程。

相反,我使用了第二个队列来存储计算,我也写了正确的协程:

    async def send_data_to_client(websocket, path):
        while True:
            data = OUTPUT_QUEUE.get()
            await websocket.send(data)
            OUTPUT_QUEUE.task_done()
    start_server = websockets.serve(send_data_to_client, host, port)
    loop = asyncio.get_event_loop()
    def start_socket_server():
        loop.run_until_complete(start_server)
        loop.run_forever()
    self.socket_thread = Thread(target=start_socket_server, daemon=True, name="socket_server")
    self.socket_thread.start()

它遵循9000在他的评论中总结的工作流程。使用asyncio,我们甚至可以将此生成器循环组合到消息使用循环see the websockets doc