rabbitmq实现过程及其代码详解

首先先上一个rabbitmq的工作示意图,这种情况是rabbitmq最简单的一种通信,之后有时间会上多exchange之间的消息交互的文章,也欢迎大家一起学习,一起讨论。

rabbitmq实现过程及其代码详解_第1张图片

代码实现:

客户端client1(发送端)

import uuid
from time import time, sleep
import traceback
import pika

import threading


class Rpc_cilent(object):
    _queue = {}
    _internal_lock = threading.RLock()

    def __init__(self, id, host="127.0.0.1", port=5672, username="guest", password="guest", v_host="/"):
        '''交换机,可认为是网络交换机,只要连接在同一个交换机上的队列都可以进行通信'''
        self._exchange_name = "wu_queue"
        self._callback_queue_name = "rpc_client_{}".format(id)  # 当前客户端与交换机连接的队列
        self._routing_key = "server_rpc_{}".format(id)  # 当前客户端的地址(唯一标识)

        self._broker_url = "amqp://{}:{}@{}:{}{}".format(username, password, host, port, v_host)
        self._param = pika.URLParameters(self._broker_url)

        self._thread = threading.Thread(target=self._run)
        self._thread.setDaemon(False)
        self._thread.start()

    def _run(self):
        print("已运行")
        try:
            self.__connect()
            self.__process_data_event()

        except Exception as e:
            sleep(5)
            print("_run connection exception : {}".format(traceback.format_exc()))

    def __connect(self):

        # 创建rabbitmq连接对象
        self._connection = pika.BlockingConnection(parameters=self._param)
        # print("创建rabbitmq对象")
        # 创建信道
        self._channel = self._connection.channel()
        # 声明或创建交换机
        # exchange:交换机的名称
        # exchange_type:交换机的类型
        # durbale:队列是否持久化
        # auto_delete:连接断开时,是否自动删除exchange
        self._channel.exchange_declare(exchange=self._exchange_name, exchange_type="topic", durable=True,
                                       auto_delete=False)
        # 声明创建队列
        # queue:列队名称
        # exclusive:是否排外,True的话,当队列关闭后会自动删除,一个列队只能有一个消费者去消费
        self._channel.queue_declare(queue=self._callback_queue_name, exclusive=False, auto_delete=True)
        # 将列队和交换机通过routing_key绑定
        self._channel.queue_bind(queue=self._callback_queue_name, exchange=self._exchange_name,
                                 routing_key=self._routing_key)

    def __process_data_event(self):
        # no_ack:开启自动确认,否则处理后的消息会一直留在队列中
        self._channel.basic_consume(self.__on_response, no_ack=True, queue=self._callback_queue_name)
        while True:
            '''以下方法是阻塞式的监听列队收到的消息,若有消息则去消费'''
            with self._internal_lock:
                self._connection.process_data_events()
                sleep(0.1)

    '''接收返回信息的接口,参数是固定的,将接收的信息存入到字典中'''
    def __on_response(self, ch, method, props, body):
        self._queue[props.correlation_id] = body
        print("收到的返回消息为:", body)

    def __send_message(self, toAddress, body_msg, notify=False):
        # 生成消息的id(唯一标识)
        correlation_id = str(uuid.uuid4())

        props = pika.BasicProperties(content_type="application/json")

        if not notify:
            self._queue[correlation_id] = None
            props.reply_to = self._routing_key  # 指定回调地址
            props.correlation_id = correlation_id  # 指定发送消息的id

        with self._internal_lock:
            # 发送消息
            self._channel.basic_publish(exchange=self._exchange_name,
                                        routing_key="worker.{}".format(toAddress),
                                        properties=props,
                                        body=str(body_msg))
            print("发送消息成功!")
        return correlation_id

    def call(self, toAddress, message, timeout=10.0, notify=False):
        '''
        调用发送接口,并获取返回结果
        :param toAddress: 消息发送地址
        :param message: 发送的消息
        :param timeout: 等待返回的超时时间
        :param notify: 是否等待返回消息标识符
        :return: 返回结果
        '''

        def current_millmi_time():
            return int(time())

        # toAddress,message 必须为字符串类型
        for p in (toAddress, message):
            if not (isinstance(p, str)):
                raise TypeError
        begin = current_millmi_time()
        body = {"msg": message}
        print("body: ", body)
        correlation_id = self.__send_message(toAddress, body, notify)
        print("corr_id: ", correlation_id)
        if notify:
            '''仅发送消息,无需等待返回消息'''
            return True
        # 判断消息是否返回,做超时处理
        while self._queue[correlation_id] is None:
            if current_millmi_time() - begin > int(timeout):
                raise RuntimeError
        print("返回值为:", self._queue[correlation_id])
        return self._queue.pop(correlation_id)

if __name__ == '__main__':
    rpc_client = Rpc_cilent("wu”)

客户端client2(接收端):

from time import time, sleep
import traceback
import pika

import threading


class Rpc_worker(object):
    _queue = {}
    _internal_lock = threading.RLock()

    def __init__(self, id, host="127.0.0.1", port=5672, username="guest", password="guest", v_host="/"):
        self._exchange_name = "wu_queue"
        self._callback_queue_name = "rpc_worker_{}".format(id)      #定义该客户端与交换机交互的队列
        self._routing_key = "worker.{}".format(id)      #该客户端的地址(唯一标识)

        self._broker_url = "amqp://{}:{}@{}:{}{}".format(username, password, host, port, v_host)
        self._param = pika.URLParameters(self._broker_url)

        self._thread = threading.Thread(target=self._run)
        self._thread.setDaemon(False)
        self._thread.start()

    def _run(self):
        print("已运行")
        # while True:
        try:
            self.__connect()
            '''监听队列的方法必须开启子线程执行,否则会阻塞主线程'''
            self.__process_data_event()

        except Exception as e:
            print("_run connection exception : {}".format(traceback.format_exc()))
            sleep(5)

    def __connect(self):

        # 创建rabbitmq连接对象
        self._connection = pika.BlockingConnection(parameters=self._param)
        # print("创建rabbitmq对象")
        # 创建信道
        self._channel = self._connection.channel()
        # 声明或创建交换机
        # exchange:交换机的名称
        # exchange_type:交换机的类型
        # durbale:队列是否持久化
        # auto_delete:连接断开时,是否自动删除exchange
        self._channel.exchange_declare(exchange=self._exchange_name, exchange_type="topic", durable=True,
                                       auto_delete=False)
        # 声明创建队列
        # queue:列队名称
        # exclusive:是否排外,True的话,当队列关闭后会自动删除,一个列队只能有一个消费者去消费
        self._channel.queue_declare(queue=self._callback_queue_name, exclusive=False, auto_delete=True)
        # 将列队和交换机通过routing_key绑定
        self._channel.queue_bind(queue=self._callback_queue_name, exchange=self._exchange_name,
                                 routing_key=self._routing_key)

    def __process_data_event(self):
        # no_ack:开启自动确认,否则处理后的消息会一直留在队列中
        self._channel.basic_consume(self.__on_response, no_ack=True, queue=self._callback_queue_name)
        while True:
            with self._internal_lock:
                '''以下方法是阻塞式的监听列队收到的消息,若有消息则去消费'''
                self._connection.process_data_events()
                sleep(0.1)

    #接收信息的接口,参数不可变
    def __on_response(self, ch, method, props, body):
        self._queue[props.correlation_id] = body
        print("收到的返回消息为:", body)
        response = "worker incept message,thanks!!!"
        self.__send_message(props, response)
        print("reply_to: %s" % props.reply_to)

    def __send_message(self, props, response):
        properties = pika.BasicProperties(content_type="application/json",
                                          correlation_id=props.correlation_id)  # 消息的唯一标识

        with self._internal_lock:
            # 发送消息
            self._channel.basic_publish(self._exchange_name,
                                        props.reply_to,  # 目的地的地址
                                        properties=properties,
                                        body=str(response))
        print("消息已发送!")

if __name__ == '__main__':
    rpc_client = Rpc_worker("wu”)

执行消息发送命令:

import time

from emit_log_topic import rpc_client as rpc        #emit_log_topic:客户端client1(发送端)的代码脚本名

n=1
while True:
    try:
        id=rpc.call("wu","hello rabbitmq %s" % n)
        print("corr_id: ",id)
        time.sleep(2)
        print("发送次数:",n)
        n+=1
    except Exception as e:
        print("error: ",e)

执行过程:

1.首先安装rabbitmq服务,针对不同的系统去网上搜索相应的安装教程
2.启动rebbitmq服务,通过web页面访问rabbitmq控制台
3.将以上代码执行,先启动客户端client1和客户端client2的服务
4.再执行消息发送命令

你可能感兴趣的:(rabbitmq)