rabbit MQ 基本
基础准备:
# 下载rabbitmq-server-3.6.9.tar.xz
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.9/rabbitmq-server-generic-unix-3.6.9.tar.xz
# 对于下载xz包进行解压,首先先下载xz压缩工具:
yum install xz
# 对rabbitmq包进行解压:
xz -d xz -d rabbitmq-server-generic-unix-3.6.9.tar.xz
tar -xvf rabbitmq-server-generic-unix-3.6.9.tar
# 将压的文件移动至/usr/local/下 改名rabbitmq:
cp -r rabbitmq_server-3.6.9 /usr/local/rabbitmq
# 这种下载的方式解压后直接可以使用,无需再编译安装;
进入到rabbit文件内,其命令文件存在于sbin文件夹下,因此需要将sbin文件夹的路径添加到PATH中:修改/etc/profile
export PATH=/usr/local/rabbitmq/sbin:$PATH
执行source /etc/profile使得PATH路径更新,rabbitMQ安装成功。
# 启用MQ管理方式:
rabbitmq-plugins enable rabbitmq_management #启动后台管理系统
rabbitmq-server -detached #后台运行rabbitmq
netstat -tulnp # 查看运行中的进程信息
iptables -I INPUT -p tcp --dport 15672 -j ACCEPT # 设置端口号,可供外部访问
# 添加用户和权限:(默认网页guest用户是不允许访问的,需要增加一个用户修改一下权限)
sudo rabbitmqctl add_user tom 123 #在rabbitmq server上创建一个用户(服务器重启后需要重新注册用户)
sudo rabbitmqctl set_permissions -p / tom ".*" ".*" ".*" # 配置权限,允许从外面访问(服务器重启后需要重新配置权限)
rabbitmqctl set_user_tags tom administrator # 修改用户角色
然后就可以远程访问了,可直接配置用户权限等信息。
rabbitMQ基本操作:
rabbitmqctl list_queues # 显示当前队列列表信息
向rabbitMQ软件发送消息:
安装python rabbitMQ module
pip install pika
or
easy_install pika
or
实现最简单的队列通信:
队列rabbit MQ 基本队列
produce.py 发送方:
import pika
credentials = pika.PlainCredentials('tom', '123') # 用户凭证
parameters = pika.ConnectionParameters(host='192.168.121.128',credentials=credentials) # 建立连接
connection = pika.BlockingConnection(parameters)
channel = connection.channel() #队列连接通道
#声明queue
channel.queue_declare(queue='task123',durable=True) # 创建队列名为task123
channel.basic_publish(exchange='', # 生产端发送消息
routing_key='task123', # 路由,数据发送到task123队列
body='Hello World2!' )# 发送的内容
print(" [x] Sent 'Hello World!'")
connection.close()
consumer.py接收端:
import pika
# 客户端连接的时候需要配置认证参数
credentials = pika.PlainCredentials('tom', '123') # 凭证
paramenters = pika.ConnectionParameters('192.168.121.128', credentials = credentials)
connection = pika.BlockingConnection(paramenters)
channel = connection.channel() # 队列连接到通道
def callback(ch, method, properties, body): # 回调函数
print(" [x] Received %r" % body)
channel.basic_consume(callback, # 消费端取到消息后,调用callback 函数
queue='task123',
no_ack=True) #接收端接收消息后,不向rabbit-server发送确认接收信息,发送方将自动完成消息的销毁
channel.start_consuming() # 阻塞模式,发送方发送的消息可以实时的进行接收
队列rabbit MQ 消息持久化
produce.py
import pika
credentials = pika.PlainCredentials('tom', '123') # 用户凭证
parameters = pika.ConnectionParameters(host='192.168.121.128',credentials=credentials) # 建立连接
connection = pika.BlockingConnection(parameters)
channel = connection.channel() #队列连接通道
#声明queue
channel.queue_declare(queue='task123',durable=True) # 创建队列名为task123,durable=True表示队列持久化,不会因为rabbitmq关闭导致队列消失
channel.basic_publish(exchange='',
routing_key='task123', #路由
properties=pika.BasicProperties(
delivery_mode=2, # 2表示消息持久化 make message persistent,用于接收端无法完成接收时,消息队列还能保留在通道中
),
body='Hello World!')
print(" [x] Sent 'Hello World!'")
connection.close()
consumer.py
import pika
import time
# 客户端连接的时候需要配置认证参数
credentials = pika.PlainCredentials('tom', '123') # 凭证
paramenters = pika.ConnectionParameters('192.168.121.128', credentials = credentials)
connection = pika.BlockingConnection(paramenters)
channel = connection.channel() # 队列连接到通道
def callback(ch, method, properties, body): # 回调函数
# time.sleep(5) # 用于模拟接收端未完成消息接收的状况
print(" [x] Received %r" % body)
ch.basic_ack(delivery_tag=method.delivery_tag) # 手动确认,向发送方发送已接收消息,让发送方将消息丢弃,与 no_ack=True形成对应关系
channel.basic_consume(callback, # 消费端取到消息后,调用callback 函数
queue='task123',)
#no_ack=True ) # no_ack=True表示消息处理后,不向rabbit-server确认消息已消费完毕
channel.start_consuming() # 阻塞模式
队列rabbit MQ 消息广播之公平分发
如果Rabbit只管按顺序把消息发到各个消费者身上,不考虑消费者负载的话,很可能出现,一个机器配置不高的消费者那里堆积了很多消息处理不完,同时配置高的消费者却一直很轻松。为解决此问题,可以在各个消费者端,配置perfetch=1,意思就是告诉RabbitMQ在我这个消费者当前消息还没处理完的时候就不要再给我发新消息了。
producer.py
略……与上面的代码相同
consumer.py
import pika
import time
credentials = pika.PlainCredentials('tom', '123') # 凭证
paramenters = pika.ConnectionParameters('192.168.121.128', credentials = credentials)
connection = pika.BlockingConnection(paramenters)
channel = connection.channel() # 队列连接到通道
def callback(ch, method, properties, body): # 回调函数
# time.sleep(5) # 用于模拟接收端未完成消息接收的状况
print(" [x] Received %r" % body)
ch.basic_ack(delivery_tag=method.delivery_tag) # 手动确认,向发送方发送已接收消息,让发送方将消息丢弃,与 no_ack=True形成对应关系
channel.basic_consume(callback, # 消费端取到消息后,调用callback 函数
queue='task123',)
#no_ack=True ) # no_ack=True表示消息处理后,不向rabbit-server确认消息已消费完毕
channel.basic_qos(prefetch_count=1) # 公平分发
channel.start_consuming() # 阻塞模式
队列rabbit MQ 消息广播fanout(参考微博)
之前的例子都基本都是1对1的消息发送和接收,即消息只能发送到指定的queue里,但有些时候你想让你的消息被所有的Queue收到,类似广播的效果,这时候就要用到exchange了.
Exchange在定义的时候是有类型的,以决定到底是哪些Queue符合条件,可以接收消息。
fanout: 所有bind到此exchange的queue都可以接收消息
direct: 通过routingKey和exchange决定的那个唯一的queue可以接收消息
topic:所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息
表达式符号说明:#代表一个或多个字符,*代表任何字符
例:#.a会匹配a.a,aa.a,aaa.a等
*.a会匹配a.a,b.a,c.a等
注:使用RoutingKey为#,Exchange Type为topic的时候相当于使用fanout
headers: 通过headers 来决定把消息发给哪些queue
消息publisher
import pika
import sys
credentials = pika.PlainCredentials('tom', '123')
parameters = pika.ConnectionParameters(host='192.168.121.128', credentials=credentials)
connection = pika.BlockingConnection(parameters)
channel = connection.channel() # 队列连接到通道
# 声明通道类型为广播,exchange名称为logs
channel.exchange_declare(exchange='logs', exchage_type='fanout') # 消息转发, 类型为广播模式
message = ''.join(sys.argv[1:]) or 'info:Hellow World!' # 组装消息
channel.basic_publish(exchange='logs', routing_key='', body=message) # 发送消息
print(" [x] Sent %r" % message)
connection.close() # 关闭连接
消息subscriber
import pika
credentials = pika.PlainCredentials('tom', '123')
parameters = pika.ConnectionParameters(host='192.168.121.128', credentials=credentials)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
# 声明exchange,以防止报错
channel.exchange_declare(exchange='logs', exchange_type='fanout')
# 声明queue队列
queue_obj = channel.queue_declare(exclusive=True)#不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = queue_obj.method.queue # 获得队列名
print('queue name',queue_name,queue_obj)
channel.queue_bind(exchange='logs', queue=queue_name) # 队列绑定到exchange
def callback(ch, method, properties, body):
print('[x]%r'%body)
channel.basic_consume(callback, queue=queue_name, no_ack=True) # 接收消息
channel.start_consuming() # 阻塞状态
队列rabbit MQ 消息组播direct
RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。
publisher
__author__ = 'Administrator'
import pika
import sys
credentials = pika.PlainCredentials('tom', '123')
parameters = pika.ConnectionParameters(host='192.168.121.128',credentials=credentials)
connection = pika.BlockingConnection(parameters)
channel = connection.channel() #队列连接通道
channel.exchange_declare(exchange='direct_log',exchange_type='direct')
log_level = sys.argv[1] if len(sys.argv) > 1 else 'info' # sys.argv[1]代表从命令行接收的参数,作为关键字
message = ' '.join(sys.argv[1:]) or "info: Hello World!"
channel.basic_publish(exchange='direct_log',
routing_key=log_level, # 关键字
body=message)
print(" [x] Sent %r" % message)
connection.close()
consumer:
__author__ = 'Administrator'
import pika,sys
credentials = pika.PlainCredentials('tom', '123')
parameters = pika.ConnectionParameters(host='192.168.121.128',credentials=credentials)
connection = pika.BlockingConnection(parameters)
channel = connection.channel() #队列连接通道
queue_obj = channel.queue_declare(exclusive=True) #不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = queue_obj.method.queue
print('queue name',queue_name,queue_obj)
log_levels = sys.argv[1:] # 接收的参数 info warning errr
if not log_levels:
sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
sys.exit(1)
for level in log_levels:
channel.queue_bind(exchange='direct_log',
queue=queue_name,
routing_key=level) #绑定队列到Exchange
print(' [*] Waiting for logs. To exit press CTRL+C')
def callback(ch, method, properties, body):
print(" [x] %r" % body)
channel.basic_consume(callback,queue=queue_name, no_ack=True)
channel.start_consuming()
队列rabbit MQ 消息组播topic(规则播)
publish
__author__ = 'Administrator'
import pika
import sys
credentials = pika.PlainCredentials('tom', '123')
parameters = pika.ConnectionParameters(host='192.168.121.128',credentials=credentials)
connection = pika.BlockingConnection(parameters)
channel = connection.channel() #队列连接通道
channel.exchange_declare(exchange='topic_log',exchange_type='topic')
#log_level = sys.argv[1] if len(sys.argv) > 1 else 'info'
log_level = sys.argv[1] if len(sys.argv) > 1 else 'all.info'
message = ' '.join(sys.argv[1:]) or "all.info: Hello World!"
channel.basic_publish(exchange='topic_log',
routing_key=log_level,
body=message)
print(" [x] Sent %r" % message)
connection.close()
consumer
__author__ = 'Administrator'
import pika,sys
credentials = pika.PlainCredentials('tom', '123')
parameters = pika.ConnectionParameters(host='192.168.121.128',credentials=credentials)
connection = pika.BlockingConnection(parameters)
channel = connection.channel() #队列连接通道
queue_obj = channel.queue_declare(exclusive=True) #不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = queue_obj.method.queue
log_levels = sys.argv[1:] # info warning errr
if not log_levels:
sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
sys.exit(1)
for level in log_levels:
channel.queue_bind(exchange='topic_log',
queue=queue_name,
routing_key=level) #绑定队列到Exchange
print(' [*] Waiting for logs. To exit press CTRL+C')
def callback(ch, method, properties, body):
print(" [x] %r" % body)
channel.basic_consume(callback,queue=queue_name, no_ack=True)
channel.start_consuming()
- consumer接收的规则:
To receive all the logs run:
python receive_logs_topic.py “#”
To receive all logs from the facility “kern”:
python receive_logs_topic.py “kern.*”
Or if you want to hear only about “critical” logs:
python receive_logs_topic.py “*.critical”
You can create multiple bindings:
python receive_logs_topic.py “kern.” “.critical”
And to emit a log with a routing key “kern.critical” type:
python emit_log_topic.py “kern.critical” “A critical kernel error”
RPC(由client消费方先发送数据)
client:
# 1.声明一个队列,作为reply_to返回消息结果的队列
# 2. 发消息到队列,消息里带一个唯一标识符uid,reply_to
# 3. 监听reply_to 的队列,直到有结果
import queue
import pika
import uuid
class CMDRpcClient(object):
def __int__(self):
# 初始化用户凭证
credentials = pika.PlainCredentials('tom', '123')
parameters = pika.ConnectionParameters('192.168.121.128', credentials=credentials)
self.connection = pika.BlockingConnection(parameters)
self.channel = self.connection.channel()
# 指定queue名字, rabbit会随机分配一个名字, exclusive = True会在使用此queue的消费者断开后, 自动将queue删除
result = self.channel.queue_declare(exclusive=True)
self.callback_queue = result.method.queue #命令的执行结果的queue
# 声明要监听callback_queue
self.channel.basic_consume(self.on_response, no_ack=True, queue=self.callback_queue)
def on_response(self, ch, method, props, body):
"""
收到服务器端命令结果后执行这个函数
:param ch:
:param method:
:param props:
:param body:
:return:
"""
if self.corr_id == props.correlation_id: # 如果当前队列id等于服务端发送消息队列的id,则获取消息,完成队列唯一性标识
self.response = body.decode('gbk') # 把执行结果赋值给Response
def call(self, n):
"""响应消息给服务端"""
self.response = None
self.corr_id = str(uuid.uuid4()) #唯一标识符号
self.channel.basic_publish(exchange='',
routing_key='rpc_queue2', # 数据发送的队列名称
properties=pika.BasicProperties(
reply_to=self.callback_queue,
correlation_id=self.corr_id,
),
body=str(n)
)
while self.response is None:
self.connection.process_data_events() #检测监听的队列里有没有新消息,如果有,收,如果没有,返回None
return self.response
cmd_rpc = CMDRpcClient()
response = cmd_rpc.call('ipconfig')
print(response)
server:
import pika
import subprocess
#1 。 定义on_request函数
#2. 声明接收指令的队列名rpc_queue
#3. 开始监听队列,收到消息后 调用on_request函数
#4 把on_request执行结果,发送回客户端指定的reply_to 队列
# 声明用户凭证
credentials = pika.PlainCredentials('tom', '123')
parameters = pika.ConnectionParameters('192.168.121.128', credentials=credentials)
connection = pika.BlockingConnection(parameters)
channel = connection.channel() # 队列连接到通道
channel.queue_declare(queue='rpc_queue2') # 声明队列
def run_cmd(cmd):
"""
执行指令
:param cmd:
:return:
"""
# 开启子进程执行服务器上的命令
cmd_obj = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
result = cmd_obj.stdout.read()+cmd_obj.stderr.read() # 读取命令结果
return result
def on_request(ch, method, props, body):
"""
接收消息后的回调函数
:return:
"""
cmd = body.decode('utf-8') # 获得消费端发送回来的消息指令
print("[.]run (%s)"%cmd)
response = run_cmd(cmd)
# 生产方发送数据
ch.basic_publish(exchange='', routing_key=props.reply_to, # 将消费方发来的消息执行完对应方法后,响应到消费方指定的队列中
properties=pika.BasicProperties(correlation_id=props.correlation_id),
body=response
)
ch.basic_ack(delivery_tag=method.delivery_tag) # 通知消费方发送的队列标识
channel.basic_consume(on_request, queue='rpc_queue2') # 去'rpc_queue2'队列中接收消费方发送过来的消息
print(" [x] Awaiting RPC requests")
channel.start_consuming() # 开始阻塞式接收