JBoss EAP 7.2中JMS MDB偶发丢失消息问题求助
我在JBoss EAP 7.2上部署了标准Web应用,请求通过Servlet发送消息至JMS队列,再由MDB处理。但约50%的情况下,生产者发送消息后,MDB的onMessage()方法从未被调用,且服务器日志无报错信息。以下是相关配置、代码及排查后的修复方案:
相关配置与代码
JBoss队列配置
<jms-queue name="HIFWebHookQueue" entries="HifWebHookQueue java:jboss/exported/jms/queue/HifWebHookQueue"/>
Servlet生产者代码
@WebServlet(name = "/") public class MyServlet extends HttpServlet { @Inject private JMSContext context; @Resource(lookup = "java:jboss/exported/jms/queue/HifWebHookQueue") private Queue queue; protected void doGet(HttpServletRequest request, HttpServletResponse response) { JMSProducer producer = context.createProducer(); producer.setDeliveryMode(DeliveryMode.PERSISTENT); ObjectMessage msg = context.createObjectMessage(evt); //evt是可序列化POJO producer.send(queue,evt); } }
MDB消费者代码
@MessageDriven(name = "WebhookListenerEJB", activationConfig = { @ActivationConfigProperty(propertyName="messagingType", propertyValue="javax.jms.MessageListener"), @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"), @ActivationConfigProperty(propertyName="destination", propertyValue="java:/jms/queue/HIFWebHookQueue"), @ActivationConfigProperty(propertyName="ConnectionFactoryName", propertyValue="ConnectionFactory"), }) @TransactionManagement(value = TransactionManagementType.CONTAINER) @TransactionAttribute(value = TransactionAttributeType.REQUIRED) public class WebhookListenerEJB implements MessageListener { public void onMessage(Message message) { ObjectMessage msg = (ObjectMessage) message; ... 业务处理 ... message.acknowledge(); } }
发送消息后的JBoss CLI输出
[standalone@localhost:9990 /] /subsystem=messaging-activemq/server=default/jms-queue=HIFWebHookQueue:read-resource(include-runtime=true) { "outcome" => "success", "result" => { "consumer-count" => 30, "dead-letter-address" => "jms.queue.DLQ", "delivering-count" => 0, "durable" => true, "entries" => [ "HifWebHookQueue", "java:jboss/exported/jms/queue/HifWebHookQueue" ], "expiry-address" => "jms.queue.ExpiryQueue", "legacy-entries" => undefined, "message-count" => 0L, "messages-added" => 1L, "paused" => false, "queue-address" => "jms.queue.HIFWebHookQueue", "scheduled-count" => 0L, "selector" => undefined, "temporary" => false } }
问题排查与修复步骤
1. 修复Servlet消息发送的代码错误
Servlet里存在低级错误:明明创建了ObjectMessage实例,发送时却直接传了evt对象而非这个消息实例。直接传对象会导致消息格式不符合JMS规范,部分情况下会被Broker静默处理,MDB自然收不到。
修改后的代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) { JMSProducer producer = context.createProducer(); producer.setDeliveryMode(DeliveryMode.PERSISTENT); ObjectMessage msg = context.createObjectMessage(evt); // 改为发送创建好的ObjectMessage实例 producer.send(queue, msg); }
2. 修正MDB的队列JNDI名称匹配
队列配置的JNDI条目是java:jboss/exported/jms/queue/HifWebHookQueue(注意首字母是小写的Hif),但MDB里配置的destination是java:/jms/queue/HIFWebHookQueue(全大写HIF)。JBoss的JNDI名称是大小写敏感的,这会导致MDB绑定到错误的队列(或者压根没绑定上),消息被其他消费者吃掉或者丢到了别的地方。
调整MDB的activation配置,确保JNDI名称完全匹配:
@ActivationConfigProperty(propertyName="destination", propertyValue="java:jboss/exported/jms/queue/HifWebHookQueue"),
或者统一大小写,把队列配置里的名称改成HIFWebHookQueue,对应的JNDI条目也调整为大写,保持一致即可。
3. 移除MDB中的手动消息确认
MDB用的是容器管理事务(TransactionManagementType.CONTAINER),事务属性是REQUIRED,这种情况下容器会在事务提交时自动确认消息。手动调用message.acknowledge()会干扰容器的事务逻辑,可能导致消息确认异常,引发消费失败却无日志的情况。
直接删除onMessage里的手动确认代码:
public void onMessage(Message message) { ObjectMessage msg = (ObjectMessage) message; ... 业务处理 ... // 删掉这行:message.acknowledge(); }
4. 清理多余的消费者
CLI输出显示consumer-count是30,这明显不正常——默认MDB的并发消费数远低于这个数值。说明存在其他未关闭的消费者(比如测试用的JMS客户端、遗留的消费代码)连接到了这个队列,消息被这些多余的消费者消费了,目标MDB根本没机会拿到消息。
- 检查应用是否有其他JMS消费逻辑连接到该队列;
- 重启JBoss服务器,清除残留的消费者连接,之后观察
consumer-count是否恢复到MDB的预期并发数(可以通过@ActivationConfigProperty配置maxSession调整,默认一般是15左右)。
5. 启用调试日志排查剩余问题
如果上面的步骤都做完还是有问题,就开启JBoss的JMS调试日志,查看消息发送、路由、消费的完整流程:
在standalone.xml里添加日志配置:
<logger category="org.apache.activemq.artemis"> <level name="DEBUG"/> </logger> <logger category="javax.jms"> <level name="DEBUG"/> </logger>
重启服务器后,查看日志就能清楚看到消息到底被路由到哪里了,有没有被目标MDB接收。
内容的提问来源于stack exchange,提问作者OldProgrammer

