1. 下载Homebrew
Homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能
Homebrew官网的下载指令由于国内网络原因下载相当慢,甚至会报 443,建议使用国内源
打开终端执行:/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
该地址提供多个国内源地址,根据提示选择国内源,安装非常简便,亲测有效
检查是否安装成功,终端执行:brew -v
打印版本信息安装成功
2. 安装rabbitmq
终端执行:brew install rabbitmq
在安装完成后,安装目录会在末尾进行提示
我的安装目录是:/opt/homebrew/opt/rabbitmq/sbin/rabbitmq-server
2.运行rabbitmq
终端进入rabbitmq安装目录的sbin目录下,执行:./rabbitmq-server --->开启rabbitmq
sbin目录下的,rabbitmqctl 用于关闭rabbitmq: ./rabbitmqctl stop --->关闭rabbitmq
3.rabbitmq的模式
- 简单模式
- 交换机模式
- 交换机之发布订阅模式
- 交换机之关键字模式
- 交换机之通配符模式
3.1 简单模式
原理:
生产者连接rabbitmq,创建一个队列,并向队列发送消息;
消费者连接rabbitmq,监听一个队列,队列内有消息后将消息取出
-
生产者
点击查看代码
import pika # 导包 没有就终端执行 pip install pika # 连接rabbitmq,指定rabitmq服务器的ip 和 端口,5672是rabbitmq的默认端口 connection = pika.BlockingConnection(pika.ConnectionParameters(host='127.0.0.1',port=5672)) # 创建管道 channel = connection.channel() # 创建名字为hello的消息队列,尽量使名字唯一 channel.queue_declare("hello") channel.basic_publish(exchange='', routing_key='hello', # 指定队列,给哪个队列发送消息 body='我是你大爷实打实的') # 向队列发送的内容 print(" [x] Sent '我是你大爷实打实的'")
-
消费者
点击查看代码
import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='127.0.0.1', port=5672)) channel = connection.channel() # 这一步不能省略,生产者、消费者谁先启动,就谁先创建队列。 # 否者消费者后启动,监听 hello 队列时,hello队列并未创建会报错。 channel.queue_declare(queue='hello') # 定义回调函数,用于处理从队列中获取的数据 def callback(ch, method, properties, body): body = body.decode('utf-8') print(" [x] Received %r" % body) # 确定监听的队列 channel.basic_consume(queue='hello', # 监听的队列名 auto_ack=True, # True表示默认应答 on_message_callback=callback) # 指定数据的处理方式 print(' [*] Waiting for messages. To exit press CTRL+C') # 开始监听 channel.start_consuming()
-
参数设置
-
应答参数
在消费者,中默认应答方式为:只要消费者将队列中的数据取出,rabbitmq就将该条消息删除。
这种应答方式,效率很高,但是如果消费者将数据取出后,在执行callback
回调函数发生错误宕机。
重启后该条数据将会消失(数据未处理完,rabbitmq队列也已经删除)。这种默认应答对重要数据显然不够安全。默认应答 ---> 手动应答
手动应答:在消费者获取队列数据后,rabbitmq不会立即将被获取的数据删除,当callback
函数处理完成后,发送消息给rabbitmq 确认后,rabbitmq才会将该条数据删除手动应答效率不如默认应答,但是更安全
点击查看代码
######## 在消费者中 ######## # 定义回调函数,用于处理从队列中获取的数据 def callback(ch, method, properties, body): body = body.decode('utf-8') print(" [x] Received %r" % body) ch.basic_ack(delivery_tag=method.delivery_tag) # 数据安全处理完成,告诉rabbitmq可以删除了 # 确定监听 channel.basic_consume(queue='hello', auto_ack=False, # 手动应答 on_message_callback=callback)
-
持久化参数
rabbitmq,运行时队列内的数据是跑在内存上,当rabbitmq所在服务器宕机时,队列内还未被消费者取出的数据将会消失。
持久化参数,用于将数据保存在硬盘上,即使rabbitmq意外宕机,重启后数据依然存在。点击查看代码
######## 在生产者中 ######## # 申明一个可以持久化存储的队列,注意:消费者此处要保持一致 channel.queue_declare("hello2", durable=True) # 已经申明过的队列不能改变其性质 channel.basic_publish(exchange='', routing_key='hello2', body='我是你大爷实打实的', properties=pika.BasicProperties( # 生产者发送消息时,添加这个参数可以使消息持久化 delivery_mode=2,) )
-
分发参数
rabbitmq默认采用轮询分发,即每个消费者获取队列内的数据个数是一致的。当轮到某个消费者从队列中获取数据,而该消费者(服务器)上个队列数据还未处理完,rabbitmq将会等待该消费者处理完成;即使该消费者的下一个消费者已经处理完上个队列数据,正在等待rabbitmq队列数据,rabbitmq也不会将队列数据发给它的下一个消费者。
这种分法方式显然是不合常理的,每台服务器配置不一样,各个队列数据处理形式也不一样,采用轮询分发,将会造成资源的浪费。
公平分发:只要各消费者数据处理完成了,就可以去取rabbitmq队列中的新数据。点击查看代码
######## 在消费者中 ######## #在消费者中新增加,实现公平分发 channel.basic_qos(prefetch_count=1) # 注意:公平分发必须在 手动应答模式下进行
-
3.2 交换机之订阅发布模式
简单模式下,rabbitmq消息队列的数据只能被一个消费者取走;而在订阅发布模式下,会为每一个订阅者创建一个队列,而发布者(生产者)发布消息时,会将消息放置在所有相关队列中。
-
生产者
点击查看代码
import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='127.0.0.1', port=5672)) channel = connection.channel() # 申明一个交换机 channel.exchange_declare(exchange='logs', # 交换机名称 exchange_type='fanout') # 交换机类型:fanout 就是发布订阅模式 message = "info: Hello World!".encode() channel.basic_publish(exchange='logs', routing_key='', body=message) print(" [x] Sent %r" % message) connection.close()
-
消费者
点击查看代码
import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='127.0.0.1', port=5672)) channel = connection.channel() # 申明一个交换机 channel.exchange_declare(exchange='logs', # 交换机名称 exchange_type='fanout') # 交换机类型:fanout 就是发布订阅模式 # 由pika模块自动生成一个唯一的队列名 result = channel.queue_declare("",exclusive=True) queue_name = result.method.queue # 将队列绑定在交换机上 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, auto_ack=True, on_message_callback=callback) channel.start_consuming()
3.3 交换机之关键字模式
与发布订阅模式类似,为每个订阅者创建一个队列;但是交换机在给队列发送数据时,会根据设定的关键字发送指定数据,并不是将所有数据都发送给每个队列。
1.生产者
点击查看代码
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# 创建一个交换机
channel.exchange_declare(exchange='logs2',
exchange_type='direct') # direct 表示关键字模式
message = "Hello World!".encode()
channel.basic_publish(exchange='logs2',
routing_key='warning', # 本次发布的关键字为 warning
body=message)
print(" [x] Sent %r" % message)
connection.close()
2.消费者
点击查看代码
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# 创建一个交换机
channel.exchange_declare(exchange='logs2',
exchange_type='direct') # direct 表示关键字模式
result = channel.queue_declare("",exclusive=True)
queue_name = result.method.queue
print(queue_name)
# 绑定交换机并指定关键字
channel.queue_bind(exchange='logs2',
queue=queue_name,
routing_key="error") # 获取关键字 error 的数据
# 可以绑定多个关键字,可用for循环写
channel.queue_bind(exchange='logs2',
queue=queue_name,
routing_key="warning") #获取关键字 warning 的数据
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,
auto_ack=True,
on_message_callback=callback)
channel.start_consuming()
3.4 交换机之通配符
通配符模式与关键字模式类似,但是比关键字模式更加精细化。通配符可以进行模糊匹配,
以“key1.key2.keyN....”的模式来指定信息传输的key的大类型和大类型下面的小类型,让消费者可以更加精细的确认自己想要获取的信息类型。而在消费者一段,不用精确的指定具体到哪一个大类型下的小类型的key,而是可以使用类似正则表达式(但与正则表达式规则完全不同)的通配符在指定一定范围或符合某一个字符串匹配规则的key,来获取想要的信息。
“通配符交换机”(Topic Exchange)将路由键和某模式进行匹配。此时队列需要绑定在一个模式上。符号“#”匹配一个或多个词,符号“”仅匹配一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.”只会匹配到“audit.irs”。(这里与我们一般的正则表达式的“*”和“#”刚好相反,这里我们需要注意一下。)
下面是一个解释通配符模式交换机工作的一个样例
1.生产者
点击查看代码
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))
channel = connection.channel()
# 创建一个交换机
channel.exchange_declare(exchange='logs5',
exchange_type='topic') # topic 表示通配符模式
message = "usa.news: Hello World!".encode()
channel.basic_publish(exchange='logs5',
routing_key='usa.news', # 本次发布的key
body=message)
print(" [x] Sent %r" % message)
connection.close()
2.消费者
点击查看代码
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# 创建一个交换机
channel.exchange_declare(exchange='logs5',
exchange_type='topic') #topic表示通配符模式
result = channel.queue_declare("",exclusive=True)
queue_name = result.method.queue
# 绑定交换机,并申明要获取的关键字
channel.queue_bind(exchange='logs5',
queue=queue_name,
routing_key="#.weather") # #号表示多个词,只要末尾是 .weather 的信息都要
# 同样可以绑定多个
channel.queue_bind(exchange='logs5',
queue=queue_name,
routing_key="*.news") # *号表示一个词,末尾是 .news 且前面只有一个词的信息,如 usa.news china.news 等
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,
auto_ack=True,
on_message_callback=callback)
channel.start_consuming()