- Remove usage of % with logs, just let the underlying LOG do the interpolation for us if the log level passes. - Use LOG.exception instead of LOG.warn and then including the exception as a interpolated variable. Just let the LOG.exception output the exception trace and info. - Capture the failure object before doing other work using a new context manager that can yield back a failure object before further code is called (to avoid a context switch happening in eventlet or elsewhere that would clear the exception state). Change-Id: Ic1a504ba69c56ba226b1a067b5ade4590c245dd8
125 lines
4.8 KiB
Python
125 lines
4.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import kombu
|
|
import logging
|
|
import socket
|
|
import threading
|
|
|
|
from amqp import exceptions as amqp_exc
|
|
|
|
from taskflow.engines.worker_based import protocol as pr
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
# NOTE(skudriashev): A timeout of 1 is often used in environments where
|
|
# the socket can get "stuck", and is a best practice for Kombu consumers.
|
|
DRAIN_EVENTS_PERIOD = 1
|
|
|
|
|
|
class Proxy(object):
|
|
"""Proxy picks up messages from the named exchange, calls on_message
|
|
callback when new message received and is used to publish messages.
|
|
"""
|
|
|
|
def __init__(self, topic, exchange_name, on_message, on_wait=None,
|
|
**kwargs):
|
|
self._topic = topic
|
|
self._exchange_name = exchange_name
|
|
self._on_message = on_message
|
|
self._on_wait = on_wait
|
|
self._running = threading.Event()
|
|
self._url = kwargs.get('url')
|
|
self._transport = kwargs.get('transport')
|
|
self._transport_opts = kwargs.get('transport_options')
|
|
|
|
# create connection
|
|
self._conn = kombu.Connection(self._url, transport=self._transport,
|
|
transport_options=self._transport_opts)
|
|
|
|
# create exchange
|
|
self._exchange = kombu.Exchange(name=self._exchange_name,
|
|
channel=self._conn,
|
|
durable=False,
|
|
auto_delete=True)
|
|
|
|
@property
|
|
def is_running(self):
|
|
"""Return whether proxy is running."""
|
|
return self._running.is_set()
|
|
|
|
def _make_queue(self, name, exchange, **kwargs):
|
|
"""Make named queue for the given exchange."""
|
|
queue_arguments = {'x-expires': pr.QUEUE_EXPIRE_TIMEOUT * 1000}
|
|
return kombu.Queue(name="%s_%s" % (self._exchange_name, name),
|
|
exchange=exchange,
|
|
routing_key=name,
|
|
durable=False,
|
|
queue_arguments=queue_arguments,
|
|
**kwargs)
|
|
|
|
def publish(self, msg, task_uuid, routing_key, **kwargs):
|
|
"""Publish message to the named exchange with routing key."""
|
|
with kombu.producers[self._conn].acquire(block=True) as producer:
|
|
queue = self._make_queue(routing_key, self._exchange)
|
|
producer.publish(body=msg,
|
|
routing_key=routing_key,
|
|
exchange=self._exchange,
|
|
correlation_id=task_uuid,
|
|
declare=[queue],
|
|
**kwargs)
|
|
|
|
def start(self):
|
|
"""Start proxy."""
|
|
LOG.info("Starting to consume from the '%s' exchange.",
|
|
self._exchange_name)
|
|
with kombu.connections[self._conn].acquire(block=True) as conn:
|
|
queue = self._make_queue(self._topic, self._exchange, channel=conn)
|
|
try:
|
|
with conn.Consumer(queues=queue,
|
|
callbacks=[self._on_message]):
|
|
self._running.set()
|
|
while self.is_running:
|
|
try:
|
|
conn.drain_events(timeout=DRAIN_EVENTS_PERIOD)
|
|
except socket.timeout:
|
|
pass
|
|
if self._on_wait is not None:
|
|
self._on_wait()
|
|
finally:
|
|
try:
|
|
queue.delete(if_unused=True)
|
|
except (amqp_exc.PreconditionFailed, amqp_exc.NotFound):
|
|
pass
|
|
except Exception:
|
|
LOG.exception("Failed to delete the '%s' queue",
|
|
queue.name)
|
|
try:
|
|
self._exchange.delete(if_unused=True)
|
|
except (amqp_exc.PreconditionFailed, amqp_exc.NotFound):
|
|
pass
|
|
except Exception:
|
|
LOG.exception("Failed to delete the '%s' exchange",
|
|
self._exchange.name)
|
|
|
|
def wait(self):
|
|
"""Wait until proxy is started."""
|
|
self._running.wait()
|
|
|
|
def stop(self):
|
|
"""Stop proxy."""
|
|
self._running.clear()
|