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文 ...
随机推荐
- 测试驱动android
测试驱动android开发 在安卓模拟器或者真机上跑测试用例速度很慢.构建.部署.启动app,通常需要花费一分钟或者更久.这不是TDD(测试驱动开发)模式.Robolectric提供一种更好的方式. ...
- React Native组件之BackAndroid !安卓手机的物理返回键的使用
ok!在安卓手机上,当我们用物理返回键的时候,会以一次性的将程序退出来,这样是很不好的体验,所以就需要使用RN的物理返回键组件:BackAndroid,其原理也就是 分析路由,然后pop()这样! o ...
- JSON parse error: Cannot deserialize instance of `int` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc
代码程序: @PostMapping("selectById") @ResponseBody public Result selectById(@RequestBody int i ...
- ASP.net 网站开发知识点总结
一.常用技术概括及介绍 1. SQL server:处理数据库的设计 2. asp.net 3. html :前端网页 4. css :网页的布局设计 5. JavaScrip ...
- 力扣(LeetCode)69. x 的平方根
实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去. 示例 1: 输入: 4 输出: 2 示例 ...
- Ajax请求导出Excel的问题
文章转载自: http://yuwenlin.iteye.com/blog/2275289 Ajax请求导出Excel的问题描述: 前端发起Ajax请求get或post,后台使用Poi生成excel文 ...
- Eclipse Jee环境配置
最近下载了新的Eclipse Jee Neon版本,记录一下如何进行开发环境的配置. 1.下载必要的开发环境文件 ①下载Java SE Development Kit (简称JDK) ②下载Tomca ...
- Python全栈开发-有趣的小程序
进度条的打印 import sys,time for i in range(20): sys.stdout.write('$') #stdout是标准输出的意思,在一般电脑上,stdout的 ...
- 使用“rz -be”命令上传文件至服务器;使用“sz 文件名”从服务器下载文件到本地
注意:需要事先安装lrzsz服务 yum install -y lrzsz 因为服务器没有安装sftp服务,无法使用FileZilla.Xftp等连接服务器上传文件,这种情况可以利用rz命令上传文件. ...
- centos7:mysql-5.7.23安装(二进制安装)
mysql有二进制码安装,和源码编译安装(mysql5.5使用cmake安装,mysql5.7需要安装boost依赖安装),因为boost依赖安装麻烦,所以用二进制码安装 MySql 5.7.23安装 ...