Rabbit MQ
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()