尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ActiveMQ高级特性实战:从存储调度到死信治理与端口排查

ActiveMQ高级特性实战:从存储调度到死信治理与端口排查 很多人一听到“ActiveMQ 高级特性”就觉得门槛很高其实你翻一翻生产环境里那些故障记录就会发现真正让运维头疼的从来不是发消息、收消息这种基础操作而是延迟消息怎么按时触发、消息失败之后怎么捞回来、消费者多了之后怎么分配、Web 控制台为什么突然打不开。这些问题看着零散本质上都是 ActiveMQ 消息代理在设计层面给出的“可选项”没用好。这篇我按生产实战的思路把存储、内存、调度、虚拟主题、消息分组、重试与死信、Spring Boot 整合、8161 端口排查、版本升级这些高频坑位挨个捋一遍。适合两类人一是已经能用 ActiveMQ 收发消息、但遇到异常场景就发怵的开发者二是打算从 5.15 往 5.18 迁、又怕把线上搞崩的运维同学。文章不堆概念每个节点都给配置、给原因、给验证方法你可以直接抄作业也可以当成排查手册留着。1. 动手之前先铺路存储、内存与调度开关1.1 持久化选型KahaDB 还是换个姿势ActiveMQ 的消息体默认存在 KahaDB 里它的数据文件在解压目录的data/kahadb下。很多教程会告诉你“默认配置就能跑”这话没错但放到生产环境里就要多问几句你允许消息落到磁盘吗允许丢多少KahaDB 的默认配置里日志文件是预分配的频繁刷盘参数也偏保守小流量下毫无感知流量一大就会出现消费端忽快忽慢的抖动。我的习惯是提前把 KahaDB 的性能参数打开而不是等项目上线半个月之后再去动它。最常用的一组配置长这样persistenceAdapter kahaDB directory${activemq.data}/kahadb journalMaxFileLength32mb journalWriteBatchSize4096 enableJournalDiskSyncsfalse indexCacheSize10000 indexWriteBatchSize10000 ignoreMissingJournalFilestrue/ /persistenceAdapter这里有两个关键点值得展开说。第一enableJournalDiskSyncs默认是 true意思是每写一条消息都会强制刷到物理磁盘换数据可靠性但吞吐量会明显下降。改成 false 之后数据先落到操作系统页缓存由系统统一刷盘单机吞吐往往能提升 30% 以上。代价是如果机器在 flush 之前突然断电可能有小概率丢失最近几毫秒的数据。能不能接受完全取决于业务。第二journalMaxFileLength控制单个日志文件大小。默认是 32MB太大也不行太小了会导致日志滚动太频繁。我见过有人为了“优化”把它改成 128MB结果消息量不大的情况下一个日志文件里塞了一堆历史消息KahaDB 清理时压力反而变大。一般来说32MB 到 64MB 是安全区间。如果你数据一致性要求极其严格可以走 JDBC 持久化把消息落到 MySQL 之类的数据库里。但我不建议无脑换 JDBC事务性写入会引入数据库连接池、事务协调、慢 SQL 等一系列新问题吞吐量通常只有 KahaDB 的几分之一。除非业务方明确提出“必须落库、必须事务”否则 KahaDB 是性价比最高的答案。1.2 开了调度支持消息才有“时间观念”ActiveMQ 默认不支持定时消息原生 JMS 协议也没这个标准能力。所以第一步是在 broker 上把调度特性打开否则后面发延迟消息时消息会直接发出去根本不进调度器。修改conf/activemq.xml在broker节点上加一个属性broker xmlnshttp://activemq.apache.org/schema/core brokerNamelocalhost dataDirectory${activemq.data} schedulerSupporttrue改完重启 broker调度才算真正可用。Java 客户端发送延迟消息时需要给消息设置几个特殊属性TextMessage message session.createTextMessage(延迟消息内容); message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, 10_000); message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD, 5_000); message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, 3); producer.send(message);上面的代码含义是消息先延迟 10 秒随后每隔 5 秒重复发送一次总共重复 3 次。这个机制非常像系统的cron任务只不过执行入口从应用服务器移到了消息代理里省掉了你自己维护一张定时任务表。这里要特别提醒一个坑调度消息的属性是字符串形式的但底层要求是 long。如果你用setStringProperty去设置AMQ_SCHEDULED_DELAY部分版本会把它当 0 处理消息秒发出去。我排查过不止一次这类“延迟失效”的问题最后发现全是类型写错了。另外AMQ_SCHEDULED_CRON支持 Cron 表达式适合“每天凌晨三点执行一次”这种固定节奏的场景但它依赖 broker 节点所在的系统时区跨时区部署时要先确认这一点否则定时结果很容易差出几个小时。1.3 内存水位怎么定才不翻车很多人在 ActiveMQ 上遇到卡死、消息丢失根因不是 broker 挂了而是内存超限后 broker 拒绝接收消息。默认配置里broker 能用的堆内存由 JVM 参数决定而针对消息内存在conf/activemq.xml里又单独有一层systemUsagesystemUsage memoryUsage memoryUsage limit512mb/ /memoryUsage storeUsage storeUsage limit8gb/ /storeUsage tempUsage tempUsage limit1gb/ /tempUsage /systemUsage三个参数的含义很直白memoryUsage是常驻内存中未消费消息的总上限超出后生产者会被阻塞。storeUsage是磁盘持久化消息的总上限超出后同样会阻塞生产。tempUsage是非持久化消息或消息在内存被换出时的临时文件上限。最容易踩的问题是“其他服务占用内存太多留给 broker 的堆不够”然后 broker 每收到一条非持久化消息都先进内存内存一满整个队列就像堵死了一样。这个 Limit 不是越大越好我习惯把它控制在 JVM 堆的 60% 到 70% 之间比如-Xmx1g时memoryUsage给 512MB 左右给 JVM 的 GC 和其他内部对象留出足够的缓冲。2. 分发与顺序控制的进阶玩法2.1 VirtualTopic让每个消费者都有专属队列拷贝JMS 的 Topic 有个老问题没有持久订阅的消费者一旦离线发布期间的消息就永远收不到。Queue 虽然不存在这个问题但由于语义是“一条消息只能被一个消费者消费”没法做到广播给多个业务方。生产环境里最常见的就是订单系统发一条“订单已创建”事件库存、积分、短信三个服务都要各自处理普通的 Queue 和普通 Topic 都不太匹配。VirtualTopic 就是为此设计的。它的用法很简单生产者消息发到一个名字以VirtualTopic.开头的 TopicDestination destination session.createTopic(VirtualTopic.Orders); producer.send(session.createTextMessage(订单已创建));消费者这边不直接订阅 Topic而是监听一条规则化的 QueueConsumer.库存.VirtualTopic.Orders Consumer.积分.VirtualTopic.Orders Consumer.短信.VirtualTopic.Orders每个微服务都用自己命名的 Queue 去订阅同一个 VirtualTopicbroker 会为每个 Queue 复制一份完整消息。这样库存、积分、短信服务相当于拿到各自独立的消息副本互不干扰。更重要的是如果某个服务挂了消息会留在它的专属 Queue 里等服务恢复后继续消费这就同时拿到了 Queue 的可靠性和 Topic 的广播性。这种设计在微服务拆分的场景下非常实用。我经常看到团队直接开一个 Topic让所有消费者用同一个JmsListener去订阅结果消费能力强的服务把消息全吃掉了消费慢的饿死这就是典型的分发模型选错。换用 VirtualTopic 之后各服务负责消费自己的副本还能独立控制并发和重试互不拖累。不过要提醒一句在较高版本的 ActiveMQ 里VirtualTopic常用于配合destinationInterceptors来使用也就是需要在activemq.xml里做一次声明才能让系统对Consumer.*.VirtualTopic.这种格式的 Queue 进行自动映射。如果你发到 VirtualTopic 之后消费者那边一直收不到消息优先检查这一项destinationInterceptors virtualDestinationInterceptor virtualDestinations virtualTopic nameVirtualTopic. prefixConsumer.*. / /virtualDestinations /virtualDestinationInterceptor /destinationInterceptors2.2 组合目标一条消息同时发到多个目的地有时候业务就是简单粗暴“这条通知既要进日志队列存档又要进实时推送队列还要进异步计算队列。”你当然可以写了三次producer.send但组合目标Composite Destinations提供了更干净的方式Destination combinedDestination session.createCompositeDestination( session.createQueue(LOG.QUEUE), session.createTopic(PUSH.TOPIC)); producer.send(combinedDestination, message);消息发出后ActiveMQ 会把它复制一份发送到LOG.QUEUE再往PUSH.TOPIC广播一份。这个特性在很多框架里是透明的比如 Camel 的to(activemq:A,activemq:B)就是底层用组合目标实现的。它的价值在于解耦业务代码只关心“发一条消息”至于这份消息要被多少个下游消费完全由路由配置决定改起来不用动代码。使用时有两点要注意。一是组合目标里如果同时有 Queue 和 Topic消息的持久化策略、消费语义都不一样最好提前在测试环境验证一遍。二是别在一封邮件里一次性组合几十个目的地组合数量越多broker 内部复制和投递的开销越大会显著拖慢单个send调用的耗时。2.3 消息分组让同一业务的消费顺序始终不乱订单支付后要顺序执行“支付回调更新订单状态”“通知仓储发货”“赠送积分”这三大动作如果被并发消费线程打乱会出现状态回写错乱。全局加锁又太粗暴于是消息分组Message Groups就是顺理成章的方案。实现方式是在生产端设置JMSXGroupID属性message.setStringProperty(JMSXGroupID, orderId);ActiveMQ 收到带同一个JMSXGroupID的消息时会保证这些消息始终被同一个消费者处理即使是多个消费者并存的场景同一组消息也不会被多个线程拆散。它有点像“按订单号做一致性哈希分流”只不过这个分流动作是在 broker 内部完成的。这个特性能解决大部分顺序问题但它依赖两个前提broker 端允许分组且同一个分组的消息会持久化到同一个消费者会话。分组的映射会在消费者连接断开时重新调整如果你有“严格串行”要求需要设置参数JMSXGroupFirstForConsumer来重新初始化组状态。很多人在生产环境里直接给所有订单消息加JMSXGroupID这是错误的。因为分组强度越大broker 能并行的消费者就越少。合理用法是针对“必须顺序处理”的业务维度而不是全局一把梭。比如只按orderId分组而不是把所有消息都塞进同一个组。3. 消费失败与死信治理别让脏数据毁了业务3.1 重试策略失败三次和失败三十次不是一个概念默认情况下ActiveMQ 客户端消费消息抛出异常后broker 会反复把消息重新推给消费者直到达到重试上限为止。这个上限、重试间隔、退避策略都定义在客户端的RedeliveryPolicy里。如果你从来没改过那大概率用的是默认值初始重试延迟 1 秒最多重试 6 次。Spring Boot 环境下可以通过自定义ConnectionFactory调一下策略Bean public ConnectionFactory activeMQConnectionFactory( ActiveMQProperties properties, PooledConnectionFactory pooledConnectionFactory) { ActiveMQConnectionFactory factory new ActiveMQConnectionFactory(); factory.setBrokerURL(properties.getBrokerUrl()); factory.setUserName(properties.getUserName()); factory.setPassword(properties.getPassword()); RedeliveryPolicy policy new RedeliveryPolicy(); policy.setInitialRedeliveryDelay(1000); policy.setMaximumRedeliveries(3); policy.setUseExponentialBackOff(true); policy.setBackOffMultiplier(2); factory.setRedeliveryPolicy(policy); pooledConnectionFactory.setConnectionFactory(factory); return pooledConnectionFactory; }这段代码的含义是消息第一次消费失败后等 1 秒再重投之后每次重试间隔翻倍变为 2 秒、4 秒……最多重试 3 次。useExponentialBackOff非常重要——如果不开启每次重试间隔都是固定 1 秒碰上外部依赖短暂故障还好要是故障持续几分钟客户端就会陷入高频空转。这里有个很多人容易误解的地方RedeliveryPolicy 是客户端行为不是 broker 行为。消息重投多少次、间隔多久由消费端决定broker 的职责只是在“收到客户端 NACK 或抛异常”之后把消息重新放回待投递队列。所以排查重试问题时别光盯着 broker 日志也要看客户端的RedeliveryPolicy设置是否生效。3.2 定制死信队列别让全部坏消息混在一起当消息重投次数耗尽后就会被送进死信队列DLQ默认是ActiveMQ.DLQ。这个默认策略对小工程够用但在多业务共用一个 broker 时问题马上就来了订单业务、短信业务、支付回调全部混在同一个 DLQ 里你根本分不清哪条是哪个业务的恢复处理时还得靠消息属性一个个筛选。更精细的做法是给不同队列指定独立的死信队列并配置死信策略policyEntry queueOrder. deadLetterStrategy individualDeadLetterStrategy queuePrefixDLQ.Order. useQueueForQueueMessagestrue/ /deadLetterStrategy /policyEntry这样Order.Create队列的死信会进入DLQ.Order.CreateOrder.Cancel的死信进入DLQ.Order.Cancel恢复时按队列名直接捞数据就行。注意queuePrefix只是加个前缀如果你希望死信队列名完全重定义写法上还可以指定queueCustomDLQName。另外不要忽略一个隐蔽点默认死信策略不会接收过期消息也就是说如果一条消息设置了 TTL 并且超时过期了它默认是被直接删除而不是进 DLQ。如果你的业务需要“过期消息也要留底排查”必须把processExpired设置为 truedeadLetterStrategy individualDeadLetterStrategy queuePrefixDLQ. useQueueForQueueMessagestrue processExpiredtrue processNonPersistenttrue/ /deadLetterStrategyprocessNonPersistent同理决定非持久化消息是否也会进入死信队列。很多线上问题排查到最后发现丢的不是消息而是没有被死信策略接住的过期消息。你可千万别在这一点上踩坑。3.3 死信订阅与积压监控有了死信队列不代表万事大吉。线上最常见的现象是死信队列不断增长业务方毫无感知等到月底清算时发现有几千条消息静静躺了好几天。我自己的做法是建一个监控任务周期性统计队列的 Pending 数量尤其是名字前缀带DLQ.的队列。最简单的命令是./activemq query -QQueueDLQ.* -QSize或者直接用 Web 控制台看队列 Pending Message Count。如果条件允许给死信队列单独配一个监听器消息一进来就发告警通知这样至少能把“静默积压”变成“即时告警”。再补一个经验恢复死信消息时不要简单地把消息重新塞回原队列。最好在恢复前看一眼原始的JMSDestination属性和JMSXDeliveryCount。因为某些业务会把“重试次数”写进幂等判断里直接重投可能会触发重复处理。我通常会在死信队列里标记“人为恢复”状态再决定是否重新投递。4. Spring Boot 整合实战从“能跑”到“好跑”4.1 依赖与连接池默认配置只适合本地Spring Boot 整合 ActiveMQ 是热搜词里最常出现的话题。不夸张地说很多人第一次接触 ActiveMQ 就是从spring-boot-starter-activemq开始的。依赖很简单dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-activemq/artifactId /dependency dependency groupIdorg.messaginghub/groupId artifactIdpooled-jms/artifactId /dependency第二个依赖pooled-jms容易被人忽略。它提供的是 JMS 连接池没有它Spring 创建的ConnectionFactory每次发送都会新建一个物理连接本地开发感觉不出来一到生产环境大量短连接并发建连broker 连接数瞬间飙升tcp://127.0.0.1:61616很容易被“并发连接数过多”拖垮。然后看关键配置spring: activemq: broker-url: tcp://127.0.0.1:61616 user: admin password: admin pool: enabled: true max-connections: 5 max-sessions-per-connection: 20max-connections建议先从 5 到 10 开始压测调优不要上来就给 50。连接池的规格不是越大越好因为 broker 端能承载的并发始终有上限池子设太大反而让 broker 缺乏自我保护能力连接风暴一来直接 OOM。max-sessions-per-connection同理它决定单个连接里能同时开多少个会话。调整这两个参数时一定要参考 JMS 监听器的并发线程数原则是“钉在一起调”否则池子配置跟消费并发脱节优化毫无意义。4.2 监听容器配置并发、重试与事务Spring Boot 默认会为你创建JmsListenerContainerFactory很多场景下是够用的。但一旦你的消息消费里有外部 API 调用、数据库写操作默认的“一个线程跑一个监听器”就不太够看。我会在配置类里自定义一个工厂Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory( ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setSessionTransacted(false); factory.setConcurrency(2-8); return factory; }setConcurrency(2-8)的含义是至少并发 2 个消费线程队列积压时最多扩展到 8 个。这个参数必须根据业务接口耗时和数据库负载来定接口平均耗时 200ms单线程每秒只能处理 5 条要支撑每秒 50 条就必须开 10 个以上线程。但开太多线程也可能把下游数据库打爆。我习惯先按“单线程处理能力 × 目标性能”倒推线程数不要一次性拍脑袋开 50。这里还涉及一个常见副作用并发增加后同一个队列里的消息会被多个线程处理顺序可能乱。如果业务必须保证顺序要么回到消息分组要么把并发改成 1二选一没有中间态。4.3 ACK 与事务细节别把业务事务塞进消息消费Spring Boot 里消费者处理成功与否直接决定 ACK 行为。默认是AUTO_ACKNOWLEDGE监听方法没有抛异常就会自动确认抛异常则会触发我们前面说的重试策略。如果业务操作有事务要求比如“从队列拿到消息同时更新订单状态和扣减库存两步必须同时成功”就应该开启 session 事务并让数据源事务和 JMS 事务保持一致。实现思路是在JmsListener方法上再叠加Transactional并把监听容器工厂的setSessionTransacted(true)打开factory.setSessionTransacted(true);但注意这里的事务是“JMS 会话事务”跟数据库事务是两套东西。很多团队以为开了Transactional就能连数据库一起回滚实际上 Spring 默认的 JmsListener 事务边界并不覆盖数据库写操作除非你显式配置了chainedTransactionManager。在没有统一事务管理器的情况下我强烈建议你把业务设计成幂等操作而不是试图让消息消费和数据库更新保持强一致。因为分布式环境里可靠消息最终靠的还是幂等和补偿而不是一个神奇的事务管理器。5. 为什么访问不了 8161 端口Web 控制台排查实录5.1 8161 端口的前世今生ActiveMQ 的 Web 控制台由内嵌 Jetty 提供默认端口是 8161。这个问题几乎每周都会被人问明明 broker 启动了http://localhost:8161就是打不开。先记住一个基础事实ActiveMQ 有多个网络端口61616 是 OpenWire 协议端口供 Java 客户端连接8161 是 Web 控制台和 REST API 端口。前者连接正常不代表后者的 Web 服务一定正常。很多人在客户端连得上、Web 打不开的情况下就开始怀疑 broker 挂了其实完全是两回事。常见的端口清单长这样端口协议用途61616OpenWireJava 客户端主连接8161Jetty HTTPWeb 控制台、REST API5672AMQP跨语言 AMQP 客户端61613STOMPPython、Ruby 等 STOMP 客户端1883MQTT物联网 MQTT 客户端61614WebSocket浏览器 WebSocket 客户端如果你在服务器上部署了 ActiveMQ却用浏览器访问本机地址那基本就是防火墙或者端口监听地址的锅。先看下面这个命令输出netstat -anp | grep 8161如果结果是127.0.0.1:8161说明 Jetty 只监听在回环地址上外部机器当然访问不了。ActiveMQ 高版本默认绑定的地址可能受jetty.xml配置影响常见做法是检查conf/jetty.xml里的 connector port 和 hostbean idjettyPort classorg.apache.activemq.web.WebConsolePort / bean idjettyHost classorg.apache.activemq.web.WebConsoleHost /jettyHost如果被设置成了127.0.0.1那你只能本机访问。生产环境需要外部访问时可以把它改成0.0.0.0或具体网卡 IP但要注意由此带来的安全暴露风险必须同步设置控制台登录账号和密码而不是一直用默认的 admin/admin。5.2 一步步定位“打不开”的根因访问不了 8161 这种问题我建议按顺序排查别一上来就改配置第一步确认 broker 进程在跑。ps -ef | grep activemq或者看data/activemq.log尾部有没有 “ActiveMQ WebConsole available at http://...” 的提示。第二步确认端口在监听。netstat -anp | grep 8161没有输出就说明 Jetty 没起来优先看conf/jetty.xml有没有语法错误。第三步本机curl -I http://localhost:8161。如果本机返回 200 但外部打不开基本就是防火墙问题。Linux 下检查firewall-cmd --list-ports没有 8161 就放行firewall-cmd --zonepublic --add-port8161/tcp --permanent firewall-cmd --reload第四步如果本机 curl 也是超时或拒绝停掉 broker启动一个临时 Jetty 占用 8161 试试端口是否被别的进程占用。如果端口被占用说明 broker 里配置的 8161 和别的服务撞了改成 8162 或 7161 都行。这个排查顺序我用了很多年基本 90% 的问题能在前两步锁定。还有一个隐蔽原因部分云服务器安全组白名单也要放行端口即使本机防火墙开了云平台安全组没放行外部仍然访问不了。这个问题经常把经验丰富的老手也绕进去。5.3 控制台常见登录与跨域问题Web 控制台默认登录账号密码在conf/jetty-realm.properties里。很多团队上线前根本没改这个文件导致控制台账号还是 admin/admin这比不设密码还危险因为攻击者连猜都不用猜。登录成功之后还有一个高频场景你想在前端页面直接调 ActiveMQ 的 REST API比如GET http://localhost:8161/api/message/xxx?typequeue结果浏览器跨域报错。ActiveMQ 的 Jetty 默认没有启动 CORS需要你在jetty.xml里增加一个跨域过滤器或者就不要在前端绕这一层改成后端代理转发。从安全角度来说我更推荐后者REST API 的凭据不要暴露到浏览器端。另外8161 端口同时也承载了 REST API 和 WebSocket。如果后端服务要用 WebSocket 订阅消息客户端会连接到ws://host:8161/ws这个路径也要确保被负载均衡设备正确转发很多人配了负载均衡但只转发 61616WebSocket 握手自然会失败。6. 从 5.15 升到 5.18升级注意与下载校验6.1 升级前必须做的三件事ActiveMQ 5.18 是一个比较稳定的版本对 OpenWire 协议、Web 控制台、broker 管理都有持续优化。但升级不是覆盖解压目录的事我的建议流程是先备份conf/和data/两个目录。data/kahadb是数据核心升级过程中如果新版本软件对数据目录格式有兼容性调整旧目录没有备份你会非常被动。读一遍新版本activemq.xml示例配置对比你现在的配置。重点看persistenceAdapter、destinationPolicy、systemUsage这些标签的属性和默认值有没有变化。ActiveMQ 的配置结构是向后兼容的但默认值可能在版本迭代中发生变化不对比的话线上行为会和你的预期产生偏差。在测试环境做一轮全量回归。不要只测“能发能收”延迟消息、死信策略、虚拟主题、连接池并发这些高级特性都要过一遍因为这类场景依赖的底层实现变化往往在基础收发测试里发现不了。启动新版本前建议先看一次启动日志确认没有出现Exception或WARN尤其是持久化适配器加载相关日志。如果你之前用的是 JDBC 持久化还要特别注意升级过程中数据库驱动版本是否兼容。6.2 下载与版本校验从 Apache 官网下载 ActiveMQ 时不要只看版本号就下载安装包一定下载同目录下对应的SHA512校验文件。方法是echo 粘贴校验值 apache-activemq-5.18.x-bin.tar.gz | sha512sum -c -校验通过后再解压。这一步很多人嫌麻烦跳过但开源软件的发布包被篡改的事件不是没发生过宁可多花十秒校验也不要赌运气。如果你在官网下载页找不到老版本入口不用担心Apache 的软件归档目录是可回溯的直接进 archive 目录找对应版本地址就行。安装包下载完了再确认一下 JDK 版本。ActiveMQ 5.17 之后对 JDK 版本有明确要求5.18 系列建议使用 JDK 11 或 17 运行JDK 8 虽然可能能跑但你会看到各种反射警告和不支持的特性提示这些警告往往是后续诡异问题的引子。6.3 升级后的快速验证清单升级完成后不要急着让业务流量切过来先按这张清单走一遍检查项执行方式预期结果broker 状态./activemq status返回进程 PIDOpenWire 端口telnet localhost 61616能连通Web 控制台curl -I http://localhost:8161HTTP 200消息收发用一个测试队列收发 100 条无丢失无重复延迟消息发送 5 秒延迟消息5 秒后收到死信队列消费端故意抛异常消息进入配置的 DLQ如果这六项全部通过升级基本就是可控的。别偷懒省掉延迟消息和死信队列两项我就是有一次跳过了这两个验证结果上线第二天发现新版本的调度器默认行为变了一点延迟消息全部提前发出差点酿成线上事故。7. 最后再分享一个小技巧无论你的 ActiveMQ 部署在单机还是集群环境把data/activemq.log单独做一个日志轮转和集中收集永远不亏。很多人把时间花在调优高级特性上却忽略了这个最基础的操作真出问题时又找不到历史日志只好靠回忆判断消息流向。我一般会在每个 broker 节点上固定保存最近 90 天的日志并把 broker 的 Web 控制台账号改成强密码至少不要是 admin/admin。除此之外生产环境建议把定时备份 KahaDB 数据目录的脚本做成 cron 任务备份粒度和保留周期根据消息量来定。这些看起来和“高级特性”关系不大但正是这些收尾功夫决定了你用起高级特性时有没有后路可退。ActiveMQ 的高级功能本质上是给可靠性做兜底不是炫技。希望这篇番外篇里的配置和踩坑经验能让你在下一回面对大流量、消息乱序、Web 控制台失联时能少走几步弯路。
返回列表