How to start websocket server in thread using python?

Viewed 248

I want to start websocket server in separate thread. I have tried to implement as below but getting Runtime error as it says attached to different loop

Code:

#!/usr/bin/env python

# WS server example

import asyncio
import websockets
import threading
import time

async def hello(websocket, path):
    name = await websocket.recv()
    print(name)

    greeting = "Hello " + name +"!"

    await websocket.send(greeting)
    print(greeting)

start_server = websockets.serve(hello, "localhost", 8765)
eventLoop = asyncio.new_event_loop()
time.sleep(2)

def startWebSocket(loop, server):
    print("WS: thread started")
    asyncio.set_event_loop(eventLoop)
    asyncio.get_event_loop().run_until_complete(start_server)
    eventLoop.run_forever()

print("Run web socket in threaded env")
TH = threading.Thread(target=startWebSocket, args=[eventLoop, start_server,])
TH.start()

# then do some other work after this

Output:

Run web socket in threaded env
WS: thread started
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.5/threading.py", line 862, in run
    self._target(*self._args, **self._kwargs)
  File "threadWithWs.py", line 26, in startWebSocket
    asyncio.get_event_loop().run_until_complete(start_server)
  File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete
    return future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/usr/lib/python3.5/asyncio/tasks.py", line 564, in _wrap_awaitable
    return (yield from awaitable.__await__())
  File "/home/krunal/.local/lib/python3.5/site-packages/websockets/py35/server.py", line 13, in __await_impl__
    server = await self._creating_server
  File "/usr/lib/python3.5/asyncio/base_events.py", line 923, in create_server
    infos = yield from tasks.gather(*fs, loop=self)
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
RuntimeError: Task <Task pending coro=<_wrap_awaitable() running at /usr/lib/python3.5/asyncio/tasks.py:564> cb=[_run_until_complete_cb() at /usr/lib/python3.5/asyncio/base_events.py:164]> got Future <_GatheringFuture pending> attached to a different loop

Code exists with this error.

How to setup loop for websocket to start websocket server in thread?

I have followed this answer but no luck.

1 Answers

You don't have to use threads with asyncio. It's redundant.

Do something like this (I actually don't know what websocket is, I assume start_server is a coroutine \ an awaitable). Create another async function, deputed to keep the loop alive. Before going into a while loop, spawn your task.

You may want to add an health check, using a global variable or wrapping all into a class.

async def run():
   eventLoop.create_task(start_server())
   while 1:
      # if not health_check():
      #     exit()
      await asyncio.sleep(1)

eventLoop.run_until_complete(run())

Do the other stuff by spawning more tasks.

To run blocking code into an asyncio loop, use eventLoop.run_in_executor(None, blocking_code): it's basically a friendly interface to threads.

As with threads, you live into the GIL.

Embrace the way asyncio do things.

Related