基於py-amqp編寫生產者和消費者代碼

在介紹erlang的dbg調試RabbitMQ源碼之前,首先介紹基於py-amqp編寫RabbitMQ的生產者和消費者代碼,其中py-amqp的安裝包可在以下鏈接下載:https://pypi.org/project/amqp/

1 公共模塊

生產者和消費者一些公共代碼編寫在utils.py文件中,代碼如下:

import logging
import json
import os

class LOG(object):
    """
    LOG object is used to print logging informations.
    """
    def __init__(self, name=__name__):
        self.logger = logging.getLogger(name)
        self.logger.setLevel(level=logging.INFO)
        handler = logging.StreamHandler()
        handler.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s %(thread)d %(levelname)s %(name)s %(message)s')
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

    def info(self, msg):
        self.logger.info(msg)

    def debug(self, msg):
        self.logger.debug(msg)

    def warning(self, msg):
        self.logger.warning(msg)

    def error(self, msg):
        self.logger.error(msg)

def get_record_file():
    for item in os.listdir(os.getcwd()):
        if item.endswith('.json'):
            return os.path.join(os.getcwd(),item)

def read_record():
    file = get_record_file()
    with open(file, 'r') as f:
        record = json.load(f)
    return record

其中的RabbitMQ的實體(如queue和exchange)以及發送消息編寫在record.json文件中,即:

{
    "context": {
        "version": "1.0",
        "body": "RabbitMQ transport message"
    },
    "entities": {
        "exchange": "base_exchange",
        "exchange_type": "topic",
        "queue": "base_queue",
        "binding_key": "base.#",
        "routing_key": "base.test"
    }
}

2 生產者模塊

生產者代碼如下:

import sys
import amqp
import time
import utils
import traceback
import json

LOG = utils.LOG(__name__)

_ts = lambda: time.time()

def seed_msg(count, context):
    if count <= 0:
        LOG.error("Please specify a value greater than 0.")
        raise ValueError
    msg = amqp.Message(json.dumps(context))
    msg.properties['delivery_mode'] = 2
    for _ in range(count):
        yield msg


class Producer(object):
    def __init__(self, url="localhost:5672", name="guest",
                    password="guest"):
        self.url = url
        self.name = name
        self.password = password

    def __enter__(self):
        self.connect()
        return self

    def __exit__(self, *eargs):
        self.close()

    def connect(self):
        LOG.info("Connect to RabbitMQ server.")
        try:
            self.conn = amqp.Connection(host=self.url,
                                        userid=self.name,
                                        password=self.password,
                                        connect_timeout=5)
            self.chan = self.conn.channel()
        except Exception as exc:
            LOG.error("Connect host: {0} failed and raise exception: {1}".format(
                        self.url, exc))
            raise exc
        return self

    def close(self):
        LOG.info("Close connection with RabbitMQ server.")
        if hasattr(self, 'chan'):
            self.chan.close()
        if hasattr(self, 'conn'):
            self.conn.close()

    def declare(self, exchange, exchange_type, queue,
                binding_key, routing_key):
        self.exchange = exchange
        self.exchange_type = exchange_type
        self.queue = queue
        self.binding_key = binding_key
        self.routing_key = routing_key
        try:
            self.chan.exchange_declare(exchange=self.exchange,
                                       type=self.exchange_type,
                                       durable=False,
                                       auto_delete=False)
            self.chan.queue_declare(queue=self.queue,
                                    exclusive=False,
                                    durable=False,
                                    auto_delete=False)
            self.chan.queue_bind(queue=self.queue,
                                 exchange=self.exchange,
                                 routing_key=self.binding_key)
        except Exception as exc:
            LOG.error("Declare failed and raise exception: {0}".format(exc))
            raise exc

    def publish_message(self, msg):
        try:

            self.chan.basic_publish_confirm(msg, exchange=self.exchange,
                                            routing_key=self.routing_key)
        except Exception as exc:
            LOG.error("Publish message failed and raise exception: {0}".format(exc))
            raise exc

def parse_record():
    record = utils.read_record()
    context = record.get('context')
    entities = record.get('entities')
    return context, entities

def main(argv):
    url = raw_input("Please input a URL (<ip>:5672): ")
    name, password = "guest", "guest"
    interval = 2
    context, entities = parse_record()
    with Producer(url, name, password) as p:
        p.declare(exchange=entities['exchange'],
                  exchange_type=entities['exchange_type'],
                  queue=entities['queue'],
                  binding_key=entities['binding_key'],
                  routing_key=entities['routing_key'])
        for i, msg in enumerate(seed_msg(2, context), 1):
            start_time = _ts()
            LOG.info("Start to send {0} msg.".format(i))
            p.publish_message(msg)
            LOG.info("Finish to send {0} msg.".format(i))
            end_time = _ts()
            delay = end_time - start_time - interval
            time.sleep(-delay if delay < 0 else 0)


if __name__ == "__main__":
    main(sys.argv)

3 消費者模塊

消費者代碼如下:

import sys
import amqp
import time
import utils
import traceback
import socket
import signal

LOG = utils.LOG(__name__)


class Consumer(object):
    def __init__(self, url="localhost:5672", name="guest",
                    password="guest"):
        self.url = url
        self.name = name
        self.password = password

    def __enter__(self):
        self.connect()
        return self

    def __exit__(self, *eargs):
        self.close()

    def connect(self):
        LOG.info("Connect to RabbitMQ server.")
        try:
            self.conn = amqp.Connection(host=self.url,
                                        userid=self.name,
                                        password=self.password,
                                        connect_timeout=5)
            self.chan = self.conn.channel()
        except Exception as exc:
            LOG.error("Connect host: {0} failed and "
                        "raise exception: {1}".format(self.url, exc))
            raise exc
        return self

    def close(self):
        LOG.info("Close connection with RabbitMQ server.")
        if hasattr(self, 'chan'):
            self.chan.close()
        if hasattr(self, 'conn'):
            self.conn.close()

    def _callback(self, msg):
        LOG.info("Received msg: {0} properties: {1} delivery: {2}".format(
                    msg.body, msg.properties, msg.delivery_info))
        delivery_tag = msg.delivery_tag
        self.chan.basic_ack(delivery_tag)

    def declare(self, exchange, exchange_type, queue,
                binding_key, routing_key):
        self.exchange = exchange
        self.exchange_type = exchange_type
        self.queue = queue
        self.binding_key = binding_key
        self.routing_key = routing_key
        try:
            self.chan.exchange_declare(exchange=self.exchange,
                                       type=self.exchange_type,
                                       durable=False,
                                       auto_delete=False)
            self.chan.queue_declare(queue=self.queue,
                                    exclusive=False,
                                    durable=False,
                                    auto_delete=False)
            self.chan.queue_bind(queue=self.queue,
                                 exchange=self.exchange,
                                 routing_key=self.binding_key)
            self.chan.basic_consume(queue=self.queue,
                                    no_ack=False,
                                    callback=self._callback,
                                    consumer_tag="tag_{0}".format(queue))
        except Exception as exc:
            LOG.error("Declare failed and raise exception: {0}".format(exc))
            raise exc

    def start_consuming(self):
        while not self._exit:
            wait_method = amqp.spec.Channel.CloseOk
            try:
                self.chan.wait(wait_method, timeout=1)
            except socket.timeout:
                pass
            except Exception as exc:
                LOG.error("Consume stop and raise exception: {0}".format(exc))
                raise exc

    def signal_handler(self, signum, frame):
        LOG.info("Received signal: {} and exit".format(signum))
        self._exit = True

    def setup_signal(self):
        self._exit = False
        signal.signal(signal.SIGHUP, self.signal_handler)
        signal.signal(signal.SIGINT, self.signal_handler)
        signal.signal(signal.SIGQUIT, self.signal_handler)
        signal.signal(signal.SIGALRM, self.signal_handler)
        signal.signal(signal.SIGTERM, self.signal_handler)
        signal.signal(signal.SIGCONT, self.signal_handler)

def parse_record():
    record = utils.read_record()
    entities = record.get('entities')
    return entities

def main(argv):
    url = raw_input("Please input a URL (<ip>:5672): ")
    name, password = "guest", "guest"
    entities = parse_record()
    with Consumer(url, name, password) as c:
        c.declare(exchange=entities['exchange'],
                  exchange_type=entities['exchange_type'],
                  queue=entities['queue'],
                  binding_key=entities['binding_key'],
                  routing_key=entities['routing_key'])
        c.setup_signal()
        c.start_consuming()


if __name__ == "__main__":
    main(sys.argv)

4 運行

[root@master code]# python producer.py
Please input a URL (<ip>:5672): 192.168.1.100
2019-10-06 10:44:20,915 140120100865856 INFO __main__ Connect to RabbitMQ server.
/usr/lib/python2.7/site-packages/amqp-2.5.2-py2.7.egg/amqp/connection.py:325: AMQPDeprecationWarning: The .frame_writer attribute on the connection was accessed before
the connection was established.  This is supported for now, but will
be deprecated in amqp 2.2.0.

Since amqp 2.0 you have to explicitly call Connection.connect()
before using the connection.

  W_FORCE_CONNECT.format(attr=attr)))
2019-10-06 10:44:20,933 140120100865856 INFO __main__ Start to send 1 msg.
2019-10-06 10:44:20,935 140120100865856 INFO __main__ Finish to send 1 msg.
2019-10-06 10:44:22,936 140120100865856 INFO __main__ Start to send 2 msg.
2019-10-06 10:44:22,938 140120100865856 INFO __main__ Finish to send 2 msg.
2019-10-06 10:44:24,949 140120100865856 INFO __main__ Close connection with RabbitMQ server.
[root@master code]# python consumer.py
Please input a URL (<ip>:5672): 192.168.1.100
2019-10-06 10:44:08,268 140428916774720 INFO __main__ Connect to RabbitMQ server.
/usr/lib/python2.7/site-packages/amqp-2.5.2-py2.7.egg/amqp/connection.py:325: AMQPDeprecationWarning: The .frame_writer attribute on the connection was accessed before
the connection was established.  This is supported for now, but will
be deprecated in amqp 2.2.0.

Since amqp 2.0 you have to explicitly call Connection.connect()
before using the connection.

  W_FORCE_CONNECT.format(attr=attr)))

2019-10-06 10:44:20,936 140428916774720 INFO __main__ Received msg: {"body": "RabbitMQ transport message", "version": "1.0"} properties: {u'delivery_mode': 2} delivery: {u'consumer_tag': u'tag_base_queue', u'redelivered': False, u'routing_key': u'base.test', u'delivery_tag': 1, u'exchange': u'base_exchange'}
2019-10-06 10:44:22,938 140428916774720 INFO __main__ Received msg: {"body": "RabbitMQ transport message", "version": "1.0"} properties: {u'delivery_mode': 2} delivery: {u'consumer_tag': u'tag_base_queue', u'redelivered': False, u'routing_key': u'base.test', u'delivery_tag': 2, u'exchange': u'base_exchange'}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章