在介紹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'}