我们有一个ActiveMQ代理,它使用JMS,AMQP和MQTT从非常不同的客户端连接。出于某种原因,我们还没有发现一组特定的MQTT客户端经常(并非总是)持久订阅。这是一个经常添加和删除客户端的测试环境,后者有时通过拔插头或重新启动嵌入式设备,因此无法正确取消订阅。效果(IIUC)是经纪人为可能再也看不到的设备堆积“离线持久订阅”(我可以在http://my_broker:8161/admin/subscribers.jsp下看到这些),永远保留关于这些主题的消息,直到它最终崩溃为止它自己的内存占用。
这里的问题是订阅者持久订阅,我们需要找出原因。然而,也有人决定这样做(不知不觉中)的客户不应该让经纪人停下来,所以我们需要独立解决这个问题。
我找到there are settings for a timeout for offline durable subscriptions并将它们放入我们的代理配置中(最后两行):
<broker
xmlns="http://activemq.apache.org/schema/core"
brokerName="my_broker"
dataDirectory="${activemq.data}"
useJmx="true"
advisorySupport="false"
persistent="false"
offlineDurableSubscriberTimeout="1800000"
offlineDurableSubscriberTaskSchedule="60000">
如果我理解正确的话,上面应该检查每一分钟,并解雇半小时没见过的客户。然而,与文档相反,这似乎不起作用:我已订阅并在几天前拔掉插件的消费者仍然可以在脱机持久订阅者列表中看到,经纪人的内存占用量不断增加,如果我在代理的Web界面中手动删除订阅者我可以看到内存占用量下降。
所以这是我的问题:
答案 0 :(得分:2)
我提取了删除超时持久订阅的相关代码(doCleanup()
)。
在成功的情况下,它执行:
LOG.info("Destroying durable subscriber due to inactivity: {}", sub);
在失败的情况下,执行:
LOG.error("Failed to remove inactive durable subscriber", e);
在日志文件中查找上面的日志行,并将其与您使用admin / subscribers.jsp查看器观察到的详细信息进行匹配。如果它没有打印任何行,则订阅可能由于某种原因而保留active
,或者您可能偶然发现了一个错误。
另外,如果可以,你可以尝试删除经纪人名称中的下划线(_)吗?该手册讨论了经纪人姓名中下划线的问题。
<强>代码:强>
public TopicRegion(RegionBroker broker, DestinationStatistics destinationStatistics, SystemUsage memoryManager, TaskRunnerFactory taskRunnerFactory, DestinationFactory destinationFactory) {
super(broker, destinationStatistics, memoryManager, taskRunnerFactory, destinationFactory);
if (broker.getBrokerService().getOfflineDurableSubscriberTaskSchedule() != -1 && broker.getBrokerService().getOfflineDurableSubscriberTimeout() != -1) {
this.cleanupTimer = new Timer("ActiveMQ Durable Subscriber Cleanup Timer", true);
this.cleanupTask = new TimerTask() {
@Override
public void run() {
doCleanup();
}
};
this.cleanupTimer.schedule(cleanupTask, broker.getBrokerService().getOfflineDurableSubscriberTaskSchedule(),broker.getBrokerService().getOfflineDurableSubscriberTaskSchedule());
}
}
public void doCleanup() {
long now = System.currentTimeMillis();
for (Map.Entry<SubscriptionKey, DurableTopicSubscription> entry : durableSubscriptions.entrySet()) {
DurableTopicSubscription sub = entry.getValue();
if (!sub.isActive()) {
long offline = sub.getOfflineTimestamp();
if (offline != -1 && now - offline >= broker.getBrokerService().getOfflineDurableSubscriberTimeout()) {
LOG.info("Destroying durable subscriber due to inactivity: {}", sub);
try {
RemoveSubscriptionInfo info = new RemoveSubscriptionInfo();
info.setClientId(entry.getKey().getClientId());
info.setSubscriptionName(entry.getKey().getSubscriptionName());
ConnectionContext context = new ConnectionContext();
context.setBroker(broker);
context.setClientId(entry.getKey().getClientId());
removeSubscription(context, info);
} catch (Exception e) {
LOG.error("Failed to remove inactive durable subscriber", e);
}
}
}
}
}
// The toString method for DurableTopicSubscription class
@Override
public synchronized String toString() {
return "DurableTopicSubscription-" + getSubscriptionKey() + ", id=" + info.getConsumerId() + ", active=" + isActive() + ", destinations=" + durableDestinations.size() + ", total=" + getSubscriptionStatistics().getEnqueues().getCount() + ", pending=" + getPendingQueueSize() + ", dispatched=" + getSubscriptionStatistics().getDispatched().getCount() + ", inflight=" + dispatched.size() + ", prefetchExtension=" + getPrefetchExtension();
}