Python-RabbitMQ
RabbitMQ:
RabbitMQ是一个AMQP实现,传统的messaging queue系统实现,基于Erlang。老牌MQ产品了。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量还在其次。
MQ全称为MessageQueue,消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBMWEBSPHEREMQ。
使用场景:
系统架构图:
![](https://images2015.cnblogs.com/blog/1139675/201707/1139675-20170717195148956-1409132524.jpg)
Client A & Client B:
Producer,数据发送方;一个Message有两个部分:
payload(有效载荷):顾名思义就是传输的数据。
label(标签):是exchange的名字或者说是一个tag,它描述了payload,而且RabbitMQ也是通过这个label来决定把这个Message发给哪个Consumer。AMQP仅仅描述了label,而RabbitMQ决定了如何使用这个label的规则。
Client 1 、2、3:
Consumer,数据接收方;
把queue比作是一个有名字的邮箱。当有Message到达某个邮箱后,RabbitMQ把它发送给它的某个订阅者即Consumer。当然可能会把同一个Message发送给很多的Consumer。在这个Message中,只有payload,label已经被删掉了。对于Consumer来说,它是不知道谁发送的这个信息的。就是协议本身不支持。但是当然了如果Producer发送的payload包含了Producer的信息就另当别论了。
RabbitMQ Server:
broker server,它不是运送食物的卡车,而是一种传输服务。他的角色就是维护一条从Producer到Consumer的路线,保证数据能够按照指定的方式进行传输。但是这个保证也不是100%的保证,但是对于普通的应用来说这已经足够了。当然对于商业系统来说,可以再做一层数据一致性的guard,就可以彻底保证系统的一致性。
Exchange:
从架构图可以看出,Producer Publish的Message进入了Exchange。接着通过“routing keys”, RabbitMQ会找到应该把这个Message放到哪个queue里。queue也是通过这个routing keys来做的绑定。
有三种类型的Exchanges:direct, fanout,topic。 每个实现了不同的路由算法(routing algorithm)。
· Direct exchange: 如果 routing key 匹配, 那么Message就会被传递到相应的queue中。其实在queue创建时,它会自动的以queue的名字作为routing key来绑定那个exchange。
· Fanout exchange: 会向响应的queue广播。
· Topic exchange: 对key进行模式匹配,比如ab*可以传递到所有ab*的queue。
Queue:
消息队列
Binding:
RabbitMQ中通过Binding将Exchange与Queue关联起来,这样RabbitMQ就知道如何正确地将消息路由到指定的Queue了。
在绑定(Binding)Exchange与Queue的同时, 一般会指定一个binding key;消费者将消息发送给Exchange时,一般会指定一个routing key;当binding key与routing key相匹配时,消息将会被路由到对应的Queue中。在绑定多个Queue到同一个Exchange的时候,这些Binding允许使用相同的binding key。
注:binding key 并不是在所有情况下都生效,它依赖于Exchange Type,比如fanout类型的Exchange就会无视binding key,而是将消息路由到所有绑定到该Exchange的Queue。
RoutingKey:
生产者在将消息发送给Exchange的时候,一般会指定一个routing key,来指定这个消息的路由规则,而这个routing key需要与Exchange Type及binding key联合使用才能最终生效。在Exchange Type与binding key固定的情况下(在正常使用时一般这些内容都是固定配置好的),我们的生产者就可以在发送消息给Exchange时,通过指定routing key来决定消息流向哪里。RabbitMQ为routing key设定的长度限制为255 bytes。
Connection:
就是一个TCP的连接。Producer和Consumer都是通过TCP连接到RabbitMQ Server的。以后我们可以看到,程序的起始处就是建立这个TCP连接。
Channels:
虚拟连接。它建立在上述的TCP连接中。数据流动都是在Channel中进行的。也就是说,一般情况是程序起始建立TCP连接,第二步就是建立这个Channel。
为什么使用Channel,而不是直接使用TCP连接?
对于OS来说,建立和关闭TCP连接是有代价的,频繁的建立关闭TCP连接对于系统的性能有很大的影响,而且TCP的连接数也有限制,这也限制了系统处理高并发的能力。但是,在TCP连接中建立Channel是没有上述代价的。对于Producer或者Consumer来说,可以并发的使用多个Channel进行Publish或者Receive。有实验表明,1s的数据可以Publish10K的数据包。当然对于不同的硬件环境,不同的数据包大小这个数据肯定不一样,但是我只想说明,对于普通的Consumer或者Producer来说,这已经足够了。如果不够用,你考虑的应该是如何细化split你的设计。
安装RabbitMQ:
Linux:
安装配置epel源 $ rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm 安装erlang $ yum -y install erlang 安装RabbitMQ $ yum -y install rabbitmq-server
注意:service rabbitmq-server start/stop
mac:MAC OS X 安装、配置、启动 rabbitMQ
安装API
pip install pika or easy_install pika or
源码
使用RibbitMQ:
生产者(Producer):
1 import pika 2 # 创建一个connection 3 connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) 4 # 创建一个管道 5 channel = connection.channel() 6 # 声明一个Queue,名字为'hello' 7 channel.queue_declare(queue = 'hello') 8 9 while True: 10 stdin_str = input('>>>') 11 # 发送数据 12 # exchange:... 13 # routing_key : 队列名字 14 # body: 所发送的数据 15 channel.basic_publish(exchange = '', 16 routing_key = 'hello', 17 body = stdin_str) 18 print(" [x] Sent '{}'".format(stdin_str)) 19 connection.close()
控制台输入:
>>>Hello World
[x] Sent 'Hello World'
>>>人生苦短,快用python
[x] Sent '人生苦短,快用python'
>>>
消费者(Consumer):
1 import pika 2 connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) 3 #创建一个管道 4 channel = connection.channel() 5 #声明QUEUE 6 channel.queue_declare(queue='hello') #在Producer中也有一个一样的声明,原注释的意思是有一次就可以的,但是,我们却不知道是Consumer先声明还是Purducer先声明。 7 #回调函数 8 def callback(ch,method,properties,body): 9 print(" [x] Received %r" % body.decode()) 10 11 channel.basic_consume(callback, 12 queue = 'hello', 13 no_ack = True) 14 # no_ack 默认为False 代表着如果Consumer如果拿到数据,但是没有处理完数据因意外挂了的话,所拿到的数据会恢复到"hello"队列中。 15 16 print(' [*] Waiting for messages. To exit press CTRL+C') 17 channel.start_consuming()
控制台输出:
[*] Waiting for messages. To exit press CTRL+C
[x] Received 'Hello World'
[x] Received '人生苦短,快用python'