You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Spring JMS捕获MQ宕机事件?容器配置及上报方法求指导

Great question! Let's tackle this from both the mechanism clarification and implementation perspective, since you're already seeing Spring's logs but need actionable downtime reporting.

Does DefaultMessageListenerContainer offer an ErrorHandler-like mechanism for MQ downtime?

Short answer: Yes, but it's a combination of JMS-level and container-level hooks, not just a single "ErrorHandler" for connection failures. Here's the breakdown:

  • JMS Connection's ExceptionListener: Catches low-level connection breakdowns directly from the MQ broker.
  • DMLC's built-in ErrorHandler: Handles container-level errors (like failed connection recovery attempts).
  • Spring Application Events: Lets you listen to DMLC lifecycle changes (e.g., container stopping due to lost connectivity).

Step-by-Step Implementation for MQ Downtime Capture & Reporting

Let's build this out with concrete code examples, assuming you're using Spring Boot (but works with vanilla Spring too).

1. Implement a JMS ExceptionListener for Raw Connection Failures

This is the first line of defense—triggers immediately when the MQ broker drops the connection.

import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import org.springframework.stereotype.Component;

@Component
public class MQConnectionExceptionListener implements ExceptionListener {
    private final MQStatusReporter statusReporter;

    // Inject your custom reporting component
    public MQConnectionExceptionListener(MQStatusReporter statusReporter) {
        this.statusReporter = statusReporter;
    }

    @Override
    public void onException(JMSException exception) {
        // Adjust this check to match your MQ vendor's error codes/messages
        boolean isDowntime = isConnectionLossError(exception);
        if (isDowntime) {
            statusReporter.reportDowntime("JMS Connection dropped: " + exception.getMessage());
        }
    }

    private boolean isConnectionLossError(JMSException e) {
        // Example for ActiveMQ: adjust for IBM MQ, Solace, etc.
        String errorCode = e.getErrorCode();
        return "DISCONNECTED".equals(errorCode) 
            || e.getMessage().contains("Connection closed") 
            || e.getMessage().contains("Broker not available");
    }
}

Bind this listener to your ConnectionFactory:

import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JmsConfig {
    @Bean
    public ActiveMQConnectionFactory connectionFactory(MQConnectionExceptionListener exceptionListener) {
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://your-mq-host:61616");
        factory.setExceptionListener(exceptionListener);
        return factory;
    }
}

2. Configure DMLC's ErrorHandler for Recovery Failures

DMLC automatically tries to reconnect after a loss, but you can catch repeated recovery failures with a custom ErrorHandler:

import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.stereotype.Component;
import org.springframework.util.ErrorHandler;

@Component
public class MQContainerErrorHandler implements ErrorHandler {
    private final MQStatusReporter statusReporter;

    public MQContainerErrorHandler(MQStatusReporter statusReporter) {
        this.statusReporter = statusReporter;
    }

    @Override
    public void handleError(Throwable t) {
        // Check if the root cause is a connection loss
        Throwable rootCause = getRootCause(t);
        if (rootCause instanceof JMSException && isConnectionLossError((JMSException) rootCause)) {
            statusReporter.reportDowntime("DMLC failed to recover connection: " + t.getMessage());
        }
    }

    private Throwable getRootCause(Throwable t) {
        while (t.getCause() != null) t = t.getCause();
        return t;
    }

    // Reuse the same isConnectionLossError method from step 1
    private boolean isConnectionLossError(JMSException e) {
        String errorCode = e.getErrorCode();
        return "DISCONNECTED".equals(errorCode) 
            || e.getMessage().contains("Connection closed") 
            || e.getMessage().contains("Broker not available");
    }
}

Attach this to your DMLC bean:

@Bean
public DefaultMessageListenerContainer messageListenerContainer(
        ConnectionFactory connectionFactory,
        MessageListener yourMessageListener,
        MQContainerErrorHandler errorHandler) {
    DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    container.setDestinationName("your-queue-name");
    container.setMessageListener(yourMessageListener);
    container.setErrorHandler(errorHandler);
    container.setRecoveryInterval(5000); // Retry connection every 5 seconds
    return container;
}

3. Listen to DMLC Lifecycle Events for Container Shutdowns

If the DMLC stops entirely due to unrecoverable MQ issues, you can catch this with a Spring ApplicationListener:

import org.springframework.context.ApplicationListener;
import org.springframework.jms.listener.event.ListenerContainerStoppedEvent;
import org.springframework.stereotype.Component;

@Component
public class MQContainerStoppedListener implements ApplicationListener<ListenerContainerStoppedEvent> {
    private final MQStatusReporter statusReporter;

    public MQContainerStoppedListener(MQStatusReporter statusReporter) {
        this.statusReporter = statusReporter;
    }

    @Override
    public void onApplicationEvent(ListenerContainerStoppedEvent event) {
        // Check if the container stopped due to a connection error
        Throwable lastException = ((DefaultMessageListenerContainer) event.getSource()).getLastException();
        if (lastException != null && getRootCause(lastException) instanceof JMSException) {
            JMSException jmsEx = (JMSException) getRootCause(lastException);
            if (isConnectionLossError(jmsEx)) {
                statusReporter.reportDowntime("DMLC stopped due to MQ downtime: " + lastException.getMessage());
            }
        }
    }

    // Reuse helper methods from previous steps
    private Throwable getRootCause(Throwable t) {
        while (t.getCause() != null) t = t.getCause();
        return t;
    }

    private boolean isConnectionLossError(JMSException e) {
        String errorCode = e.getErrorCode();
        return "DISCONNECTED".equals(errorCode) 
            || e.getMessage().contains("Connection closed") 
            || e.getMessage().contains("Broker not available");
    }
}

4. Build the MQ Status Reporter (Your Alerting Logic)

Finally, implement the actual reporting to your monitoring system (DingTalk, Prometheus, email, etc.):

import org.springframework.stereotype.Component;

@Component
public class MQStatusReporter {
    // Add a debounce to avoid spamming alerts
    private long lastReportedTime = 0;
    private static final long DEBOUNCE_MS = 60000; // 1 minute

    public void reportDowntime(String message) {
        long now = System.currentTimeMillis();
        if (now - lastReportedTime > DEBOUNCE_MS) {
            // Replace with your actual alerting logic
            System.err.println("[CRITICAL] MQ DOWNTIME: " + message);
            // Example: Send to DingTalk webhook, increment Prometheus counter, etc.
            // DingTalkAlert.send("MQ Down", message);
            // Metrics.counter("mq.downtime.alerts").increment();
            lastReportedTime = now;
        }
    }
}

Key Notes

  • Vendor-Specific Adjustments: The error code/message checks need to match your MQ broker (ActiveMQ, IBM MQ, etc.—each has unique error signatures).
  • Debounce: The reporter includes a debounce to avoid flooding your alerts during repeated connection retries.
  • Recovery Alerts: If you need to report when MQ comes back up, you can add a listener for ListenerContainerStartedEvent or check for connection recovery in the ExceptionListener.

内容的提问来源于stack exchange,提问作者hacker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:21:38