Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

108 строки
4.0KB

  1. import asyncio
  2. import pickle
  3. from urllib.parse import urlparse
  4. try:
  5. import aioredis
  6. except ImportError:
  7. aioredis = None
  8. from .asyncio_pubsub_manager import AsyncPubSubManager
  9. def _parse_redis_url(url):
  10. p = urlparse(url)
  11. if p.scheme not in {'redis', 'rediss'}:
  12. raise ValueError('Invalid redis url')
  13. ssl = p.scheme == 'rediss'
  14. host = p.hostname or 'localhost'
  15. port = p.port or 6379
  16. password = p.password
  17. if p.path:
  18. db = int(p.path[1:])
  19. else:
  20. db = 0
  21. return host, port, password, db, ssl
  22. class AsyncRedisManager(AsyncPubSubManager): # pragma: no cover
  23. """Redis based client manager for asyncio servers.
  24. This class implements a Redis backend for event sharing across multiple
  25. processes. Only kept here as one more example of how to build a custom
  26. backend, since the kombu backend is perfectly adequate to support a Redis
  27. message queue.
  28. To use a Redis backend, initialize the :class:`Server` instance as
  29. follows::
  30. server = socketio.Server(client_manager=socketio.AsyncRedisManager(
  31. 'redis://hostname:port/0'))
  32. :param url: The connection URL for the Redis server. For a default Redis
  33. store running on the same host, use ``redis://``. To use an
  34. SSL connection, use ``rediss://``.
  35. :param channel: The channel name on which the server sends and receives
  36. notifications. Must be the same in all the servers.
  37. :param write_only: If set ot ``True``, only initialize to emit events. The
  38. default of ``False`` initializes the class for emitting
  39. and receiving.
  40. """
  41. name = 'aioredis'
  42. def __init__(self, url='redis://localhost:6379/0', channel='socketio',
  43. write_only=False, logger=None):
  44. if aioredis is None:
  45. raise RuntimeError('Redis package is not installed '
  46. '(Run "pip install aioredis" in your '
  47. 'virtualenv).')
  48. (
  49. self.host, self.port, self.password, self.db, self.ssl
  50. ) = _parse_redis_url(url)
  51. self.pub = None
  52. self.sub = None
  53. super().__init__(channel=channel, write_only=write_only, logger=logger)
  54. async def _publish(self, data):
  55. retry = True
  56. while True:
  57. try:
  58. if self.pub is None:
  59. self.pub = await aioredis.create_redis(
  60. (self.host, self.port), db=self.db,
  61. password=self.password, ssl=self.ssl
  62. )
  63. return await self.pub.publish(self.channel,
  64. pickle.dumps(data))
  65. except (aioredis.RedisError, OSError):
  66. if retry:
  67. self._get_logger().error('Cannot publish to redis... '
  68. 'retrying')
  69. self.pub = None
  70. retry = False
  71. else:
  72. self._get_logger().error('Cannot publish to redis... '
  73. 'giving up')
  74. break
  75. async def _listen(self):
  76. retry_sleep = 1
  77. while True:
  78. try:
  79. if self.sub is None:
  80. self.sub = await aioredis.create_redis(
  81. (self.host, self.port), db=self.db,
  82. password=self.password, ssl=self.ssl
  83. )
  84. self.ch = (await self.sub.subscribe(self.channel))[0]
  85. return await self.ch.get()
  86. except (aioredis.RedisError, OSError):
  87. self._get_logger().error('Cannot receive from redis... '
  88. 'retrying in '
  89. '{} secs'.format(retry_sleep))
  90. self.sub = None
  91. await asyncio.sleep(retry_sleep)
  92. retry_sleep *= 2
  93. if retry_sleep > 60:
  94. retry_sleep = 60