ActiveMQ consumer按顺序处理消息
http://activemq.apache.org/exclusive-consumer.html
producer发送消息是有先后顺序的,这种顺序保持到了broker中。如果希望消息按顺序被消费掉,则应该把消息投送给单独一个consumer。如果队列只有一个consumer,那就很ok了,broker没有选择。但是,一旦唯一的consumer挂了,会造成服务不可用。因此出现了exclusive consumer,配置如下:
new ActiveMQQueue("TEST.QUEUE?consumer.exclusive=true");
如果有2个consumer都是这样配置的,broker只会把队列消息发送给其中一个consumer,如果这个consumer挂掉了,broker会把消息推送给另外的consumer,这样就保证了按顺序消费消息。
那么,ActiveMQ是怎样实现这种逻辑的呢?
org.apache.activemq.broker.region.Queue中维持了一个consumer列表,分发消息的时候,会去遍历列表,在队列中靠前的consumer会优先被分发消息。
// org.apache.activemq.broker.region.Queue
// 该方法把消息分发给consumer,PendingList是消息列表
private PendingList doActualDispatch(PendingList list) throws Exception {
//消费者列表
List<Subscription> consumers;
consumersLock.writeLock().lock(); try {
if (this.consumers.isEmpty()) { // 消费者为空,直接返回
// slave dispatch happens in processDispatchNotification
return list;
}
consumers = new ArrayList<Subscription>(this.consumers);
} finally {
consumersLock.writeLock().unlock();
} // 初始化fullConsumers
Set<Subscription> fullConsumers = new HashSet<Subscription>(this.consumers.size());
// 遍历消息列表
for (Iterator<MessageReference> iterator = list.iterator(); iterator.hasNext();) {
MessageReference node = iterator.next();
Subscription target = null;
// 遍历消费者
for (Subscription s : consumers) {
if (s instanceof QueueBrowserSubscription) {
continue;
} if (!fullConsumers.contains(s)) {
if (!s.isFull()) { //消费者not full
//满足以下条件可以分发:
//1. 符合QueueDispatchSelector的规则
//2. 消息的group属性和消费者匹配
//3. 消息没有被应答
if (dispatchSelector.canSelect(s, node) && assignMessageGroup(s, (QueueMessageReference)node) && !((QueueMessageReference) node).isAcked() ) {
// Dispatch it.
s.add(node);
LOG.trace("assigned {} to consumer {}", node.getMessageId(), s.getConsumerInfo().getConsumerId());
//从发送列表中删除,消息并不会真的删除
iterator.remove();
//设置消息的target
target = s;
break;
}
} else {
// no further dispatch of list to a full consumer to
// avoid out of order message receipt
fullConsumers.add(s);
LOG.trace("Subscription full {}", s);
}
}
} if (target == null && node.isDropped()) {
iterator.remove();
} // return if there are no consumers or all consumers are full
if (target == null && consumers.size() == fullConsumers.size()) {
return list;
} // 在列表中调整consumer的顺序
// 如果是exlusive consumer,则不会进分支,那么exlusive consumer的顺序不会变
// 一旦进入这个分支,当前的consumer会被放到最后
// If it got dispatched, rotate the consumer list to get round robin
// distribution.
if (target != null && !strictOrderDispatch && consumers.size() > 1
&& !dispatchSelector.isExclusiveConsumer(target)) {
consumersLock.writeLock().lock();
try {
// 先从this.consumers中删除当前consumer
if (removeFromConsumerList(target)) {
// 然后把当前consumer添加到this.consumers中
addToConsumerList(target);
consumers = new ArrayList<Subscription>(this.consumers);
}
} finally {
consumersLock.writeLock().unlock();
}
}
} return list;
} private void addToConsumerList(Subscription sub) {
if (useConsumerPriority) {
consumers.add(sub);
Collections.sort(consumers, orderedCompare);
} else {
consumers.add(sub);
}
}
QueueDispatchSelector保证:如果配置了 exclusive consumer,一定会把消息分发给 exclusive consumer。
// org.apache.activemq.broker.region.QueueDispatchSelector
public boolean canSelect(Subscription subscription,
MessageReference m) throws Exception { boolean result = super.canDispatch(subscription, m);
if (result && !subscription.isBrowser()) {
// 没有配置exclusiveConsumer,或者exclusiveConsumer就是当前消费者
result = exclusiveConsumer == null || exclusiveConsumer == subscription;
}
return result;
}
在添加消费者的时候,设置exclusive consumer:
//org.apache.activemq.broker.region.Queue
public void addSubscription(ConnectionContext context, Subscription sub) throws Exception {
LOG.debug("{} add sub: {}, dequeues: {}, dispatched: {}, inflight: {}", new Object[]{ getActiveMQDestination().getQualifiedName(), getDestinationStatistics().getDequeues().getCount(), getDestinationStatistics().getDispatched().getCount(), getDestinationStatistics().getInflight().getCount() }); super.addSubscription(context, sub);
// synchronize with dispatch method so that no new messages are sent
// while setting up a subscription. avoid out of order messages,
// duplicates, etc.
pagedInPendingDispatchLock.writeLock().lock();
try { sub.add(context, this); // needs to be synchronized - so no contention with dispatching
// consumersLock.
consumersLock.writeLock().lock();
try {
// set a flag if this is a first consumer
if (consumers.size() == 0) {
firstConsumer = true;
if (consumersBeforeDispatchStarts != 0) {
consumersBeforeStartsLatch = new CountDownLatch(consumersBeforeDispatchStarts - 1);
}
} else {
if (consumersBeforeStartsLatch != null) {
consumersBeforeStartsLatch.countDown();
}
} addToConsumerList(sub);
//设置 exclusive consumer
if (sub.getConsumerInfo().isExclusive() || isAllConsumersExclusiveByDefault()) {
Subscription exclusiveConsumer = dispatchSelector.getExclusiveConsumer();
if (exclusiveConsumer == null) {
// exclusiveConsumer为空
exclusiveConsumer = sub;
} else if (sub.getConsumerInfo().getPriority() == Byte.MAX_VALUE ||
sub.getConsumerInfo().getPriority() > exclusiveConsumer.getConsumerInfo().getPriority()) {
//如果当前订阅者的优先级比已有的exclusiveConsumer高
exclusiveConsumer = sub;
}
dispatchSelector.setExclusiveConsumer(exclusiveConsumer);
}
} finally {
consumersLock.writeLock().unlock();
} if (sub instanceof QueueBrowserSubscription) {
// tee up for dispatch in next iterate
QueueBrowserSubscription browserSubscription = (QueueBrowserSubscription) sub;
BrowserDispatch browserDispatch = new BrowserDispatch(browserSubscription);
browserDispatches.add(browserDispatch);
} if (!this.optimizedDispatch) {
wakeup();
}
} finally {
pagedInPendingDispatchLock.writeLock().unlock();
}
if (this.optimizedDispatch) {
// Outside of dispatchLock() to maintain the lock hierarchy of
// iteratingMutex -> dispatchLock. - see
// https://issues.apache.org/activemq/browse/AMQ-1878
wakeup();
}
}
ActiveMQ consumer按顺序处理消息的更多相关文章
- ActiveMQ笔记(6):消息延时投递
在开发业务系统时,某些业务场景需要消息定时发送或延时发送(类似:飞信的短信定时发送需求),这时候就需要用到activemq的消息延时投递,详细的文档可参考官网说明,本文只介绍二种常用的用法: 注:本文 ...
- ActiveMQ的几种消息持久化机制
为了避免意外宕机以后丢失信息,需要做到重启后可以恢复消息队列,消息系统一般都会采用持久化机制. ActiveMQ的消息持久化机制有JDBC,AMQ,KahaDB和LevelDB,无论使用哪种持久化方式 ...
- spring+activemq中多个consumer同时处理消息时遇到的性能问题
最近在做数据对接的工作,用到了activemq,我需要从activemq中接收消息并处理,但是我处理数据的步骤稍微复杂,渐渐的消息队列中堆的数据越来越多,就想到了我这边多开几个线程来处理消息. 可是会 ...
- JMS学习(八)-ActiveMQ Consumer 使用 push 还是 pull 获取消息
ActiveMQ是一个消息中间件,对于消费者而言有两种方式从消息中间件获取消息: ①Push方式:由消息中间件主动地将消息推送给消费者:②Pull方式:由消费者主动向消息中间件拉取消息.看一段官网对P ...
- JMS学习十一(ActiveMQ Consumer高级特性之独有消费者(Exclusive Consumer))
一.简介 Queue中的消息是按照顺序被分发到consumers的.然而,当你有多个consumers同时从相同的queue中提取消息时, 你将失去这个保证.因为这些消息是被多个线程并发的处理.有的时 ...
- activemq安装与简单消息发送接收实例
安装环境:Activemq5.11.1, jdk1.7(activemq5.11.1版本需要jdk升级到1.7),虚拟机: 192.168.147.131 [root@localhost softwa ...
- 基于ActiveMQ的点对点收发消息
ActiveMQ是apache的一个开源消息引擎.可以作为即通引擎或者消息中间件引擎. 准备 下载ActiveMQ http://activemq.apache.org/download.html 进 ...
- 学习ActiveMQ(五):activemq的五种消息类型和三种监听器类型
一.前面我们一直发送的是字符串类型,其实activemq一共支持五种消息类型: 1.String消息类型:发送者:消费者: 1.String消息类型:发送者:消费者: 1.String消息类型:发送者 ...
- ActiveMQ(4)---ActiveMQ原理分析之消息消费
消费端消费消息的原理 我们通过上一节课的讲解,知道有两种方法可以接收消息,一种是使用同步阻塞的MessageConsumer#receive方法.另一种是使用消息监听器MessageListener. ...
随机推荐
- 【Ruby】【变量】
知识点[Ruby 中$开头的全局变量.内部变量.隐藏变量介绍] Ruby 中充满了一系列的隐藏变量,我们可以从这些预定义的全局变量中获取一些有意思的信息. 全局进程变量 $$ 表示当前运行的 ruby ...
- 【Python】【有趣的模块】【sys&time&os】
[模块] sys.path.append('C:/Users/wangxue1/PycharmProjects/selenium2TestOne') 然后就可以直接import 这个路径下的模块了 [ ...
- Youtube-dl 配置 使用方法 + 配合aria2 多线程 下载 + 配合 ffmpeg 自动合并分段视频
首先介绍软件,Youtube-dl可以下载网页的视频,功能很强大. 但遇到分段视频不能合并,遇到视频音频分开播放的网站也没办法合并视频音频,所以 需要用ffmpeg来配合的合并视频.合并过程是无损的, ...
- 基于 Python 和 Pandas 的数据分析(3) --- 输入/输出 基础
这一节, 我们要讨论 Pandas 的输入与输出, 并且应用在现实的实际例子中. 为了得到大量的数据, 向大家推荐一个网站 Quandl. Quandl 有很多免费和付费的资源. 这个网站最大的优势在 ...
- Ubuntu18.04的网络配置
网卡与DNS配置 1)打开命令窗口(右键单机桌面选择Open Terminal或者用快捷键Ctrl+Alt+T打开终端),输入ip a查看自己的网卡编号 2)输入命令sudo vim /etc/net ...
- 哈密顿绕行世界问题 HDU 2181
题意让你先输20行数表示20个城市及所相邻的三个城市(行数就是该城市),然后给你一个数,从这个(给的数就表示城市)城市出发走遍所有城市一次回到出发的城市:看着复杂,仔细想想是个不算太难的深搜题,主要你 ...
- 《剑指offer》第六十七题(把字符串转换成整数)
// 面试题67:把字符串转换成整数 // 题目:请你写一个函数StrToInt,实现把字符串转换成整数这个功能.当然,不 // 能使用atoi或者其他类似的库函数. #include <ios ...
- 学习笔记25—python基本运算法则
1.矩阵的点乘: a*b, 矩阵乘法:dot(a*b),矩阵的次方:a**num (num = 2,表示2次)2.数组的并集,交集: >>> a = [1,2,3] >> ...
- 最短路径遍历所有的节点 Shortest Path Visiting All Nodes
2018-10-06 22:04:38 问题描述: 问题求解: 本题要求是求遍历所有节点的最短路径,由于本题中是没有要求一个节点只能访问一次的,也就是说可以访问一个节点多次,但是如果表征两次节点状态呢 ...
- Oracle DB , 计算各个用户/schema 的磁盘占用空间
http://www.dba-oracle.com/t_find_size_schema.htm Question: How do I find the size of a schema in my ...