class MessageHelper(object):
  _output_channel = None
  _pending = []
  _channel_in = None
  _channel_out = None
  _last_time_called = 0
  _min_interval = 0.5
  _later = None
  _shutdown = False

  def __init__(self, configuration, sender_ready, receiver_ready, message_received):
    """
      Kicks off configuration and connections.
      sender_ready will be called when the sender is ready, and message_received will
      be called with the plaintext body of a message.
    """

    self._configuration = configuration

    self.sender_ready = sender_ready
    self.receiver_ready = receiver_ready
    self.message_received = message_received

    self._connect()

  def _connect(self):
    spec_path = os.path.dirname(__file__) + "/amqp0-8.stripped.rabbitmq.xml"
    spec = txamqp.spec.load(spec_path)
    self.delegate = TwistedDelegate()

    d = ClientCreator(reactor, AMQClient, delegate=self.delegate, vhost=self._configuration["vhost"], spec=spec).connectTCP(self._configuration["host"], self._configuration["port"])

    d.addCallback(self._connected)

    def whoops(err):
      if reactor.running:
        log.err(err)
        self.shutdown()
        reactor.stop()

    d.addErrback(whoops)

  def shutdown(self):
    self._shutdown = True

    if self._later:
      self._later.reset()

    self.delegate.connection_close(None, "Shutting down")

  @inlineCallbacks
  def _connected(self, connection):
    self._connection = connection

    yield self._connection.authenticate(self._configuration["username"], self._configuration["password"])

    yield self._set_up_sender()
    yield self._set_up_receiver()

  @inlineCallbacks
  def _set_up_sender(self):
    self._channel_out = yield self._connection.channel(1)
    yield self._channel_out.channel_open()

    yield self._channel_out.exchange_declare(
      exchange=self._configuration["outgoing"]["exchange"],
      type=self._configuration["outgoing"]["exchange_type"],
      durable=self._configuration["outgoing"]["durable"],
      auto_delete=False
    )

    yield self._sender_ready(self._channel_out)

  @inlineCallbacks
  def _set_up_receiver(self):
    self._channel_in = yield self._connection.channel(2)
    yield self._channel_in.channel_open()
    yield self._channel_in.basic_qos(prefetch_count=self._configuration["incoming"]["prefetch_count"])

    yield self._channel_in.queue_declare(
      queue=self._configuration["incoming"]["queue"],
      durable=self._configuration["incoming"]["durable"],
      exclusive=self._configuration["incoming"]["exclusive"],
      auto_delete=False
    )

    yield self._channel_in.exchange_declare(
      exchange=self._configuration["incoming"]["exchange"],
      type=self._configuration["incoming"]["exchange_type"],
      durable=self._configuration["incoming"]["durable"],
      auto_delete=False
    )

    yield self._channel_in.queue_bind(
      queue=self._configuration["incoming"]["queue"],
      exchange=self._configuration["incoming"]["exchange"],
      routing_key=self._configuration["incoming"]["routing_key"]
    )

    yield self._channel_in.basic_consume(
      queue=self._configuration["incoming"]["queue"],
      no_ack=False,
      consumer_tag=self._configuration["incoming"]["routing_key"]
    )

    self._queue_in = yield self._connection.queue(self._configuration["incoming"]["routing_key"])
    self._receiveMessages()
    self.receiver_ready()

  def _ack(self, raw_message):
    self._channel_in.basic_ack(delivery_tag=raw_message.delivery_tag)

  @inlineCallbacks
  def _receiveMessages(self):
    elapsed = time.time() - self._last_time_called
    left_to_wait = self._min_interval - elapsed

    if self._shutdown:
      return

    if left_to_wait > 0:
      self._later = self.callLater(left_to_wait, self._receiveMessages)
    else:
      self._last_time_called = time.time()

      message = yield self._queue_in.get()
      self._message_received(message)

      elapsed = time.time() - self._last_time_called
      left_to_wait = self._min_interval - elapsed

      if left_to_wait < 0:
        left_to_wait = 0

      self._later = self.callLater(left_to_wait*1.01, self._receiveMessages)

  def _send_queued_messages(self):
    if self._channel_out:
      while self._pending:
        message = Content(json.dumps(self._pending.pop()))

        self._channel_out.basic_publish(
          exchange=self._configuration["outgoing"]["exchange"],
          routing_key=self._configuration["outgoing"]["routing_key"],
          content=message
        )

  def _sender_ready(self, output_channel):
    self._send_queued_messages()
    self.sender_ready()

  def _message_received(self, message):
    self._ack(message)

    body = json.loads(message.content.body)

    self.message_received(body)

  def callLater(self, *args, **kwargs):
    return reactor.callLater(*args, **kwargs)

  def send(self, message):
    self._pending.append(message)
    self._send_queued_messages()

  def sender_ready(self):
    raise Exception("message_received in message_helper has not been set")

  def message_received(self, message):
    raise Exception("message_received in message_helper has not been set")
Exemple #2
0
 def connection_close(self, ch, msg):
     TwistedDelegate.connection_close(self, ch, msg)
     self._fireDisconnected(msg)