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

Wildfly集群实现单消息触发各节点MDB接收的方案咨询

问题:Wildfly集群中JMS Topic消息仅触发单个节点MDB,需实现全节点接收

我使用Wildfly 25.0.1.preview,通过JMS Topic发送消息到集群,但每条消息仅随机触发一个节点的MDB接收。集群配置无问题,推测是默认行为,想实现单消息触发集群所有节点MDB接收的需求,也可考虑JMS替代方案。

以下是standalone-full-ha.xml中的messaging-activemq配置段:

<subsystem xmlns="urn:jboss:domain:messaging-activemq:13.0">
    <server name="default" persistence-enabled="false">
        <security elytron-domain="ApplicationDomain"/>
        <journal file-size="1024"/>
        <replication-master cluster-name="messaging-cluster" group-name="activemq-group"/>
        <security-setting name="#">
            <role name="guest" send="true" consume="true" create-non-durable-queue="true" delete-non-durable-queue="true" 
                create-durable-queue="true" delete-durable-queue="true"/>
        </security-setting>
        <address-setting name="#" dead-letter-address="jms.queue.DLQ" expiry-address="jms.queue.ExpiryQueue" max-size-bytes="10485760" page-size-bytes="2097152" message-counter-history-day-limit="10"/>
        <http-connector name="http-connector" socket-binding="http" endpoint="http-acceptor"/>
        <http-connector name="http-connector-throughput" socket-binding="http" endpoint="http-acceptor-throughput">
            <param name="batch-delay" value="50"/>
        </http-connector>
        <in-vm-connector name="in-vm" server-id="0"/>
        <http-acceptor name="http-acceptor" http-listener="default"/>
        <http-acceptor name="http-acceptor-throughput" http-listener="default">
            <param name="batch-delay" value="50"/>
            <param name="direct-deliver" value="false"/>
        </http-acceptor>
        <in-vm-acceptor name="in-vm" server-id="0"/>
        <jgroups-broadcast-group name="bg-group1" jgroups-cluster="activemq-cluster" connectors="http-connector"/>
        <jgroups-discovery-group name="dg-group1" jgroups-cluster="activemq-cluster"/>
        <cluster-connection name="messaging-cluster" address="jms" connector-name="http-connector" discovery-group="dg-group1"/>
        <jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
        <jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
        <jms-topic name="sfereTopic" entries="jms/topic/sfere java:jboss/exported/jms/topic/sfere"/>
        <connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory" connectors="in-vm"/>
        <connection-factory name="RemoteConnectionFactory" entries="java:jboss/exported/jms/RemoteConnectionFactory" connectors="http-connector" ha="true" block-on-acknowledge="true" reconnect-attempts="-1"/>
        <pooled-connection-factory name="activemq-ra" entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="in-vm" transaction="xa"/>
    </server>
</subsystem>

MDB代码片段:

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/topic/sfere"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "jakarta.jms.Topic") })
public class SfereMessageDrivenBean implements MessageListener {

@Override
public void onMessage(Message message) {
    // 消息处理逻辑
}
}

解决方案

1. 调整MDB订阅配置(推荐JMS原生方案)

默认情况下,集群中同一MDB对Topic的订阅是共享非持久化订阅,消息会被负载均衡到单个节点。要让每个节点的MDB独立接收消息,需配置持久化订阅并保证每个节点的客户端ID唯一:

修改MDB的activationConfig,添加以下配置:

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/topic/sfere"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "jakarta.jms.Topic"),
    // 启用持久化订阅
    @ActivationConfigProperty(propertyName = "subscriptionDurability", propertyValue = "Durable"),
    // 使用节点名称作为客户端ID后缀,保证每个节点唯一
    @ActivationConfigProperty(propertyName = "clientId", propertyValue = "${jboss.node.name}-sfere-mdb"),
    // 指定订阅名称,每个节点的订阅名称可一致
    @ActivationConfigProperty(propertyName = "subscriptionName", propertyValue = "sfere-topic-subscription")
})

原理说明

  • 持久化订阅会在Broker中注册独立的订阅者,每个节点的MDB对应一个唯一订阅者,Broker会将消息推送给所有订阅者。
  • ${jboss.node.name}是Wildfly内置系统属性,每个集群节点的名称唯一,确保客户端ID不重复。

2. 使用Artemis广播转发配置

通过配置Artemis的divert,将Topic消息转发到每个节点的本地队列,让MDB监听本地队列实现全节点接收:

  1. 在standalone-full-ha.xml的messaging-activemq subsystem中添加本地队列和divert:
<!-- 每个节点创建唯一的本地队列 -->
<jms-queue name="sfereLocalQueue-${jboss.node.name}" entries="java:/jms/queue/sfereLocal"/>

<!-- 创建divert,将Topic消息转发到所有节点的本地队列 -->
<divert name="sfereTopicToLocalQueue" address="jms.topic.sfere" forwarding-address="jms.queue.sfereLocal-${jboss.node.name}" exclusive="false"/>
  1. 修改MDB监听本地队列:
@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "java:/jms/queue/sfereLocal"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "jakarta.jms.Queue")
})

3. 替代方案:JGroups集群广播

若不需要JMS的消息持久化、事务等特性,可直接使用Wildfly内置的JGroups实现集群广播:

  1. 注入JGroups Channel:
@Inject
@JChannel("ee")
private JChannel channel;
  1. 发送广播消息:
channel.send(null, channel.getView().getMembers(), "集群广播消息内容".getBytes());
  1. 接收消息(实现Receiver接口):
public class SfereClusterReceiver implements Receiver {
    @Override
    public void receive(Message msg) {
        String content = new String(msg.getBuffer());
        // 处理广播消息
    }
}

// 在启动时注册Receiver
channel.setReceiver(new SfereClusterReceiver());

配置错误修正
  1. 集群名称不一致:replication-master的cluster-name="messaging-cluster"与jgroups-broadcast-group、jgroups-discovery-group的jgroups-cluster="activemq-cluster"名称不匹配,需统一为同一名称(如messaging-cluster),否则集群节点无法正确发现。

  2. 持久化关闭风险:persistence-enabled="false"关闭了消息持久化,集群节点重启后会丢失未处理的消息。若需消息可靠性,建议设置为true并配置持久化存储路径。

  3. 广播组配置优化:jgroups-broadcast-group的connectors仅配置了http-connector,若集群节点在同一局域网,可添加in-vm-connector提升内部通信效率。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 09:20:19