ActiveMQ producer不断发送消息,会导致broker内存耗尽吗?
http://activemq.apache.org/my-producer-blocks.html 回答了这个问题:
ActiveMQ 5.x 支持Message Cursors,它默认把消息从内存移出到磁盘上。所以,只有在分配给message store的磁盘空间被用完了,才会出现问题。分配的磁盘空间是可以配置的。
http://activemq.apache.org/message-cursors.html 有一张描述store based cursor的图:
上图中的元素对应的数据结构如下:
public class Queue extends BaseDestination implements Task, UsageListener {
// StoreQueueCursor
protected PendingMessageCursor messages;
}
public class StoreQueueCursor extends AbstractPendingMessageCursor { private static final Logger LOG = LoggerFactory.getLogger(StoreQueueCursor.class);
private final Broker broker;
private int pendingCount;
private final Queue queue;
// 非持久化 pending cursor,真实类型是 FilePendingMessageCursor
private PendingMessageCursor nonPersistent;
// 持久化 pending cursor,真实类型是 QueueStorePrefetch
private final QueueStorePrefetch persistent;
private boolean started;
private PendingMessageCursor currentCursor;
}
class QueueStorePrefetch extends AbstractStoreCursor {
private static final Logger LOG = LoggerFactory.getLogger(QueueStorePrefetch.class);
// Message Store
private final MessageStore store;
private final Broker broker;
}
调试时,message store 的类型为 KahaDBTransactionStore$1
producer发送消息后,broker的调用栈:
org.apache.activemq.broker.region.Queue.doMessageSend
void doMessageSend(final ProducerBrokerExchange producerExchange, final Message message) throws IOException,
Exception {
final ConnectionContext context = producerExchange.getConnectionContext();
ListenableFuture<Object> result = null;
boolean needsOrderingWithTransactions = context.isInTransaction(); producerExchange.incrementSend();
checkUsage(context, producerExchange, message);
sendLock.lockInterruptibly();
try {
// store类型是KahaDBTransactionStore$1
// 持久化消息,先存入kahadb,也就是图中的message store
if (store != null && message.isPersistent()) {
try {
message.getMessageId().setBrokerSequenceId(getDestinationSequenceId());
if (messages.isCacheEnabled()) {
result = store.asyncAddQueueMessage(context, message, isOptimizeStorage());
result.addListener(new PendingMarshalUsageTracker(message));
} else {
store.addMessage(context, message);
}
if (isReduceMemoryFootprint()) {
message.clearMarshalledState();
}
} catch (Exception e) {
// we may have a store in inconsistent state, so reset the cursor
// before restarting normal broker operations
resetNeeded = true;
throw e;
}
}
// did a transaction commit beat us to the index?
synchronized (orderIndexUpdates) {
needsOrderingWithTransactions |= !orderIndexUpdates.isEmpty();
}
if (needsOrderingWithTransactions ) {
// If this is a transacted message.. increase the usage now so that
// a big TX does not blow up
// our memory. This increment is decremented once the tx finishes..
message.incrementReferenceCount(); registerSendSync(message, context);
} else { // 普通的非事务消息,加到 pending list 中
// Add to the pending list, this takes care of incrementing the
// usage manager.
sendMessage(message);
}
} finally {
sendLock.unlock();
}
if (!needsOrderingWithTransactions) {
messageSent(context, message);
}
if (result != null && message.isResponseRequired() && !result.isCancelled()) {
try {
result.get();
} catch (CancellationException e) {
// ignore - the task has been cancelled if the message
// has already been deleted
}
}
}
StoreQueueCursor.addMessageLast
public synchronized void addMessageLast(MessageReference node) throws Exception {
if (node != null) {
Message msg = node.getMessage();
if (started) {
pendingCount++;
if (!msg.isPersistent()) {
//对应图中的 non-persistent pending cursor
nonPersistent.addMessageLast(node);
}
}
if (msg.isPersistent()) {
// 对应图中的 persistent pending cursor
persistent.addMessageLast(node);
}
}
}
store based cursor图中的数据流,基本梳理清楚,还差non-persistent pending curosr 到 tmeporary files的数据流。
//org.apache.activemq.broker.region.cursors.FilePendingMessageCursor
@Override
public synchronized void addMessageLast(MessageReference node) throws Exception {
tryAddMessageLast(node, 0);
} @Override
public synchronized boolean tryAddMessageLast(MessageReference node, long maxWaitTime) throws Exception {
if (!node.isExpired()) {
try {
regionDestination = (Destination) node.getMessage().getRegionDestination();
if (isDiskListEmpty()) {
if (hasSpace() || this.store == null) {
memoryList.addMessageLast(node);
node.incrementReferenceCount();
setCacheEnabled(true);
return true;
}
}
if (!hasSpace()) {
if (isDiskListEmpty()) {
expireOldMessages();
if (hasSpace()) {
memoryList.addMessageLast(node);
node.incrementReferenceCount();
return true;
} else {
flushToDisk();
}
}
}
if (systemUsage.getTempUsage().waitForSpace(maxWaitTime)) {
ByteSequence bs = getByteSequence(node.getMessage());
//把消息写到磁盘
getDiskList().addLast(node.getMessageId().toString(), bs);
return true;
}
return false; } catch (Exception e) {
LOG.error("Caught an Exception adding a message: {} first to FilePendingMessageCursor ", node, e);
throw new RuntimeException(e);
}
} else {
discardExpiredMessage(node);
}
//message expired
return true;
} //org.apache.activemq.broker.region.cursors.AbstractPendingMessageCursor
// 判断内存使用量是否超过70%
public boolean hasSpace() {
return systemUsage != null ? (!systemUsage.getMemoryUsage().isFull(memoryUsageHighWaterMark)) : true;
}
在内存使用发送变化时,会触发flush:
FilePendingMessageCursor.onUsageChanged(Usage usage, int oldPercentUsage, int newPercentUsage)
public void onUsageChanged(Usage usage, int oldPercentUsage, int newPercentUsage) {
// 内存使用超过70%,会把消息刷到磁盘上,后面的hasSpace()方法也是以此判断
if (newPercentUsage >= getMemoryUsageHighWaterMark()) {
synchronized (this) {
if (!flushRequired && size() != 0) {
flushRequired =true;
if (!iterating) {
expireOldMessages();
if (!hasSpace()) {
flushToDisk();
flushRequired = false;
}
}
}
}
}
}
public abstract class AbstractPendingMessageCursor implements PendingMessageCursor {
protected int memoryUsageHighWaterMark = 70;
}
ActiveMQ producer不断发送消息,会导致broker内存耗尽吗?的更多相关文章
- kafka producer batch 发送消息
1. 使用 KafkaProducer 发送消息,是按 batch 发送的,producer 首先把消息放入 ProducerBatch 中: org.apache.kafka.clients.pro ...
- producer怎样发送消息到指定的partitions
http://www.aboutyun.com/thread-9906-1-1.html http://my.oschina.net/u/591402/blog/152837 https://gith ...
- ActiveMQ producer 提交事务时突然宕机,会发生什么
producer 在提交事务时,发生宕机,commit 的命令没有发送到 broker,这时会发生什么? ActiveMQ 开启事务发送消息的步骤: session.getTransactionCon ...
- 17 个方面,综合对比 Kafka、RabbitMQ、RocketMQ、ActiveMQ 四个分布式消息队列
原文:https://mp.weixin.qq.com/s/lpsQ3dEZHma9H0V_mcxuTw 一.资料文档 二.开发语言 三.支持的协议 四.消息存储 五.消息事务 六.负载均衡 七.集群 ...
- 综合对比 Kafka、RabbitMQ、RocketMQ、ActiveMQ 四个分布式消息队列
来源:http://t.cn/RVDWcfe 一.资料文档 Kafka:中.有kafka作者自己写的书,网上资料也有一些.rabbitmq:多.有一些不错的书,网上资料多.zeromq:少.没有专门写 ...
- ActiveMQ producer同步/异步发送消息
http://activemq.apache.org/async-sends.html producer发送消息有同步和异步两种模式,可以通过代码配置: ((ActiveMQConnection)co ...
- ActiveMQ(2)---ActiveMQ原理分析之消息发送
持久化消息和非持久化消息的发送策略 消息同步发送和异步发送 ActiveMQ支持同步.异步两种发送模式将消息发送到broker上.同步发送过程中,发送者发送一条消息会阻塞直到broker反馈一个确认消 ...
- activemq安装与简单消息发送接收实例
安装环境:Activemq5.11.1, jdk1.7(activemq5.11.1版本需要jdk升级到1.7),虚拟机: 192.168.147.131 [root@localhost softwa ...
- Kafka学习笔记(6)----Kafka使用Producer发送消息
1. Kafka的Producer 不论将kafka作为什么样的用途,都少不了的向Broker发送数据或接受数据,Producer就是用于向Kafka发送数据.如下: 2. 添加依赖 pom.xml文 ...
随机推荐
- Python 全集变量
1.添加关键字: global 在要给变量从新赋值的时候添加. 全局变量都大写,局部变量都小写.
- 蚂蚁金服“定损宝”现身AI顶级会议NeurIPS
小蚂蚁说: 长期以来,车险定损(通过现场拍摄定损照片确定车辆损失,以作为保险公司理赔的依据)是车险理赔中最为重要的操作环节.以往传统保险公司的车险处理流程,一般为报案.现场查勘.提交理赔材料.审核.最 ...
- GCD LCM UVA - 11388
代码很短理解不容易,在这说不清,大家代码里寻真相. 为什么二者相除就可以A了多找点数试试理解理解. #include<stdio.h> #define mod 1000000007 #de ...
- 搞定'express' 不是内部或外部命令,也不是可运行的程序或批处理文件
1 -- 官方下nodejs,一路next完成, node -v 没问题 可以检测到版本号,node环境ok~ 2-- 本地 安装express :npm install express -g 检测 ...
- OSI 七层和五层
- django数据库的增、删、改、查
1.增加 第一种:save 通过创建模型类对象,执行对象的save()方法保存到数据库中. 第二种:create 2.修改 3.查询 get 查询单一结果,如果不存在会抛出模型类.DoesNotExi ...
- python入门知识点(上)
1.硬件系统: 主机部分: 1.中央处理器(CPU): 电脑的大脑 运算器: 数值计算和逻辑判断 控制器: 可以电脑中的各个部件协同工作 2.内部存储器: 随机存储器:内存条 使用电信号表示数据; 特 ...
- MySQL根据when-else条件批量更新
#类型 0:默认 1:黑(0302) 2:白(0110) SELECT * FROM t_power_plat WHERE plat_type=1; UPDATE t_power_plat SET p ...
- Angular 学习笔记 Material
以后都不会写 0 到 1 的学习记入了,因为官网已经写得很好了. 这里只写一些遇到的坑或则概念和需要注意的事情. Material Table 1. ng-content 无法传递 CdkColumn ...
- spring cloud: zuul(二): zuul的serviceId/service-id配置(微网关)
spring cloud: zuul(二): zuul的serviceId/service-id配置(微网关) zuul: routes: #路由配置表示 myroute1: #路由名一 path: ...