Message Queue学习笔记 --- 消息队列入门

来源:互联网 发布:tomcat 端口 ipv6 编辑:程序博客网 时间:2024/06/06 14:22

什么是rabbitMQ

rabbitMQ是一款基于AMQP协议的消息中间件,它能够在应用之间提供可靠的消息传输。在易用性,扩展性,高可用性上表现优秀。而且使用消息中间件利于应用之间的解耦,生产者(客户端)无需知道消费者(服务端)的存在。而且两端可以使用不同的语言编写,大大提供了灵活性。

rabbitMQ工作原理

首先我们得先理解rabbitMQ里的一些基本定义,主要如下:

exchange: producer只能将消息发送给exchange。而exchange负责将消息发送到queues。Exchange必须准确的知道怎么处理它接受到的消息,是被发送到一个特定的queue还是许多quenes,还是被抛弃,这些规则则是通过exchange type来定义。主要的type有direct,topic,headers,fanout。具体针对不同的场景使用不同的type。

queue: 消息队列,消息的载体。接收来自exchange的消息,然后再由consumer取出。exchange和queue是可以一对多的,它们通过routingKey来绑定。

Producer:生产者,消息的来源,消息必须发送给exchange。而不是直接给queue

Consumer:消费者,直接从queue中获取消息进行消费,而不是从exchange。

从以上可以看出Rabbitmq工作原理大致就是producer把一条消息发送给exchange。rabbitMQ根据routingKey负责将消息从exchange发送到对应绑定的queue中去,这是由rabbitMQ负责做的。而consumer只需从queue获取消息即可。

大致流程如下:



rabbitMQ工作模型

下面通过几个列子来详细说明一下如何使用rabbitmq

简单发送模型

在rabbit MQ里消息永远不能被直接发送到queue。这里我们通过提供一个空字符串来使用默认的exchange。这个exchange是特殊的,它可以根据routingKey把消息发送给指定的queue。所以我们的设计看起来如下所示



Sender:

# -*- coding:utf-8 -*-import pikaconnection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))# get channelchannel = connection.channel()# declare queuechannel.queue_declare(queue='hello')# send messagechannel.basic_publish(exchange='', routing_key = 'hello', body = 'hello world!')print " [x] Sent 'Hello World'"connection.close()


Received:

# -*- coding:utf-8 -*-import pikaconnection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))channel = connection.channel()channel.queue_declare(queue='hello')# callback functiondef callback(ch, method, properties, body):    print(" [] Received %r"% body)# consumerchannel,basic_consume(callback, queue="hello", no_ack=True)channel.start_consuming()

工作队列模型

这种模式常常用来处理耗资源耗时间的任务在多个workers中,主要是为了避免立即去处理一个耗时的任务而等待它的完成。代替的做法是一个稍后去处理这个任务,让一个worker process 在后台处理这个任务。当有许多workers的时候,消息将会以轮询的方式被workers获取。模型如下




这里就会有一个问题,如果consumer在执行任务时需要花费一些时间,这个时候如果突然挂了,消息还没有被完成,消息岂不是丢失了,为了不让消息丢失,rabbitmq提供了消息确认机制,consumer在接收到,执行完消息后会发送一个ack给rabbitmq告诉它可以从queue中移除消息了。如果没收到ack。Rabbitmq会重新发送此条消息,如果有其他的consumer在线,将会接收并消费这条消息。消息确认机制是默认打开的。如果想关闭它只需要设置no_ack=true。在此处我们不需要设置。默认如下就行

除了consumer之外我们还得确保rabbitMQ挂了之后消息不被丢失。这里我们就需要确保队列queue和消息messages都得是持久化的。

队列的持久话需要设置durable属性

消息的持久话则是通过delivery_mode属性,设置值为2即可

还有一个属性相对比较重要,它可以保证consumer确认消费完一条消息之后再去获取下一条消息。如果consumer正在忙碌的状态,消息将会被分发到下一个不是很忙的consumer。

producer

# -*- coding:utf-8- -*-import pika# durablechannel.queue_declare(queue = 'task_queue', durable = True)message = "Hello World"channel.bash_publish(exchange='', routing_key = 'task_queue', body=message, properties = pika.BasicProperties(delivery_mode = 2, ))print " [x] Sent %r"%message

consumer

# -*- coding:utf-8 -*-import pikaimport timechannel.queue_declare(queue='task_queue',durable=True)def callback(ch, method,properties, body):    print("Received %r"%body)    time.sleep(10)    ch.basic_ack(delevery_tag = method.delivery_tag)# prefetch_countchannel.basic_qos(prefetch_count = 1)channel.basic_consume(callback, queue='task_queue')channel.stat_consuming()

广播模型

在前面2个示例我们都适用默认的exchange。这里我们将自己定义一个exchange。并设置type为fanout。它可以将消息广播给绑定的每一个queue。而不再是某一个queue。我们在此创建一个叫logs的exchange



producer


# -*- coding:utf-8 -*-channel.exchange_declare(exchange = 'logs', type = 'fanout')message = 'Hello World!'channel.basic_publish(exchange = 'logs', routing_key = '', body = message)print(" [x] Sent %r " % message)

consumer

# -*-coding:utf-8 -*-channel.exchange_declare(exchange='logs', type = 'fanout')result = channel.queue_declare(exclusive = True)queue_name = result.method.queuechannel.queue_bind(exchange = 'logs', queue = queue_name)def callback(ch, method, properties, body):    print(" [x] %r"%body)channel.basic_consume(callback, queue = queue_name, no_ack = True)channel.start_consuming()

direct模型

在上个模型中,消息被发送给所有的消费者,而在这一部分我们将通过路由的方式使exchange通过定义的路由方式将消息发送给队列。所以我们需要在绑定exchange和queue的时候指定routing_key字段,注意这里的routing_key不是basic_publish中的routing_key。


这里我们将使用type为direct的exchange。这种路由方式exchange将消息通过绑定的routing_key发送到指定的队列。而且exchange可以通过多个routing_key把消息发送给同一个queue

通过下面这张图我们来分析一下




在上面的图中,我们可以看出type为direct的exchange X 绑定了2个队列。队列Q1与关联路由orange。队列Q2关联路由black和green。所以一个带有路由健orange消息将被exchange发送给队列Q1。而带有路由健black或者green的消息将被发送给队列Q2。

我们还是通过修改前面的日志系统,来展示direct类型的exchange如何工作,如下图




Topic模型

这种模型是最灵活的,相比较于direct的完全匹配和fanout的广播。Topic可以用类似正则的手法更好的匹配来满足我们的应用。下面我们首先了解一下topic类型的exchange

topic类型的routing_key不可以是随意的单词,它必须是一系列的单词组合,中间以逗号隔开,譬如“quick.orange.rabbit”这个样子。发送消息的routing_key必须匹配上绑定到队列的routing_key。消息才会被发送。此外还有个重要的地方要说明,在如下代码处绑定的routing_key种可以有*和#2种字符

它们代表的意义如下

*(星号) 可以匹配任意一个单词

#(井号) 可以匹配0到多个单词

我们通过下图来解释一下



Q1匹配3个单词中间为orange的routing_key ,而Q2可以匹配3个单词最后一个单词为rabbit和第一个单词为lazy后面可以有多个单词的routing_key


RPC应用模型

当我们需要在远程服务器上执行一个方法并等待它的结果的时候,我们将这种模式称为RPC。下面我们用rabbitMQ建立一个RPC系统

在rabbit MQ中为了能让client收到server端的response message。需要定义一个callback queue ,就像下面这样不过现在有一个问题,就是每次请求都会创建一个callback queue .这样的效率是极其低下的。幸运的是我们可以通过correlation_id为每一个client创建一个单独的callback queue。通过指定correlation_id我们可以知道callback queue中的消息属于哪个client。要做到这样只需client每次发送请求时带上这唯一的correlation_id。然后当我们从callback queue中收到消息时,我们能基于 correlation_id 匹配上我们的消息。匹配不上的消息将被丢弃,看上去就像下图这样



总结一下流程如下

  1. client发起请求,请求中带有2个参数reply_to和correlation_id
  2. 请求发往rpc_queue
  3. server获取到rpc_queue中的消息,处理完毕后,将结果发往reply_to指定的callback queue
  4. client 获取到callback queue中的消息,匹配correlation_id,如果匹配就获取,不匹配就丢弃.


0 0
原创粉丝点击