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

123 строки
5.2KB

  1. import pickle
  2. import uuid
  3. try:
  4. import kombu
  5. except ImportError:
  6. kombu = None
  7. from .pubsub_manager import PubSubManager
  8. class KombuManager(PubSubManager): # pragma: no cover
  9. """Client manager that uses kombu for inter-process messaging.
  10. This class implements a client manager backend for event sharing across
  11. multiple processes, using RabbitMQ, Redis or any other messaging mechanism
  12. supported by `kombu <http://kombu.readthedocs.org/en/latest/>`_.
  13. To use a kombu backend, initialize the :class:`Server` instance as
  14. follows::
  15. url = 'amqp://user:password@hostname:port//'
  16. server = socketio.Server(client_manager=socketio.KombuManager(url))
  17. :param url: The connection URL for the backend messaging queue. Example
  18. connection URLs are ``'amqp://guest:guest@localhost:5672//'``
  19. and ``'redis://localhost:6379/'`` for RabbitMQ and Redis
  20. respectively. Consult the `kombu documentation
  21. <http://kombu.readthedocs.org/en/latest/userguide\
  22. /connections.html#urls>`_ for more on how to construct
  23. connection URLs.
  24. :param channel: The channel name on which the server sends and receives
  25. notifications. Must be the same in all the servers.
  26. :param write_only: If set ot ``True``, only initialize to emit events. The
  27. default of ``False`` initializes the class for emitting
  28. and receiving.
  29. :param connection_options: additional keyword arguments to be passed to
  30. ``kombu.Connection()``.
  31. :param exchange_options: additional keyword arguments to be passed to
  32. ``kombu.Exchange()``.
  33. :param queue_options: additional keyword arguments to be passed to
  34. ``kombu.Queue()``.
  35. :param producer_options: additional keyword arguments to be passed to
  36. ``kombu.Producer()``.
  37. """
  38. name = 'kombu'
  39. def __init__(self, url='amqp://guest:guest@localhost:5672//',
  40. channel='socketio', write_only=False, logger=None,
  41. connection_options=None, exchange_options=None,
  42. queue_options=None, producer_options=None):
  43. if kombu is None:
  44. raise RuntimeError('Kombu package is not installed '
  45. '(Run "pip install kombu" in your '
  46. 'virtualenv).')
  47. super(KombuManager, self).__init__(channel=channel,
  48. write_only=write_only,
  49. logger=logger)
  50. self.url = url
  51. self.connection_options = connection_options or {}
  52. self.exchange_options = exchange_options or {}
  53. self.queue_options = queue_options or {}
  54. self.producer_options = producer_options or {}
  55. self.producer = self._producer()
  56. def initialize(self):
  57. super(KombuManager, self).initialize()
  58. monkey_patched = True
  59. if self.server.async_mode == 'eventlet':
  60. from eventlet.patcher import is_monkey_patched
  61. monkey_patched = is_monkey_patched('socket')
  62. elif 'gevent' in self.server.async_mode:
  63. from gevent.monkey import is_module_patched
  64. monkey_patched = is_module_patched('socket')
  65. if not monkey_patched:
  66. raise RuntimeError(
  67. 'Kombu requires a monkey patched socket library to work '
  68. 'with ' + self.server.async_mode)
  69. def _connection(self):
  70. return kombu.Connection(self.url, **self.connection_options)
  71. def _exchange(self):
  72. options = {'type': 'fanout', 'durable': False}
  73. options.update(self.exchange_options)
  74. return kombu.Exchange(self.channel, **options)
  75. def _queue(self):
  76. queue_name = 'flask-socketio.' + str(uuid.uuid4())
  77. options = {'durable': False, 'queue_arguments': {'x-expires': 300000}}
  78. options.update(self.queue_options)
  79. return kombu.Queue(queue_name, self._exchange(), **options)
  80. def _producer(self):
  81. return self._connection().Producer(exchange=self._exchange(),
  82. **self.producer_options)
  83. def __error_callback(self, exception, interval):
  84. self._get_logger().exception('Sleeping {}s'.format(interval))
  85. def _publish(self, data):
  86. connection = self._connection()
  87. publish = connection.ensure(self.producer, self.producer.publish,
  88. errback=self.__error_callback)
  89. publish(pickle.dumps(data))
  90. def _listen(self):
  91. reader_queue = self._queue()
  92. while True:
  93. connection = self._connection().ensure_connection(
  94. errback=self.__error_callback)
  95. try:
  96. with connection.SimpleQueue(reader_queue) as queue:
  97. while True:
  98. message = queue.get(block=True)
  99. message.ack()
  100. yield message.payload
  101. except connection.connection_errors:
  102. self._get_logger().exception("Connection error "
  103. "while reading from queue")