RabbitMQ > 核心概念

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012006689/article/details/48497901

参考:http://www.rabbitmq.com/getstarted.html
对应示例代码:https://github.com/tonyxugb/rabbitmqexercise

Exchange

producer只能发送消息到exchange(图中X),exchange的一侧用来接收源自producer的消息,另一侧把消息推送到队列中。如下图所示。
这里写图片描述

不同类型的Exchange处理消息的方式不同。Exchange有四种类型:direct、topic、headers、fanout。

下面介绍各类型exchange处理消息的方式。介绍之前,为了避免混淆,我们把binding的routingKey改称为bindingKey,即Java Client queueBind()方法的第三个参数:
这里写图片描述

fanout: 顾名思义,不顾及任何routingKey,把消息发送给该exchange所知道的所有queue,Just Mindless BroadCasting!

direct:路由规则是消息的routingKey和bindingKey做match,命中就会进入,如下图:
这里写图片描述

如图,X代表direct类型的exchange,绑定到Queue Q1和Q2上, X和Q1之间的使用orange作为binding Key,X和Q2之间有两个binding,分别使用black和green作为binding key。

此时,如果使用orange作为routing Key来publish一个消息,将会进入Q1;如果用black或者green来作为routing Key来publish消息,就会进入Q2;其他不匹配的将会被discarded。

topic:如下图所示
这里写图片描述

创建三个binding: X和Q1之间之间 binding key是 .orange.; X和Q2之间有两个binding,binding key分别是 ..rabbit和lazy.#;

在binding key中有两个通配符:
1) * : 代表 1个word;
2) # : 代表 0或多个word;

此时,如果:
1)Message的routing key是quick.orange.fox,命中.orange.,X把Message传递给Q1;
2)Message routing key是quick.black.rabbit,命中..rabbit,X把Message传递给Q2;
3)Message routing key是lazy.red.dog.cat.elephant,命中lazy.#,进入Q2;
4)routing key为quick.yellow.dog,均不命中,被discarded。

利用*和#,topic exchange有时表现的像其他exchange:

如果binding key有且仅有#,那么就会忽略message的routing key,queue接收所有的消息,并发给与该Queue相连的所有consumer,这类似于fanout;

如果binding key没有*和#,那么topic exchange表现的和direct exchange相同。

通过命令rabbitmqctl list_exchanges可以查看当前的exchange列表:

[root@localhost ~]# rabbitmqctl list_exchanges
Listing exchanges ...
     direct
amq.direct     direct
amq.fanout     fanout
amq.headers     headers
amq.match     headers
amq.rabbitmq.log     topic
amq.rabbitmq.trace     topic
amq.topic     topic

第一列是exchange的名称,第二列是类型。第一行是默认的exchange,名称是空白的,类型是direct。 当发布消息时exchange设置为空字符串,如下:
channel .basicPublish(“”, QUEUE_NAME , null, message .getBytes(“UTF-8”));
则使用默认的这个direct类型的exchange。所有的queue都会和这个默认的exchange产生一个binding,binding的routing key就是queue的名称。

Bindings

这里写图片描述

如上图所示,exchange和queue之间的关系称为binding。Java Client API使用下面这种方式定义binding:
channel.exchangeDeclare( “logs”, “fanout”);
channel.queueBind(queueName,”logs”,”“);
queueBind函数的第三个参数就是routingKey。

下面的几个概念和Work Queues有关,如下图所示:
这里写图片描述

Round-robin dispatching

如上图所示,queue会把消息依次循环发送给C1和C2。使用work queue的优点之一是可以轻易的使任务并行执行。当我们积累了一堆的任务时,只管添加workers就解决了任务堆积的问题。默认情况下,RabbitMQ依次把消息发送到下一个consumer,通常每个consumer会得到相同数量的Message。这种分发消息的方式成为Round-robin。

Message acknowledgment

假设一旦queue把消息传送给consumer,queue将立即删除该消息。如果consumer自己crash掉了,它正在处理的消息就丢失了,rabbitmq已发送到该consumer但尚未开始处理的消息也丢失了。 为了避免消息的丢失,RabbitMQ提供了Message acknowledgment机制,consumer成功处理完消息后会向rabbitmq发送acknowledgment,rabbitmq接收到ack之后知道消息已被正确处理了,就会删除该消息。

如果consumer因为意外die掉而未发出acknowledgment,rabbitmq不存在消息超时这一说法,当consumer crash掉后,它与rabbitmq之间的连接断开了,只有当与consumer之间的连接断开了,queue才会认为消息没有被正确处理,才会把消息重新发送到下一个consumer。即便consumer处理了很长时间,只要queue与consumer之间的连接未断开,queue依然认为OK,不会重发到下一consumer。
默认情况下,message acknowledgment是开启的。

Message durability
上面解决了即便consumer crash掉任务仍然不会丢失的问题。如果RabbitMQ server crash掉之后,消息依然会丢失。为了解决这个问题,需要两件事:
a.创建持久化的队列,这样即便是rabbit重启,queue仍然存在。

boolean durable = true;
channel.queueDeclare("hello", durable, false, false, null);

RabbitMQ不允许对一个已存在的queue设置不同的参数,所以持久化属性只能在第一次声明queue时设置。
b.发送持久化的消息

import com.rabbitmq.client.MessageProperties;

channel.basicPublish("", "task_queue", 
            MessageProperties.PERSISTENT_TEXT_PLAIN,
            message.getBytes());

Fair dispatch

这里写图片描述

假设如上图,进行round-robin dispatch时,奇数的任务都很complex,偶数的任务都很easy,queue意识不到这一点,依然round-robin,结果是:C1(接收奇数)一直很忙,C2一直很闲。显然,queue的分配方式并不合理。

出现这种问题,原因是queue从不看consumer是否返回acknowledgment,只是傻傻的dispatch。
使用basicQos(1)设置queue每次只可以向consumer发送一条Message,换句话说,只有在接收到consumer返回的上一条消息的acknowledgment之后才能向该consumer发送新的Message。

int prefetchCount = 1;
channel.basicQos(prefetchCount);

猜你喜欢

转载自blog.csdn.net/u012006689/article/details/48497901