rabbitmq 安装、使用

一、安装

须知:

~ 百度网盘存了对应版本软件

~erlang 与 rabbitmq 版本要一致: RabbitMQ Erlang Version Requirements — RabbitMQ

~安装目录一定不能有空格和中文字符

 

主参考: windows安装Rabbit MQ_51CTO博客_rabbitmq windows安装

 

其中: ERLANG 环境变量配置完后, win + R  -> cmd

有下图效果表示成功.

 

 

其他参考:

rabbitmq 下载地址: 

Downloading and Installing RabbitMQ — RabbitMQ

~ github 下载地址:  Release RabbitMQ 3.8.4 · rabbitmq/rabbitmq-server · GitHub

 

二、使用

参考1  Hello World · RabbitMQ in Chinese (mr-ping.com)    该内容中部分文字翻译和代码编写与官方英文文档之间有误差

参考2 Python三方库:Pika(RabbitMQ基础使用) - 山上下了雪-bky - 博客园 (cnblogs.com) 

官方文档RabbitMQ: RabbitMQ Tutorials — RabbitMQ  这里的代码是最准确的

官方文档pika: Connecting to RabbitMQ with Callback-Passing Style — pika 1.2.1 documentation

 

三 、使用总结

模式一 Hello world模式

 生产者py

import pika

"""
send.py
主逻辑
1 创建一个连接
2 创建一个Channel
3 创建Queue
4 发布消息
5 关闭连接
"""
conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))

channel = conn.channel()

channel.queue_declare(queue='que1')

channel.basic_publish(
    exchange='',
    routing_key='que1',  # 默认交换机比较特别,它允许我们指定消息究竟需要投递到哪个具体的队列中,队列名字需要在routing_key参数中指定
    body=b'hello word'
)

conn.close()
View Code

消费者py

"""
receive.py
主逻辑
1 Create conn
2 Create Channel
3 Set Queue
4 Define func of Msg deal
5 Receive
6 Cycle Waiting

"""
import pika


def main():
    conn2 = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))

    channel2 = conn2.channel()

    channel2.queue_declare(queue='que1')

    def deal_msg(ch, method, properties, body):
        print(f'The msg is {body}')

    channel2.basic_consume(queue='que1', on_message_callback=deal_msg, auto_ack=True)

    print(f'Waiting for Msg')

    channel2.start_consuming()


if __name__ == '__main__':
    main()
View Code

 

模式二 Work queues模式

本模式叫 'task queue',
    特点:
        同一queue可以允许多个consume从中获取消息
     一个任务只分发给一个worker queue可持久化: durable=True --- 可以看源码注释理解 msg可持久化: properties=pika.BasicProperties(deliver_mode=2) 手动确认消息处理完成: ch.basic_ack(delivery_tag=method.delivery_tag)

 

生产者py

"""
new_task.py
1 Create Connection
2 Create Channel
3 Set queue
4 Publish msg
5 Close Conn

"""
import pika

Conn_rabbitmq = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))

Channel_rabbitmq = Conn_rabbitmq.channel()

Channel_rabbitmq.queue_declare(queue='task_queue', durable=True)  # make queue persistent

Channel_rabbitmq.basic_publish(
    exchange='',
    routing_key='task_queue',
    body=b'Test task queue66',
    properties=pika.BasicProperties(delivery_mode=2)  # make msg persistent
)

Conn_rabbitmq.close()
View Code

消费者py

"""
worker.py
主逻辑
1 create conn
2 create channel
3 declare queue
4 make func_of_callback
5 set num of pre deal msg
6 consume msg(deal task)
"""
import time

import pika

conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))

channel = conn.channel()

channel.queue_declare(queue='task_queue', durable=True)


def callback(ch, method, properties, body):
    print(f'body:{body}')
    time.sleep(11)
    print(f'wan le 1')
    # 手动标记消息已处理
    ch.basic_ack(delivery_tag=method.delivery_tag)


channel.basic_qos(prefetch_count=1)
channel.basic_consume(
    queue='task_queue',
    on_message_callback=callback
)
channel.start_consuming()
View Code

 

模式三 Publish/Subscribe模式

演示假设:

1 交换机选择: 默认交换机还是指定交换机(指定类型且命名)
2 关于队列:
如何让服务器给我们安排一个随机唯一队列
res = channel.queue_declare(queue='')
当与消费者断开连接时, 如何断开连接
exclusive = True
3 交换机与队列绑定

=================================================
本次演示设计一个日志系统:
选定一个扇形交换机,这样所有队列都可以收到一样消息;
两个接收者,一个接收日志把日志保存到硬盘,一个接收日志把日志输出到控制台.
View Code

生产者py

"""
emit_log.py
1 make conn
2 make channel
3 declare exchange
4 emit log(exchange is all exchange; set msg content)
5 close conn
"""

import pika

conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = conn.channel()
channel.exchange_declare(
    exchange='logs',
    exchange_type='fanout'
)

channel.basic_publish(
    exchange='logs',
    routing_key='',  # 消息携带的标识符(本来是用于跟交换机与队列绑定的routing_key去匹配,但是扇形交换机没有.即不用匹配)
    body=b'this log6'
)

conn.close()
View Code

消费者py

"""
1 make conn
2 make channel
31 declare exchange
32 declare queue
41 queue is bound to exchange  (add get queue name)
51 set basic consume  (set func of callback)
52 start consume
"""

import pika

conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = conn.channel()

channel.exchange_declare(
    exchange='logs',
    exchange_type='fanout'
)

queue_be_set = channel.queue_declare(queue='', exclusive=True)
queue_name = queue_be_set.method.queue
channel.queue_bind(
    queue=queue_name,
    exchange='logs'
)


def callback(ch, method, properties, body):
    print(f'received log: {body}')


channel.basic_consume(
    queue=queue_name,
    on_message_callback=callback,
    auto_ack=True

)

channel.start_consuming()
View Code

 

模式四 Routing模式

producer端:从代码上讲,producer的代码与发布订阅模式相似. 不同的是, exchange类型为direct, 且发送msg时多了一个routing_key参数, exchange会根据消息携带的routing_key是否匹配 "exchange与queue绑定的routing_key"来发送消息到对应的queue中.

生产者py

"""
emit_log_direct.py
1 create conn
2 create channel
3 declare exchange(name and type)
4 set basic publish
5 close conn
"""

import pika

conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))

channel = conn.channel()

channel.exchange_declare(exchange='logs_direct', exchange_type='direct')

log_level1 = 'info'
log_level2 = 'warn'
log_level3 = 'error'

# exchange 会根据routing_key 将消息发送到 对应的queue中
channel.basic_publish(
    exchange='log_direct',
    routing_key='error',
    body=b'wo shi first log_direct2'
)

conn.close()
View Code

消费者py

"""
receive_logs_direct.py
1 create conn
2 create channel
31 declare exchange
32 make unique queue
4 queue bind exchange, and set receive whiches routing_key
5 set basic consume
    make func of callback
6 start consume

"""

import pika

conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))

channel = conn.channel()

channel.exchange_declare(exchange='log_direct', exchange_type='direct')
unique_queue = channel.queue_declare(queue='', exclusive=True)
queue_name = unique_queue.method.queue

for severity in ['error']:
    channel.queue_bind(
        queue=queue_name,
        exchange='log_direct',
        routing_key=severity
    )


def callback(ch, method, properties, body):
    print(f'receive msg is {body}')


channel.basic_consume(
    queue=queue_name,
    on_message_callback=callback,
    auto_ack=True
)

channel.start_consuming()
View Code

 

模式五 Topic模式

 相较于路由模式, 主题模式更加灵活, 原因是 "exchange与queue绑定的routing_key"可以带通配符(*号和#号), 单词间用英文小圆点分割

关于Topic<主题交换机>模式 /专门研究总结的/
一 routing_key:
1 以小圆点分割词语组 如:<单词1>.<单词2>.<单词3>
2 匹配规则可以使用通配符*和#,其中
*匹配一个单词
#匹配零个或多个单词

3 是用来匹配消息的关键
当一个消息被发送到交换机时,交换机会根据消息的routing_key是否符合
       '队列与交换机之间绑定的routing_key'的匹配规则,是, 对应队列可以接收到消息;反之,不能.

二 它可以表现出跟其他交换机类似的行为
当一个队列的绑定键为 "#"(井号) 的时候,这个队列将会无视消息的路由键,接收所有的消息。
当 * (星号) 和 # (井号) 这两个特殊字符都未在绑定键中出现的时候,此时主题交换机就拥有的直连交换机的行为

生产者py

"""
# emit_log_topic.py
1 make conn
2 make channel
3 declare exchange(name & type)
4 set routing_key
5 set publish basic
6 close conn
"""

import pika

conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = conn.channel()

channel.exchange_declare(exchange='topic_logs', exchange_type='topic')

channel.basic_publish(
    exchange='topic_logs',
    routing_key='anonymous.a',
    body=b'test send msg1'
)

conn.close()
View Code

消费者py

"""
1 make conn
2 make channel
3 declare exchange(name & type)
4 set unique queue
5 queue is bound to exchange
6 consume basic
    which queue
    func of callback
    is manual tell task which be finish or auto
7 start consume
"""

# receive_logs_topic.py
import pika

conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = conn.channel()

channel.exchange_declare(exchange='topic_logs', exchange_type='topic')
unique_queue = channel.queue_declare(queue='', exclusive=True)
queue_name = unique_queue.method.queue

# 可以绑定多个routing_key
# for key in ['anonymous.*', '*.info', '*.error']:
for key in ['anonymous.a', '*.info', '*.error']:
    channel.queue_bind(
        queue=queue_name,
        exchange='topic_logs',
        routing_key=key
    )


def callback(ch, method, properties, body):
    print(f'I consume {body}')


channel.basic_consume(
    queue=queue_name,
    on_message_callback=callback,
    auto_ack=True
)

channel.start_consuming()
View Code

 

模式六 RPC模式

RPC远程调用(Remote Procedure Call)模式其实就是使用消息队列处理请求的一种方式,通常请求接收到后会立即执行且多个请求是并行执行的,如果一次性来了太多请求,达到了服务端处理请求的瓶颈就会影响性能,但是如果使用消息队列的方式,最大的一点好处是可以不用立即处理请求,而是将请求放入消息队列,服务端只需要根据自己的状态从消息队列中获取并处理请求即可。

producer端:RPC模式的客户端(producer)需要使用到两个queue,一个用于发送request消息(此queue通常在服务端声明和创建),一个用于接收response消息。另外需要特别注意的一点是,需要为每个request消息指定一个uuid(correlation_id属性,类似请求id),用于识别返回的response消息是否属于对应的request

 

客户端py

服务端py

"""
rpc_server.py
1 make conn
2 make channel
3 declare queue that server from queue get task
4 get task, deal task
    1 deal
    2 set properties one: replay_to -> 处理结果发送到哪个队列
    3 set properties two: correlation_id -> 使得请求与响应一一对应

"""

import pika

conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = conn.channel()

# 声明服务端从该队列获取任务
channel.queue_declare('rpc_queue')


def fib(n):
    if n < 1:
        return n
    else:
        return n - 1


# 回调函数
def callback(ch, method, properties, body):
    n = int(body)
    resp = fib(n)

    ch.basic_publish(
        exchange='',
        routing_key=properties.reply_to,  # response发送到该queue
        body=str(resp),
        properties=pika.BasicProperties(
            # 通过correlation_id 使得处理结果与请求一一对应
            correlation_id=properties.correlation_id
        )
    )
    # 手动方式标记消息已处理完
    ch.basic_ack(delivery_tag=method.delivery_tag)


# 设定当前服务端处理任务的数量
channel.basic_qos(prefetch_count=1)

# 从rpc_queue队列获取消息后处理
channel.basic_consume(
    queue='rpc_queue',
    on_message_callback=callback
)

channel.start_consuming()
View Code

 

posted @ 2023-12-12 16:24  tslam  阅读(25)  评论(0编辑  收藏  举报