0

0

解决ActiveMQ Artemis中选择器浏览与接收消息不一致问题

心靈之曲

心靈之曲

发布时间:2025-11-05 22:16:01

|

875人浏览过

|

来源于php中文网

原创

解决ActiveMQ Artemis中选择器浏览与接收消息不一致问题

本文探讨activemq artemis在使用openwire jms客户端时,通过选择器浏览消息成功但无法接收消息的问题。核心原因在于activemq artemis 2.18.0版本与openwire客户端存在的已知bug (artemis-3916)。文章提供了两种解决方案:切换至activemq artemis核心jms客户端或将artemis broker升级至2.25.0或更高版本,并附带代码示例进行说明。

问题描述:选择器浏览成功,接收失败

在使用ActiveMQ Artemis 2.18.0及artemis-jms-client-all:2.18.0作为客户端依赖时,开发者可能会遇到一个异常情况:能够通过QueueBrowser结合JMSMessageID选择器成功浏览到目标消息,但随后使用MessageConsumer以相同的选择器尝试接收消息时,却无法获取到消息,导致receive(timeout)方法返回null,进而抛出IllegalStateException。这种现象并非总是发生,而是在大量消息中以较低的概率(例如十万分之一三十)出现。

以下代码片段展示了这一问题:

import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import javax.jms.*;
import java.util.Enumeration;

public class ArtemisMessageIssueReproducer {

    private static final String BROKER_URL = "tcp://localhost:61616"; // 假设Broker运行在本地61616端口

    public static void main(String[] args) {
        // 模拟一个JMSMessageID,实际场景中应从已发送消息中获取
        String messageIdToFind = "ID:some-broker-id-12345-1-1"; 
        // 假设消息已发送到名为 "hospital" 的队列中

        Connection connection = null;
        Session session = null;
        String selector = "JMSMessageID='" + messageIdToFind + "'";

        try {
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
            connection = connectionFactory.createConnection();
            session = connection.createSession(true, Session.SESSION_TRANSACTED);
            Queue deadQueue = session.createQueue("hospital");
            connection.start();

            // 1. 使用QueueBrowser浏览消息
            QueueBrowser browser = session.createBrowser(deadQueue, selector);
            Enumeration e = browser.getEnumeration();
            int foundedElements = 0;
            while (e.hasMoreElements()) {
                Message message = (Message) e.nextElement();
                System.out.println("Browser found message: " + message.getJMSMessageID());
                foundedElements++;
            }
            browser.close();

            if (foundedElements != 1) {
                throw new IllegalStateException("根据选择器找到的消息数量不为1,实际为: " + foundedElements);
            }
            System.out.println("Browser成功找到消息。");

            // 2. 使用MessageConsumer尝试接收消息
            MessageConsumer messageConsumer = session.createConsumer(deadQueue, selector);
            Message receivedMessage = messageConsumer.receive(1000); // 等待1秒

            if (receivedMessage == null) {
                throw new IllegalStateException("MessageConsumer未能接收到消息,返回null。");
            } else {
                System.out.println("MessageConsumer成功接收到消息: " + receivedMessage.getJMSMessageID());
            }
            messageConsumer.close();

            session.commit();
            System.out.println("事务提交成功。");

        } catch (Exception e) {
            System.err.println("发生异常: " + e.getMessage());
            try {
                if (session != null) {
                    session.rollback();
                    System.err.println("事务回滚。");
                }
            } catch (JMSException e1) {
                System.err.println("回滚异常: " + e1.getMessage());
            }
            throw new RuntimeException(e);
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                    System.out.println("连接关闭。");
                } catch (JMSException e) {
                    System.err.println("关闭连接异常: " + e.getMessage());
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

在上述代码中,如果foundedElements为1,但receivedMessage却为null,则说明遇到了该问题。

问题根源分析:OpenWire客户端与Broker版本兼容性

经过深入分析,此问题并非JMS规范的普遍行为,而是特定于ActiveMQ Artemis在使用OpenWire JMS客户端时,与较旧的Broker版本(如2.18.0)之间存在的兼容性问题。

ActiveMQ Artemis支持多种JMS客户端协议,其中:

  • ActiveMQ Artemis Core JMS Client:这是Artemis原生的、推荐的JMS客户端,通常通过artemis-jms-client或artemis-jms-client-all(但需注意其内部可能包含OpenWire依赖)引入。
  • OpenWire JMS Client:这是Apache ActiveMQ Classic使用的协议,Artemis为了兼容性也提供了支持。当使用artemis-jms-client-all时,如果配置不当或默认行为,可能会隐式地使用OpenWire协议。

问题的关键在于,ActiveMQ Artemis 2.18.0版本在处理OpenWire客户端的MessageConsumer与选择器结合时的内部机制存在一个已知的Bug,编号为ARTEMIS-3916。这个bug会导致即使消息存在并能被浏览器看到,消费者也可能无法正确匹配并接收到它。而QueueBrowser只是读取消息的副本或元数据,不涉及消息的实际消费和状态改变,因此不受此bug影响。

解决方案

针对此问题,主要有两种推荐的解决方案,可以根据实际项目情况选择:

PixVerse
PixVerse

PixVerse是一款强大的AI视频生成工具,可以轻松地将多种输入转化为令人惊叹的视频。

下载

方案一:切换至ActiveMQ Artemis核心JMS客户端

这是最直接且推荐的解决方案,因为它避免了OpenWire协议带来的潜在兼容性问题。确保你的项目显式地使用Artemis Core JMS客户端。

  1. 检查并调整Maven/Gradle依赖: 确保你的pom.xml或build.gradle中引入的是ActiveMQ Artemis的核心JMS客户端依赖,而不是可能默认使用OpenWire的聚合包或特定OpenWire客户端。通常,artemis-jms-client是核心客户端。

    
    
        org.apache.activemq
        artemis-jms-client
        2.18.0 
    

    或者,如果使用artemis-jms-client-all,请确认其内部配置或连接工厂是否强制使用了Artemis Core协议而非OpenWire。

  2. 使用ActiveMQConnectionFactory创建连接: 确保你的连接工厂是org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory,它默认使用Artemis的原生协议。

    以下是使用核心JMS客户端的示例代码,该代码在ActiveMQ Artemis 2.18.0上测试通过,未复现问题:

    import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
    import javax.jms.*;
    import java.util.Enumeration;
    
    public class ArtemisCoreClientExample {
    
        private static final String BROKER_URL = "tcp://localhost:61616";
        private static final String TEST_MESSAGE_CONTENT = "This is a test message for Artemis.";
    
        public static void main(String[] args) {
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
            try (Connection connection = connectionFactory.createConnection()) {
                Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
                Queue deadQueue = session.createQueue("hospital");
                connection.start();
    
                // 1. 发送一条消息以供测试
                MessageProducer mp = session.createProducer(deadQueue);
                TextMessage m = session.createTextMessage(TEST_MESSAGE_CONTENT);
                mp.send(m);
                session.commit(); // 提交发送操作
                String sentMessageId = m.getJMSMessageID();
                System.out.println("消息发送成功,ID: " + sentMessageId);
    
                // 2. 使用QueueBrowser浏览消息
                String selector = "JMSMessageID='" + sentMessageId + "'";
                QueueBrowser browser = session.createBrowser(deadQueue, selector);
                Enumeration e = browser.getEnumeration();
                int foundedElements = 0;
                while (e.hasMoreElements()) {
                    e.nextElement(); // 仅遍历,不处理内容
                    foundedElements++;
                }
                browser.close();
                if (foundedElements != 1) {
                    throw new IllegalStateException("Browser找到的消息数量不为1,实际为: " + foundedElements);
                }
                System.out.println("Browser成功找到消息,数量: " + foundedElements);
    
                // 3. 使用MessageConsumer接收消息
                MessageConsumer messageConsumer = session.createConsumer(deadQueue, selector);
                Message received = messageConsumer.receive(1000); // 等待1秒
                if (received == null) {
                    throw new IllegalStateException("MessageConsumer未能接收到消息,返回null。");
                } else if (!(received instanceof TextMessage) || !((TextMessage) received).getText().equals(TEST_MESSAGE_CONTENT)) {
                    throw new IllegalStateException("接收到的消息内容不匹配或类型错误。");
                }
                System.out.println("MessageConsumer成功接收到消息,内容: " + ((TextMessage) received).getText());
                messageConsumer.close();
    
                session.commit(); // 提交接收操作
                System.out.println("事务提交成功,消息已成功接收并处理。");
    
            } catch (Exception e) {
                System.err.println("操作失败: " + e.getMessage());
                throw new RuntimeException(e);
            }
        }
    }

方案二:升级ActiveMQ Artemis Broker

如果由于某些原因无法切换客户端库,那么升级ActiveMQ Artemis Broker是另一种有效的解决方案。

  1. 升级Broker版本: 将ActiveMQ Artemis Broker升级到至少2.25.0版本。ARTEMIS-3916问题在该版本中已得到修复。理想情况下,建议升级到最新稳定版本,以获得最新的bug修复、性能改进和新功能。

  2. 升级客户端依赖: 如果升级了Broker,通常也建议将客户端依赖(artemis-jms-client或artemis-jms-client-all)升级到与Broker版本兼容或相同的新版本,以确保最佳的兼容性和功能。

总结与注意事项

  • 客户端选择至关重要:在ActiveMQ Artemis生态系统中,选择正确的JMS客户端库(核心客户端 vs. OpenWire客户端)对于系统的稳定性和性能至关重要。对于新项目或遇到兼容性问题时,优先考虑使用ActiveMQ Artemis的核心JMS客户端。
  • 版本管理:JMS客户端库与Broker版本之间的兼容性非常重要。通常建议两者保持版本一致或客户端版本略高于Broker版本(在兼容范围内)。
  • 调试策略:当遇到消息丢失或无法接收等问题时,应同时检查客户端日志和Broker日志。特别是Broker的broker.xml配置中的日志级别,可以调高以获取更详细的内部操作信息。
  • 事务处理:示例代码中使用了事务会话 (session.createSession(true, Session.SESSION_TRANSACTED)),并在操作成功后进行commit(),失败时进行rollback()。这是生产环境中确保消息可靠性的标准实践。

通过理解问题根源并采取上述解决方案,可以有效解决ActiveMQ Artemis中选择器浏览与接收消息不一致的问题,确保消息系统的稳定可靠运行。

相关专题

更多
Java Maven专题
Java Maven专题

本专题聚焦 Java 主流构建工具 Maven 的学习与应用,系统讲解项目结构、依赖管理、插件使用、生命周期与多模块项目配置。通过企业管理系统、Web 应用与微服务项目实战,帮助学员全面掌握 Maven 在 Java 项目构建与团队协作中的核心技能。

0

2025.09.15

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

231

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

436

2024.03.01

session失效的原因
session失效的原因

session失效的原因有会话超时、会话数量限制、会话完整性检查、服务器重启、浏览器或设备问题等等。详细介绍:1、会话超时:服务器为Session设置了一个默认的超时时间,当用户在一段时间内没有与服务器交互时,Session将自动失效;2、会话数量限制:服务器为每个用户的Session数量设置了一个限制,当用户创建的Session数量超过这个限制时,最新的会覆盖最早的等等。

308

2023.10.17

session失效解决方法
session失效解决方法

session失效通常是由于 session 的生存时间过期或者服务器关闭导致的。其解决办法:1、延长session的生存时间;2、使用持久化存储;3、使用cookie;4、异步更新session;5、使用会话管理中间件。

739

2023.10.18

cookie与session的区别
cookie与session的区别

本专题整合了cookie与session的区别和使用方法等相关内容,阅读专题下面的文章了解更详细的内容。

88

2025.08.19

pdf怎么转换成xml格式
pdf怎么转换成xml格式

将 pdf 转换为 xml 的方法:1. 使用在线转换器;2. 使用桌面软件(如 adobe acrobat、itext);3. 使用命令行工具(如 pdftoxml)。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1879

2024.04.01

xml怎么变成word
xml怎么变成word

步骤:1. 导入 xml 文件;2. 选择 xml 结构;3. 映射 xml 元素到 word 元素;4. 生成 word 文档。提示:确保 xml 文件结构良好,并预览 word 文档以验证转换是否成功。想了解更多xml的相关内容,可以阅读本专题下面的文章。

2086

2024.08.01

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

9

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.6万人学习

C# 教程
C# 教程

共94课时 | 6.9万人学习

Java 教程
Java 教程

共578课时 | 46.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号