RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。
RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。
所有主要的编程语言均有与代理接口通讯的客户端库。
1.安装
1、下载erlang和rabbitmq-server的rpm
http://www.rabbitmq.com/releases/erlang/erlang-19.0.4-1.el7.centos.x86_64.rpm
http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.6/rabbitmq-server-3.6.6-1.el7.noarch.rpm
2、安装erlang
[root@rabbitmq ~]# cd /server/scripts/
[root@rabbitmq scripts]# ll
total 23508
-rw-r--r--. 1 root root 18580960 Jan 28 10:04 erlang-19.0.4-1.el7.centos.x86_64.rpm
-rw-r--r--. 1 root root 5487706 Jan 28 10:04 rabbitmq-server-3.6.6-1.el7.noarch.rpm
[root@rabbitmq scripts]# rpm -ivh erlang-19.0.4-1.el7.centos.x86_64.rpm
测试erlang是否安装成功:
[root@rabbitmq scripts]# erl
Erlang/OTP 19 [erts-8.0.3] [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.0.3 (abort with ^G)
1> 5+6.
11
2> halt(). #退出
3、安装socat (备注:安装RabbitMQ必须先安装socat依赖,否则会报错)
[root@rabbitmq scripts]# yum install socat
4、安装RabbitMQ
[root@rabbitmq scripts]# rpm -ivh rabbitmq-server-3.6.6-1.el7.noarch.rpm
启动和关闭:
/sbin/service rabbitmq-server start #启动服务
/sbin/service rabbitmq-server stop #关闭服务
/sbin/service rabbitmq-server status #查看服务状态
5、cd 到/sbin目录下:
./rabbitmq-plugins list
./rabbitmqctl status
2.用户
# 创建用户
[root@localhost sbin]# rabbitmqctl add_user admin admin # 创建 用户admin 密码admin
Creating user "admin" ...
...done.
[root@localhost sbin]# rabbitmqctl set_user_tags admin administraotr # 为用户分配角色
Setting tags for user "admin" to [administraotr] ...
[root@localhost sbin]# rabbitmqctl list_users # 查看用户列表
Listing users ...
admin [administraotr]
guest [administrator]
# 删除用户命令
rabbitmqctl delete_user Username
# 修改用户的密码命令
rabbitmqctl change_password Username Newpassword
3.简单示例
发送端:
import pika
auth = pika.PlainCredentials('admin', 'admin')
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=auth))
channel = connection.channel()
channel.queue_declare(queue="hello")
channel.basic_publish(exchange='',
routing_key='hello',
body='Hello word!',)
print(" [x] Sent 'Hello word!'")
connection.close()
接收端:
import pika
credentials = pika.PlainCredentials('admin',"admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=credentials))
channel = connection.channel()
channel.queue_declare(queue='hello')
def my_callback(ch,method,properties,body):
print(" [x] Rev %r" % body)
channel.basic_consume(queue='hello',
on_message_callback=my_callback,
# auto_ack=True # 自动应答,应答后队列中的消息消失
)
print(" [*]")
channel.start_consuming()
# 当有多个消费者使用同一个消息队列(queue)时,生产者生产的消息是公平的依次分发
4.不同的消费者对消息的处理能力不同,假设A一秒处理十个消息,B十秒处理一个消息,按照消息的依次分发,消息会在B处堆积
channel.basic_qos(prefetch_count=1),#预载数,B的消息未处理完,不会再给它分发消息
5.消息持久化
生产者生产完消息就宕机了,而且此时也没有消费者消费消息,此时如有需要,就可以使队列及队列中的消息持久化,
在生产者恢复后依然存在
channel.queue_declare(queue='hello2', durable=True) # 队列持久化 在生产者和消费者中都要声明
#durable:耐用的、持久的
properties=pika.BasicProperties(
delivery_mode=2, # 消息持久化
)
6.消息订阅
消息的发送顺序 发送端 -> exchange(交换机) -> queue(队列)-> 接收端
交换机分配消息可以有不同的类型
fanout: 所有绑定到此exchange的queue都可以接收消息
direct: 通过routingKey和exchange决定的那个唯一的queue可以接收消息
topic: 所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息
fanout类型: # 你会收到每个主播的消息
发送端:
import pika
auth = pika.PlainCredentials('admin', 'admin')
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=auth))
channel = connection.channel()
# channel.queue_declare(queue="hello")
channel.exchange_declare(exchange="logs",
exchange_type="fanout",)
channel.basic_publish(exchange='logs',
# routing_key='hello',
routing_key='',
body='Hello word!',
# properties=pika.BasicProperties(delivery_mode=True),
)
print(" [x] Sent 'Hello word!'")
connection.close()
接收端:
import pika
credentials = pika.PlainCredentials('admin',"admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=credentials))
channel = connection.channel()
# channel.queue_declare(queue='hello')
channel.exchange_declare(exchange="logs",
exchange_type="fanout",
)
# 获取queue的名字
result = channel.queue_declare(queue='',exclusive=True)
queue_name = result.method.queue
# 将队列和exchange绑定
channel.queue_bind(
queue=queue_name,
exchange="logs",
)
def my_callback(ch,method,properties,body):
print(" [x] Rev %r" % body)
channel.basic_consume(queue=queue_name,
on_message_callback=my_callback,
auto_ack=True
)
print(" [*]")
channel.start_consuming()
direct: # 你可以指定收哪个主播的消息
发送端:
import pika
import sys
auth = pika.PlainCredentials('admin', 'admin')
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=auth))
channel = connection.channel()
# channel.queue_declare(queue="hello")
channel.exchange_declare(exchange="direct_logs",
exchange_type="direct",
#exchange_type="fanout",
)
severity = sys.argv[1] if len(sys.argv[1]) > 1 else "info"
message = 'direct_Hello word!'
channel.basic_publish(exchange='direct_logs',
# routing_key='hello',
routing_key=severity,
body=message,
# properties=pika.BasicProperties(delivery_mode=2),
)
print(" [x] Sent 'Hello word!'")
connection.close()
接收端:
import pika
import sys
credentials = pika.PlainCredentials('admin',"admin")
channel = connection.channel()
# channel.queue_declare(queue='hello')
channel.exchange_declare(exchange="direct_logs",
#exchange_type="fanout",
exchange_type="direct",
)
# 获取queue的名字
result = channel.queue_declare('',exclusive=True)
queue_name = result.method.queue
# 将队列和exchange绑定
severities = sys.argv[1:]
if not severities:
sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
sys.exit(1)
for severity in severities:
channel.queue_bind(
queue=queue_name,
exchange="direct_logs",
routing_key = severity
)
def my_callback(ch,method,properties,body):
print(" [x] Rev %r" % body)
channel.basic_consume(queue=queue_name,
on_message_callback=my_callback,
auto_ack=True,
)
print(" [*]")
channel.start_consuming()
topic # 可以接收指定话题的消息 '#'可以接收所有
发送端:
import pika
import sys
auth = pika.PlainCredentials('admin', 'admin')
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=auth))
channel = connection.channel()
# channel.queue_declare(queue="hello")
channel.exchange_declare(exchange="topic_logs",
exchange_type="topic",
#exchange_type="fanout",
)
severity = sys.argv[1] if len(sys.argv) > 1 else "info"
message = 'direct_Hello word!'
channel.basic_publish(exchange='topic_logs',
# routing_key='hello',
routing_key=severity,
body=message,
# properties=pika.BasicProperties(delivery_mode=2),
)
print(" [x] Sent 'Hello word!'")
connection.close()
接收端:
import pika
import sys
credentials = pika.PlainCredentials('admin',"admin")
channel = connection.channel()
# channel.queue_declare(queue='hello')
channel.exchange_declare(exchange="topic_logs",
#exchange_type="fanout",
exchange_type="topic",
)
# 获取queue的名字
result = channel.queue_declare('',exclusive=True)
queue_name = result.method.queue
# 将队列和exchange绑定
severities = sys.argv[1:]
if not severities:
sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
sys.exit(1)
for severity in severities:
channel.queue_bind(
queue=queue_name,
exchange="topic_logs",
routing_key = severity
)
def my_callback(ch,method,properties,body):
print(" [x] Rev %r" % body)
channel.basic_consume(queue=queue_name,
on_message_callback=my_callback,
auto_ack=True,
)
print(" [*]")
channel.start_consuming()
7.RPC 实现(Remote procedure call 远程过程调用):双向交流
客户端:
import pika
import sys
import uuid
class FibonacciRpcClient(object):
def __init__(self):
self.auth = pika.PlainCredentials('admin', 'admin')
self.channel = self.connection.channel()
result = self.channel.queue_declare('',exclusive=True)
self.callback_queue = result.method.queue
self.channel.basic_consume(
queue=self.callback_queue,
on_message_callback=self.on_response,
auto_ack=True
)
def on_response(self,ch,method,properties,body):
if self.corr_id == properties.correlation_id:
self.response = body
def call(self,n):
self.response = None
self.corr_id = str(uuid.uuid4())
self.channel.basic_publish(
exchange='',
routing_key="rpc_queue",
body=str(n),
properties=pika.BasicProperties(
reply_to = self.callback_queue,
correlation_id=self.corr_id,
)
)
while not self.response:
self.connection.process_data_events()
return int(self.response)
if __name__ == "__main__":
fibonacci_rpc = FibonacciRpcClient()
print(" [x] Requesting fib(7)")
response = fibonacci_rpc.call(8)
print(" [.] Got %r" % response)
服务端:
import pika
import sys,time
def fib(n):
"""计算指定位置的斐波那契数列"""
if n==0:
return 0
elif n == 1:
return 1
else:
return fib(n-2) + fib(n-1)
def on_response(ch,method,properties,body):
response = fib(int(body))
ch.basic_publish(
exchange='', # 把执行结果发回给客户端
routing_key = properties.reply_to, # 客户端要求返回想用的queue
# 返回客户端发过来的correction_id 为了让客户端验证消息一致性
properties = pika.BasicProperties(correlation_id=properties.correlation_id),
body = str(response)
)
ch.basic_ack(delivery_tag = method.delivery_tag)
if __name__ == "__main__":
credentials = pika.PlainCredentials('admin',"admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=credentials))
channel = connection.channel()
channel.queue_declare(queue='rpc_queue')
channel.basic_consume(queue='rpc_queue',
on_message_callback=on_response,
auto_ack=True,
)
print(" [*]")
channel.start_consuming()