I am trying to develop an application consisting of a Server and a bunch of Clients that should work like this:
- each Client itself will listen to an independent websocket consisting of some information
- they will then process this information and redirect the processed message to the Server
- the Server will do some processing (send some information trhough another websocket and wait for response)
- once the Server gets the response, it will redirect it to the Client
Since it consists of asynchronous responses and communication between classes my first idea was to use the combo RxPy + asyncio. Here is a toy model of my problema that I tryied to develop:
Here is the Server:
import reactivex as rx
import random
import reactivex.operators as ops
class Server:
def __init__(self, clients):
self.clients = clients
self.stream = rx.Subject()
def info(self, msg):
print(msg)
async def start(self, loop):
done = asyncio.Future()
def on_completed():
done.set_result(True)
async def react(item):
msg = {'alias': 'server', 'action': 'received', 'from': item['alias'], 'request': item['request']}
self.info(msg)
await asyncio.sleep(random.randint(1,3))
response = {'alias': 'server', 'action': 'send', 'request': msg['request'] ** 2}
self.info(response)
self.stream.on_next(response)
client_listener = rx.empty()
for client in self.clients:
client_listener= rx.merge(client_listener, client.stream(self.stream, loop))
client_listener.subscribe(
on_next = lambda x: react(x),
on_error = print,
on_completed = print('completed server')
)
await done
Here is the Client:
import reactivex.operators as ops
class Client:
def __init__(self, alias):
self.alias = alias
def info(self, msg):
print(msg)
def stream(self, controller, loop):
def on_subscribe(observer, scheduler):
for i in range(10):
msg = {'alias': self.alias, 'action': 'send', 'request': i}
self.info(msg)
observer.on_next(msg)
loop.call_soon(observer.on_completed())
controller.subscribe(
on_next = lambda msg: self.info({'alias': self.alias, 'action': 'received', 'from': msg['alias'], 'request': msg['request']})
)
return rx.create(on_subscribe)
And here is how I am trying to run it:
import asyncio
loop = asyncio.get_event_loop()
client_1 = Client('client_1')
client_2 = Client('client_2')
loop.run_until_complete(Server([client_1, client_2]).start(loop=loop))
loop.close()
When I try executing the above code I get the followin:
RuntimeError Traceback (most recent call last)
Cell In[3], line 8
5 client_1 = Client('client_1')
6 client_2 = Client('client_2')
----> 8 loop.run_until_complete(Server([client_1, client_2]).start(loop=loop))
9 loop.close()
File ~\Anaconda3\envs\nfs_hom\lib\asyncio\base_events.py:625, in BaseEventLoop.run_until_complete(self, future)
614 """Run until the Future is done.
615
616 If the argument is a coroutine, it is wrapped in a Task.
(...)
622 Return the Future's result, or raise its exception.
623 """
624 self._check_closed()
--> 625 self._check_running()
627 new_task = not futures.isfuture(future)
628 future = tasks.ensure_future(future, loop=self)
File ~\Anaconda3\envs\nfs_hom\lib\asyncio\base_events.py:584, in BaseEventLoop._check_running(self)
582 def _check_running(self):
583 if self.is_running():
--> 584 raise RuntimeError('This event loop is already running')
585 if events._get_running_loop() is not None:
586 raise RuntimeError(
587 'Cannot run the event loop while another loop is running')
RuntimeError: This event loop is already running
Could you help me understand where I am getting it wrong and/or where I should change my approach for the problem?