• rabbitmq 从入门到精通


    rabbitmq 从入门到精通

    消息队列介绍

    1.1 介绍

    消息队列就是基础数据结构中的“先进先出”的一种数据机构。想一下,生活中买东西,需要排队,先排的人先买消费,就是典型的“先进先出”

    在这里插入图片描述

    1.2 MQ解决什么问题

    MQ是一直存在,不过随着微服务架构的流行,成了解决微服务之间问题的常用工具。

    应用解耦

    以电商应用为例,应用中有订单系统、库存系统、物流系统、支付系统。用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障,都会造成下单操作异常。

    当转变成基于消息队列的方式后,系统间调用的问题会减少很多,比如物流系统因为发生故障,需要几分钟来修复。在这几分钟的时间里,物流系统要处理的内存被缓存在消息队列中,用户的下单操作可以正常完成。当物流系统恢复后,继续处理订单信息即可,中单用户感受不到物流系统的故障。提升系统的可用性
    在这里插入图片描述

    流量消峰

    举个栗子,如果订单系统最多能处理一万次订单,这个处理能力应付正常时段的下单时绰绰有余,正常时段我们下单一秒后就能返回结果。但是在高峰期,如果有两万次下单操作系统是处理不了的,只能限制订单超过一万后不允许用户下单。

    使用消息队列做缓冲,我们可以取消这个限制,把一秒内下的订单分散成一段时间来处理,这事有些用户可能在下单十几秒后才能收到下单成功的操作,但是比不能下单的体验要好。

    消息分发

    多个服务队数据感兴趣,只需要监听同一类消息即可处理。

    在这里插入图片描述

    例如A产生数据,B对数据感兴趣。如果没有消息的队列A每次处理完需要调用一下B服务。过了一段时间C对数据也感性,A就需要改代码,调用B服务,调用C服务。只要有服务需要,A服务都要改动代码。很不方便。

    在这里插入图片描述

    有了消息队列后,A只管发送一次消息,B对消息感兴趣,只需要监听消息。C感兴趣,C也去监听消息。A服务作为基础服务完全不需要有改动

    异步消息

    在这里插入图片描述

    有些服务间调用是异步的,例如A调用B,B需要花费很长时间执行,但是A需要知道B什么时候可以执行完,以前一般有两种方式,A过一段时间去调用B的查询api查询。或者A提供一个callback api,B执行完之后调用api通知A服务。这两种方式都不是很优雅

    在这里插入图片描述

    使用消息总线,可以很方便解决这个问题,A调用B服务后,只需要监听B处理完成的消息,当B处理完成后,会发送一条消息给MQ,MQ会将此消息转发给A服务。

    这样A服务既不用循环调用B的查询api,也不用提供callback api。同样B服务也不用做这些操作。A服务还能及时的得到异步处理成功的消息

    1.3 常见消息队列及比较

    在这里插入图片描述

    结论:

    Kafka在于分布式架构,RabbitMQ基于AMQP协议来实现,RocketMQ/思路来源于kafka,改成了主从结构,在事务性可靠性方面做了优化。广泛来说,电商、金融等对事务性要求很高的,可以考虑RabbitMQ和RocketMQ,对性能要求高的可考虑Kafka

    Rabbitmq安装

    官网:https://www.rabbitmq.com/getstarted.html

    2.1 服务端原生安装

    # 安装配置epel源
    # 安装erlang
    yum -y install erlang
    # 安装RabbitMQ
    yum -y install rabbitmq-server
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.2 服务端Docker安装

    推荐使用

    docker pull rabbitmq:management
    docker run -di --name Myrabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq:managemen
    
    • 1
    • 2

    2.3 客户端安装

    # python 连接 rabbitmq 需要下载 pika 模块
    pip3 install pika
    
    • 1
    • 2

    2.4 设置用户和密码

    1. 添加一个新用户
    rabbitmqctl add_user lxx 123
    2. 设置用户为administrator角色
    rabbitmqctl set_user_tags lxx administrator
    3. 设置权限
    rabbitmqctl set_permissions -p "/" lxx ".*" ".*" ".*"
    
    4. 然后重启rabbiMQ服务
    systemctl reatart rabbitmq-server
     
    # 然后可以使用刚才的用户远程连接rabbitmq server了。
    # 也可以通过前端页面设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    基于Queue实现生产者消费者模型

    import Queue
    import threading
    
    message = Queue.Queue(10)
    
    def producer(i):
        while True:
            message.put(i)
    
    def consumer(i):
        while True:
            msg = message.get()
    
    for i in range(12):
        t = threading.Thread(target=producer, args=(i,))
        t.start()
    
    for i in range(10):
        t = threading.Thread(target=consumer, args=(i,))
        t.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    基本使用(生产者消费者模型)

    对于RabbitMQ来说,生产和消费不再针对内存里的一个Queue对象,而是某台服务器上的RabbitMQ Server实现的消息队列。

    生产者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    channel.queue_declare(queue="lxx")
    channel.basic_publish(exchange='',
                          routing_key='lxx',  # 消息队列名称
                          body='hello world'  # 消息内容
                          )
    connection.close()  # 关闭链接
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    消费者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("lxx", "123")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    
    # 定义一个回调函数
    def callback(ch, method, properites, body):
        print("消费者接收到了任务: %s" % body)
    
    
    channel.basic_consume(queue="lxx", on_message_callback=callback, auto_ack=True)
    channel.start_consuming()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    消息安全之ack

    ack 也叫做消息确认机制,只有当消息处理完之后,消费者才会向生产者发送消息,确认数据处理完,然后数据就进行清除

    生产者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    channel.queue_declare(queue="lxx")
    channel.basic_publish(exchange='',
                          routing_key='lxx',  # 消息队列名称
                          body='hello world'  # 消息内容
                          )
    connection.close()  # 关闭链接
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    消费者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("lxx", "123")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    
    
    def callback(ch, method, properites, body):
        print("消费者接收到了任务: %s" % body)
        # 通知服务端,消息处理完了,如果auto_ack=False,不加,消息会一直存在
        ch.basic_ack(delivery_tag=method.delivery_tag)
    # 不会自动恢复确认消息, auto_ack = True, 队列收到确认,就会自动把消费过的消息删除
    channel.basic_consume(queue="lxx", on_message_callback=callback, auto_ack=False)
    channel.start_consuming()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    消息安全之durable持久化

    durable 持久化有两个注意点:

    1. 消息持久化的前提的是先开启队列持久化
    2. 消息持久化并不能完全确保数据不丢失

    生产者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    channel.queue_declare(queue="lxx", durable=True)  # 开启持久化队列
    channel.basic_publish(exchange='',
                          routing_key='lxx1',  # 消息队列名称
                          body='111',  # 消息内容
                          properties=pika.BasicProperties(
                              delivery_mode=2  # 开启消息持久化
                          )
                          )
    connection.close()  # 关闭链接
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    消费者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("lxx", "123")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    
    channel.queue_declare(queue='lxx1', durable=True)
    
    def callback(ch, method, properites, body):
        print("消费者接收到了任务: %s" % body)
        # 通知服务端,消息取走了,如果auto_ack=False,不加下面,消息会一直存在
        ch.basic_ack(delivery_tag=method.delivery_tag)
    
    # 不会自动恢复确认消息, auto_ack = True, 队列收到确认,就会自动把消费过的消息删除
    channel.basic_consume(queue="lxx1", on_message_callback=callback, auto_ack=False)
    channel.start_consuming()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    闲置消费

    rabbitmq 默认情况下如果有多个消费者,是按照顺序第一个消息给第一个消费者,第二个消息给第二个消费者,这种轮询的方式

    但是在处理消息的过程中,可能会出现第一个消息的消费者处理消息很耗时,一直没结束,如果一直等着,队列就会阻塞,第二个消费者就出现了闲置现象,解决方式是 更改分配方案,谁有空谁处理

    生产者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    channel.queue_declare(queue="lxx", durable=True)  # 开启持久化队列
    channel.basic_publish(exchange='',
                          routing_key='lxx1',  # 消息队列名称
                          body='111',  # 消息内容
                          properties=pika.BasicProperties(
                              delivery_mode=2  # 开启消息持久化
                          )
                          )
    connection.close()  # 关闭链接
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    消费者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    
    
    def callback(ch, method, properties, body):
        print("消费者接受到了任务: %r" % body)
        # 通知服务端,消息取走了,如果auto_ack=False,不加下面,消息会一直存在
        ch.basic_ack(delivery_tag=method.delivery_tag)
    
    # 就只有这一句话 谁闲置谁获取,没必要按照顺序一个一个来
    channel.basic_qos(prefetch_count=1)  
    channel.basic_consume(queue='lxx1', on_message_callback=callback, auto_ack=False)
    
    channel.start_consuming()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    发布订阅

    发布者只能向交换器发送消息。然后交换器再将消息推送到队列中,交换器必须清楚的知道如何处理该消息,具体规则由exchange来定义:

    exchange 自定义即可,有 directtopicheadersfanout四种方案。

    发布者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    # 声明队列没有指定名字,指定了exchange
    channel.exchange_declare(exchange='logs', exchange_type='fanout')
    
    message = "info: Hello World!"
    channel.basic_publish(exchange='logs', routing_key='', body=message)
    print(" [x] Sent %r" % message)
    connection.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    订阅者

    订阅者可以多启动几个, 用来查看,并且每启动一个就会产生一个队列

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    
    channel.exchange_declare(exchange='logs', exchange_type='fanout')
    
    # exclusive 一旦 consumer 连接断开,队列就被删除
    result = channel.queue_declare(queue='', exclusive=True)
    # 获取随机产生的 队列名字
    queue_name = result.method.queue
    print(queue_name)
    channel.queue_bind(exchange='logs', queue=queue_name)
    
    print(' [*] Waiting for logs. To exit press CTRL+C')
    
    def callback(ch, method, properties, body):
        print(" [x] %r" % body)
    
    channel.basic_consume(
        queue=queue_name, on_message_callback=callback, auto_ack=True)
    
    channel.start_consuming()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    发布订阅高级之Routing

    Routing 方案下,可以通过关键词来匹配订阅者是否接收消息

    发布者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    # 声明队列没有指定名字,指定了exchange
    channel.exchange_declare(exchange='m1', exchange_type='direct')
    
    message = "info: Hello World!"
    # 通过 routing_key 设置关键字
    channel.basic_publish(exchange='m1', routing_key='bnb', body=message)
    print(" [x] Sent %r" % message)
    connection.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    订阅者1

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    
    channel.exchange_declare(exchange='m1', exchange_type='direct')
    
    # exclusive 一旦 consumer 连接断开,队列就被删除
    result = channel.queue_declare(queue='', exclusive=True)
    # 获取随机产生的 队列名字
    queue_name = result.method.queue
    print(queue_name)
    # 因为 routing_key 不一样,因此收不到消息
    channel.queue_bind(exchange='m1', queue=queue_name, routing_key='nb')
    
    print(' [*] Waiting for logs. To exit press CTRL+C')
    
    def callback(ch, method, properties, body):
        print(" [x] %r" % body)
    
    channel.basic_consume(
        queue=queue_name, on_message_callback=callback, auto_ack=True)
    
    channel.start_consuming()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    订阅者2

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    
    channel.exchange_declare(exchange='m1', exchange_type='direct')
    
    # exclusive 一旦 consumer 连接断开,队列就被删除
    result = channel.queue_declare(queue='', exclusive=True)
    # 获取随机产生的 队列名字
    queue_name = result.method.queue
    print(queue_name)
    # 可以绑定多个,只要有一个 routing_key 符合即可收到消息
    channel.queue_bind(exchange='m1', queue=queue_name, routing_key='nb')
    channel.queue_bind(exchange='m1', queue=queue_name, routing_key='bnb')
    
    print(' [*] Waiting for logs. To exit press CTRL+C')
    
    def callback(ch, method, properties, body):
        print(" [x] %r" % body)
    
    channel.basic_consume(
        queue=queue_name, on_message_callback=callback, auto_ack=True)
    
    channel.start_consuming()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    发布订阅高级之Topic

    相比于Routing 的匹配功能,Topic 的使用和 Routing 一样, 需要注意的只有两点

    1. * 只能代替 符号 . 后面一个单词
    2. # 可以代替 符号 . 一个或多个单词

    发布者

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    # 声明队列没有指定名字,指定了exchange
    channel.exchange_declare(exchange='m2', exchange_type='topic')
    
    message = "info: Hello World!"
    # 通过 routing_key 设置关键字
    channel.basic_publish(exchange='m2',
                          routing_key='bnb.handsome.xx', # 这种情况下,只有bnb.# 能收到
                          body=message)
    print(" [x] Sent %r" % message)
    connection.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    订阅者1

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    
    channel.exchange_declare(exchange='m2', exchange_type='topic')
    
    # exclusive 一旦 consumer 连接断开,队列就被删除
    result = channel.queue_declare(queue='', exclusive=True)
    # 获取随机产生的 队列名字
    queue_name = result.method.queue
    print(queue_name)
    # 匹配不到接收不到消息
    channel.queue_bind(exchange='m2', queue=queue_name, routing_key='bnb.*')
    
    print(' [*] Waiting for logs. To exit press CTRL+C')
    
    def callback(ch, method, properties, body):
        print(" [x] %r" % body)
    
    channel.basic_consume(
        queue=queue_name, on_message_callback=callback, auto_ack=True)
    
    channel.start_consuming()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    订阅者2

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    
    channel.exchange_declare(exchange='m2', exchange_type='topic')
    
    # exclusive 一旦 consumer 连接断开,队列就被删除
    result = channel.queue_declare(queue='', exclusive=True)
    # 获取随机产生的 队列名字
    queue_name = result.method.queue
    print(queue_name)
    # 如下两种方式都能成功匹配
    # channel.queue_bind(exchange='m2', queue=queue_name, routing_key='bnb.handsome.*')
    channel.queue_bind(exchange='m2', queue=queue_name, routing_key='bnb.#')
    
    print(' [*] Waiting for logs. To exit press CTRL+C')
    
    def callback(ch, method, properties, body):
        print(" [x] %r" % body)
    
    channel.basic_consume(
        queue=queue_name, on_message_callback=callback, auto_ack=True)
    
    channel.start_consuming()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    基于rabbitmq实现rpc

    有关rpc介绍请看 python rpc 框架

    服务端

    import pika
    
    # 链接rabbitmq
    # 有密码 加上这一行 本地无密码链接忽略即可
    credentials = pika.PlainCredentials("admin", "admin")
    # 获取链接对像
    connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
    # 声明一个队列
    channel = connection.channel()
    # 声明队列没有指定名字,指定了exchange
    
    # 启动一个监听任务队列
    channel.queue_declare(queue='rpc_queue')
    def on_request(ch, method, props, body):
        n = int(body)
        response = n + 100
        # props.reply_to  存放结构的队列
        # props.correlation_id  任务
        ch.basic_publish(exchange='',
                         routing_key=props.reply_to,
                         properties=pika.BasicProperties(correlation_id=props.correlation_id),
                         body=str(response))
        ch.basic_ack(delivery_tag=method.delivery_tag)
    
    channel.basic_qos(prefetch_count=1)
    channel.basic_consume(queue='rpc_queue',on_message_callback=on_request)
    channel.start_consuming()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    客户端

    import pika
    import uuid
    
    
    class FibonacciRpcClient(object):
        def __init__(self):
            credentials = pika.PlainCredentials("admin", "admin")
            self.connection = pika.BlockingConnection(pika.ConnectionParameters("150.116.92.62", credentials=credentials))
            self.channel = self.connection.channel()
    
            # 随机生成一个消息队列(用于接收结果)
            result = self.channel.queue_declare(queue='', exclusive=True)
            self.callback_queue = result.method.queue
    
            # 监听消息队列中是否有值返回,如果有值则执行 on_response 函数(一旦有结果,则执行on_response)
            self.channel.basic_consume(queue=self.callback_queue, on_message_callback=self.on_response, auto_ack=True)
    
        def on_response(self, ch, method, props, body):
            if self.corr_id == props.correlation_id:
                self.response = body
    
        def call(self, n):
            self.response = None
            self.corr_id = str(uuid.uuid4())
    
            # 客户端 给 服务端 发送一个任务:  任务id = corr_id / 任务内容 = '30' / 用于接收结果的队列名称
            self.channel.basic_publish(exchange='',
                                       routing_key='rpc_queue',  # 服务端接收任务的队列名称
                                       properties=pika.BasicProperties(
                                           reply_to=self.callback_queue,  # 用于接收结果的队列
                                           correlation_id=self.corr_id,  # 任务ID
                                       ),
                                       body=str(n))
            while self.response is None:
                # 保持连接, 检查是否有回调
                self.connection.process_data_events()
            return self.response
    
    fibonacci_rpc = FibonacciRpcClient()
    
    response = fibonacci_rpc.call(50)
    print('返回结果:', response)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
  • 相关阅读:
    CMSC5724-数据挖掘之线性分类问题与感知机
    区分何时使用内连接或者外连接
    高效技巧揭秘:Java轻松批量插入或删除Excel行列操作
    flask第一个应用
    基于ISO智能交通系统框架的 LTE-V2X技术规范
    猿创征文|Java-泛型(Generic)
    IDEA 设置代码注释模板
    PCL 环境下安装配置CGAL 5.5
    【C++模拟实现】手撕红黑树(含图解)
    Java final关键字
  • 原文地址:https://blog.csdn.net/qq_55752792/article/details/126547688