|
- import asyncio
- import pickle
- from urllib.parse import urlparse
-
- try:
- import aioredis
- except ImportError:
- aioredis = None
-
- from .asyncio_pubsub_manager import AsyncPubSubManager
-
-
- def _parse_redis_url(url):
- p = urlparse(url)
- if p.scheme not in {'redis', 'rediss'}:
- raise ValueError('Invalid redis url')
- ssl = p.scheme == 'rediss'
- host = p.hostname or 'localhost'
- port = p.port or 6379
- password = p.password
- if p.path:
- db = int(p.path[1:])
- else:
- db = 0
- return host, port, password, db, ssl
-
-
- class AsyncRedisManager(AsyncPubSubManager): # pragma: no cover
- """Redis based client manager for asyncio servers.
-
- This class implements a Redis backend for event sharing across multiple
- processes. Only kept here as one more example of how to build a custom
- backend, since the kombu backend is perfectly adequate to support a Redis
- message queue.
-
- To use a Redis backend, initialize the :class:`Server` instance as
- follows::
-
- server = socketio.Server(client_manager=socketio.AsyncRedisManager(
- 'redis://hostname:port/0'))
-
- :param url: The connection URL for the Redis server. For a default Redis
- store running on the same host, use ``redis://``. To use an
- SSL connection, use ``rediss://``.
- :param channel: The channel name on which the server sends and receives
- notifications. Must be the same in all the servers.
- :param write_only: If set ot ``True``, only initialize to emit events. The
- default of ``False`` initializes the class for emitting
- and receiving.
- """
- name = 'aioredis'
-
- def __init__(self, url='redis://localhost:6379/0', channel='socketio',
- write_only=False, logger=None):
- if aioredis is None:
- raise RuntimeError('Redis package is not installed '
- '(Run "pip install aioredis" in your '
- 'virtualenv).')
- (
- self.host, self.port, self.password, self.db, self.ssl
- ) = _parse_redis_url(url)
- self.pub = None
- self.sub = None
- super().__init__(channel=channel, write_only=write_only, logger=logger)
-
- async def _publish(self, data):
- retry = True
- while True:
- try:
- if self.pub is None:
- self.pub = await aioredis.create_redis(
- (self.host, self.port), db=self.db,
- password=self.password, ssl=self.ssl
- )
- return await self.pub.publish(self.channel,
- pickle.dumps(data))
- except (aioredis.RedisError, OSError):
- if retry:
- self._get_logger().error('Cannot publish to redis... '
- 'retrying')
- self.pub = None
- retry = False
- else:
- self._get_logger().error('Cannot publish to redis... '
- 'giving up')
- break
-
- async def _listen(self):
- retry_sleep = 1
- while True:
- try:
- if self.sub is None:
- self.sub = await aioredis.create_redis(
- (self.host, self.port), db=self.db,
- password=self.password, ssl=self.ssl
- )
- self.ch = (await self.sub.subscribe(self.channel))[0]
- return await self.ch.get()
- except (aioredis.RedisError, OSError):
- self._get_logger().error('Cannot receive from redis... '
- 'retrying in '
- '{} secs'.format(retry_sleep))
- self.sub = None
- await asyncio.sleep(retry_sleep)
- retry_sleep *= 2
- if retry_sleep > 60:
- retry_sleep = 60
|