Python-RabbitMQ

RabbitMQ:

  RabbitMQ是一个AMQP实现,传统的messaging queue系统实现,基于Erlang。老牌MQ产品了。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量还在其次。

  MQ全称为MessageQueue,消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBMWEBSPHEREMQ。

使用场景:

  消息队列的主要特点是异步处理,主要目的是减少请求响应时间和解耦。所以主要的使用场景就是将比较耗时而且不需要即时(同步)返回结果的操作作为消息放入消息队列。同时由于使用了消息队列,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦和。

系统架构图:

 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 Typebinding 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你的设计。

出处:RibbitMQ从入门到精通

 

安装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'
posted @ 2017-07-17 23:09  LeeeetMe  阅读(174)  评论(0编辑  收藏  举报