You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
4.1 KiB
107 lines
4.1 KiB
import asyncio
|
|
import pickle
|
|
|
|
from socketio.asyncio_pubsub_manager import AsyncPubSubManager
|
|
|
|
try:
|
|
import aio_pika
|
|
except ImportError:
|
|
aio_pika = None
|
|
|
|
|
|
class AsyncAioPikaManager(AsyncPubSubManager): # pragma: no cover
|
|
"""Client manager that uses aio_pika for inter-process messaging under
|
|
asyncio.
|
|
|
|
This class implements a client manager backend for event sharing across
|
|
multiple processes, using RabbitMQ
|
|
|
|
To use a aio_pika backend, initialize the :class:`Server` instance as
|
|
follows::
|
|
|
|
url = 'amqp://user:password@hostname:port//'
|
|
server = socketio.Server(client_manager=socketio.AsyncAioPikaManager(
|
|
url))
|
|
|
|
:param url: The connection URL for the backend messaging queue. Example
|
|
connection URLs are ``'amqp://guest:guest@localhost:5672//'``
|
|
for RabbitMQ.
|
|
:param channel: The channel name on which the server sends and receives
|
|
notifications. Must be the same in all the servers.
|
|
With this manager, the channel name is the exchange name
|
|
in rabbitmq
|
|
:param write_only: If set to ``True``, only initialize to emit events. The
|
|
default of ``False`` initializes the class for emitting
|
|
and receiving.
|
|
"""
|
|
|
|
name = 'asyncaiopika'
|
|
|
|
def __init__(self, url='amqp://guest:guest@localhost:5672//',
|
|
channel='socketio', write_only=False, logger=None):
|
|
if aio_pika is None:
|
|
raise RuntimeError('aio_pika package is not installed '
|
|
'(Run "pip install aio_pika" in your '
|
|
'virtualenv).')
|
|
self.url = url
|
|
self.listener_connection = None
|
|
self.listener_channel = None
|
|
self.listener_queue = None
|
|
super().__init__(channel=channel, write_only=write_only, logger=logger)
|
|
|
|
async def _connection(self):
|
|
return await aio_pika.connect_robust(self.url)
|
|
|
|
async def _channel(self, connection):
|
|
return await connection.channel()
|
|
|
|
async def _exchange(self, channel):
|
|
return await channel.declare_exchange(self.channel,
|
|
aio_pika.ExchangeType.FANOUT)
|
|
|
|
async def _queue(self, channel, exchange):
|
|
queue = await channel.declare_queue(durable=False,
|
|
arguments={'x-expires': 300000})
|
|
await queue.bind(exchange)
|
|
return queue
|
|
|
|
async def _publish(self, data):
|
|
connection = await self._connection()
|
|
channel = await self._channel(connection)
|
|
exchange = await self._exchange(channel)
|
|
await exchange.publish(
|
|
aio_pika.Message(body=pickle.dumps(data),
|
|
delivery_mode=aio_pika.DeliveryMode.PERSISTENT),
|
|
routing_key='*'
|
|
)
|
|
|
|
async def _listen(self):
|
|
retry_sleep = 1
|
|
while True:
|
|
try:
|
|
if self.listener_connection is None:
|
|
self.listener_connection = await self._connection()
|
|
self.listener_channel = await self._channel(
|
|
self.listener_connection
|
|
)
|
|
await self.listener_channel.set_qos(prefetch_count=1)
|
|
exchange = await self._exchange(self.listener_channel)
|
|
self.listener_queue = await self._queue(
|
|
self.listener_channel, exchange
|
|
)
|
|
retry_sleep = 1
|
|
|
|
async with self.listener_queue.iterator() as queue_iter:
|
|
async for message in queue_iter:
|
|
async with message.process():
|
|
yield pickle.loads(message.body)
|
|
except Exception:
|
|
self._get_logger().error('Cannot receive from rabbitmq... '
|
|
'retrying in '
|
|
'{} secs'.format(retry_sleep))
|
|
self.listener_connection = None
|
|
await asyncio.sleep(retry_sleep)
|
|
retry_sleep *= 2
|
|
if retry_sleep > 60:
|
|
retry_sleep = 60
|