
1. 问题场景描述
在一个典型的jboss eap应用中,web服务通过servlet将消息发送到jms队列,随后一个mdb负责消费并处理这些消息。然而,在实际运行中,可能会出现mdb的onmessage()方法未能被调用的情况,导致消息似乎“丢失”,但服务器日志中并未报告任何错误。这种现象通常是间歇性的,例如约50%的消息未能被预期的mdb处理。
以下是涉及到的基本配置和代码片段:
JBoss JMS 队列配置:
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(); // 如果是 CLIENT_ACKNOWLEDGE 模式,需要手动确认
}
}2. 诊断方法:分析JMS队列运行时指标
当遇到此类问题时,第一步是检查JMS队列的运行时状态,以确定消息是否真的滞留在队列中,或者是否已被消费。JBoss CLI提供了强大的工具来实时查看这些指标。
使用以下JBoss CLI命令查询特定JMS队列的运行时资源信息:
/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
}
}关键指标解读:
- messages-added: 表示队列接收到的消息总数。在上述示例中,为 1L,表明有一条消息成功发送到了队列。
- message-count: 表示当前队列中等待被消费的消息数量。在上述示例中,为 0L。
- consumer-count: 表示当前连接到此队列的消费者数量。在上述示例中,为 30。
诊断结论:
从上述输出可以看出,messages-added 为 1 而 message-count 为 0,这明确表明发送到队列的消息已被某个消费者成功接收并处理,而不是滞留在队列中。如果消息未被消费,message-count 应该大于 0。
问题的关键在于 consumer-count。通常,一个MDB的默认并发会话数(即消费者数量)在JBoss EAP中是有限的,例如默认可能为 15。然而,示例中 consumer-count 达到了 30,这远超单个MDB的默认并发数。这强烈暗示存在多于预期的MDB实例或重复的MDB部署正在消费同一个队列。
3. 根本原因:多余的MDB部署
当 consumer-count 异常高时,最常见的原因是:
- 重复部署: 同一个MDB被部署了多次(例如,同一个EAR或WAR文件被意外地部署了两次,或者MDB被打包在不同的应用中但都部署到了同一个服务器)。
- 多实例部署: 在集群环境中,如果MDB的部署策略不当,可能导致在单个节点上启动了过多实例,或者在非集群预期的情况下,多个应用实例都包含了该MDB。
在这种情况下,消息并没有“丢失”,而是被其他(可能是不受控制的)MDB实例消费了。因此,您预期的MDB实例可能只是“碰巧”没有接收到消息,因为其他实例抢先处理了。
4. 解决方案:识别并管理消费者
为了解决这个问题,您需要识别所有连接到该队列的消费者,并消除多余的部署。
步骤一:列出所有消费者
使用以下JBoss CLI命令来获取连接到特定JMS队列的所有消费者的详细信息:
/subsystem=messaging-activemq/server=default/jms-queue=HIFWebHookQueue:list-consumers-as-json
这个命令将返回一个JSON数组,其中包含每个消费者的详细信息,包括其客户端ID、连接ID以及可能关联的应用程序名称或MDB名称。通过分析这些信息,您可以确定哪些MDB实例正在消费队列。
步骤二:识别并移除多余部署
根据 list-consumers-as-json 的输出,您应该能够识别出导致 consumer-count 异常高的那些MDB实例。
- 检查部署目录: 检查JBoss EAP的部署目录(如 standalone/deployments 或 domain/deployments),确认是否存在重复的 .ear 或 .war 文件,特别是那些包含 WebhookListenerEJB 的部署。
- 审查应用程序结构: 如果您的应用是EAR结构,确保MDB没有被意外地包含在多个子模块中。
- 集群环境检查: 在集群环境中,确认MDB的部署策略是否正确,以避免在单个节点上启动过多实例,或者在所有节点上都部署了不必要的MDB。
- 手动卸载/禁用: 一旦识别出多余的部署,通过JBoss CLI或管理控制台将其卸载或禁用。
例如,如果发现 WebhookListenerEJB.jar 被部署了两次,您需要卸载其中一个。
5. 注意事项与最佳实践
- MDB并发性配置: 了解并合理配置MDB的并发性。@ActivationConfigProperty(propertyName="maxSession", propertyValue="...") 可以控制MDB的最大并发会话数。不当的配置可能导致资源浪费或意外的消费者行为。
- 消息确认模式: 确保您的MDB消息确认模式(如 AUTO_ACKNOWLEDGE 或 CLIENT_ACKNOWLEDGE)配置正确。在 CLIENT_ACKNOWLEDGE 模式下,如果 message.acknowledge() 没有被调用,消息可能会被重新投递,但这不是本案例中的“丢失”原因。
- 事务管理: 确保MDB的事务管理(@TransactionManagement 和 @TransactionAttribute)配置得当,以保证消息处理的原子性和可靠性。
- 日志记录: 在MDB的 onMessage 方法中添加详细的日志记录,包括消息ID和处理开始/结束的日志,这有助于追踪消息的处理路径。
- 持续监控: 定期监控JMS队列的运行时指标,特别是 consumer-count 和 message-count,以在问题发生初期就发现异常。
总结
JBoss EAP中JMS MDB“消息丢失”的现象,往往并非消息真正消失,而是被预期之外的MDB实例消费。通过JBoss CLI工具,特别是 read-resource(include-runtime=true) 和 list-consumers-as-json 命令,可以有效地诊断队列的运行时状态和消费者情况。一旦确认存在多余的MDB部署,及时进行管理和清理,即可解决此类问题,确保消息被正确的MDB实例可靠地处理。










