无法将消息发送到 JBoss EAP 7.2 中嵌入的远程 ActiveMQ Artemis

Unable to send message to remote ActiveMQ Artemis embedded in JBoss EAP 7.2

我正在尝试将 JMS 消息发送到 JBoss EAP 7.2 中嵌入的远程 ActiveMQ Artemis 队列。我在 standalone-full.xml 中定义了以下内容:

  1. 远程连接器
  2. 池化连接工厂
  3. 远程绑定
<subsystem xmlns="urn:jboss:domain:messaging-activemq:4.0">
    <server name="default">
        <journal pool-files="10"/>
        <security-setting name="#">
            <role name="guest" send="true" consume="true" create-non-durable-queue="true" delete-non-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>
        <remote-connector name="ImportantMessages-remote" socket-binding="ImportantMessages-remote"/>
        <in-vm-connector name="in-vm" server-id="0">
            <param name="buffer-pooling" value="false"/>
        </in-vm-connector>
        <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">
            <param name="buffer-pooling" value="false"/>
        </in-vm-acceptor>
        <jms-queue name="TestQueue" entries="java:/jms/queue/TestQueue"/>
        <jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
        <jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
        <connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory" connectors="in-vm"/>
        <connection-factory name="RemoteConnectionFactory" entries="java:jboss/exported/jms/RemoteConnectionFactory" connectors="http-connector"/>
        <pooled-connection-factory name="activemq-ra" entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="in-vm" transaction="xa"/>
        <pooled-connection-factory name="ImportantMessages-remote" entries="java:/jms/remoteIM" connectors="ImportantMessages-remote"/>
    </server>
</subsystem>
<subsystem xmlns="urn:wildfly:microprofile-config-smallrye:1.0"/>
<subsystem xmlns="urn:wildfly:microprofile-health-smallrye:1.0" security-enabled="false"/>
<subsystem xmlns="urn:wildfly:microprofile-opentracing-smallrye:1.0"/>
<subsystem xmlns="urn:jboss:domain:naming:2.0">
    <bindings>
        <external-context name="java:global/remoteContext" module="org.apache.activemq.artemis" class="javax.naming.InitialContext">
            <environment>
                <property name="java.naming.factory.initial" value="org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"/>
                <property name="java.naming.provider.url" value="tcp://127.0.0.1:61616"/>
                <property name="queue.ImportantMessages" value="ImportantMessages"/>
            </environment>
        </external-context>
        <lookup name="java:/ImportantMessages" lookup="java:global/remoteContext/ImportantMessages"/>
    </bindings>
    <remote-naming/>
</subsystem>

下面是我的客户端程序:

public void sendObjectMessage() {
    Properties initialProperties = new Properties();
    initialProperties.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory");
    initialProperties.put(InitialContext.PROVIDER_URL, "tcp://127.0.0.1:61616");
    try {
        context = new InitialContext(initialProperties);
        factory = (QueueConnectionFactory) context.lookup("java:/jms/remoteIM");
        destination = (Queue) context.lookup("java:global/remoteContext/ImportantMessages");
        connection = factory.createConnection();
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        producer = session.createProducer(destination);
        EventMessage eventMessage = new EventMessage("1", "Message from FirstClient");
        ObjectMessage objectMessage = session.createObjectMessage();
        objectMessage.setObject(eventMessage);
        connection.start();
        producer.send(objectMessage);
        System.out.println(this.getClass().getName() + "has sent a message : " + eventMessage);
    } catch (NamingException e) {
        e.printStackTrace();
    } catch (JMSException e) {
        e.printStackTrace();
    } finally {
        if (context != null) {
            try {
                context.close();
            } catch (NamingException ex) {
                ex.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (JMSException ex) {
                ex.printStackTrace();
            }
        }
    }
}

我遇到以下异常:

javax.naming.NamingException: scheme java not recognized
    at org.apache.activemq.artemis.jndi.ReadOnlyContext.lookup(ReadOnlyContext.java:222)
    at javax.naming.InitialContext.lookup(Unknown Source)
    at com.example.demo.sender.FirstClient.sendObjectMessage(FirstClient.java:41)
    at com.example.demo.controller.MessagingController.sendMessage(MessagingController.java:19)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)

任何人都可以告诉我我在这里做错了什么吗?

为了将 JMS 消息发送到嵌入在 JBoss EAP 中的远程 ActiveMQ Artemis 队列,您可以使用默认值 standalone-full.xml。您不需要设置远程连接器、池化连接工厂、 远程绑定。您只需要使用 JBoss EAP JNDI 上下文从您的客户端查找 jms/RemoteConnectionFactory。需要明确的是,当 ActiveMQ Artemis 嵌入 JBoss 中时,EAP 远程客户端使用 JBoss EAP JNDI 上下文而不是 ActiveMQ Artemis JNDI 上下文,因为 EAP 本身负责所有 JNDI 操作。

您的客户端正在使用 ActiveMQ Artemis JNDI 上下文:

initialProperties.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory");
initialProperties.put(InitialContext.PROVIDER_URL, "tcp://127.0.0.1:61616");

这是不正确的。你应该改用这个:

initialProperties.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
initialProperties.put(InitialContext.PROVIDER_URL, "http-remoting://127.0.0.1:8080");

注意: 您也可以在此处使用 org.jboss.naming.remote.client.InitialContextFactory,因为 class 也随 JBoss EAP 7.2 一起提供。然而,这个 class 确实包含在内,以便与旧版客户端向后兼容。

那么您的客户端正在尝试查找您在 standalone-full.xml 中配置的 pooled-connection-factory:

factory = (QueueConnectionFactory) context.lookup("java:/jms/remoteIM");

这也是不正确的。 pooled-connection-factory 只能 由客户端 运行 在与 JBoss EAP 相同的 JVM 中使用(例如,需要发送消息的 MDB)。您不能简单地从远程客户端查找它并期望获得池化。它不是这样工作的。你应该这样做:

factory = (QueueConnectionFactory) context.lookup("jms/RemoteConnectionFactory");

这将使用名为 RemoteConnectionFactorystandalone-full.xml 中的默认 connection-factory

然后您正在查找甚至没有在消息传递子系统中定义的队列(即 java:global/remoteContext/ImportantMessages)。出于某种原因,您显然已经在 external-context 中配置了它。这对 EAP 来说是错误的。您应该在 standalone-full.xml:

中定义它
<jms-queue name="ImportantMessages" entries="java:jboss/exported/ImportantMessages"/>

然后在您的客户端中使用:

destination = (Queue) context.lookup("ImportantMessages");

最终,standalone-full.xml 中的消息传递子系统应该如下所示:

<subsystem xmlns="urn:jboss:domain:messaging-activemq:4.0">
    <server name="default">
        <journal pool-files="10"/>
        <security-setting name="#">
            <role name="guest" send="true" consume="true" create-non-durable-queue="true" delete-non-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">
            <param name="buffer-pooling" value="false"/>
        </in-vm-connector>
        <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">
            <param name="buffer-pooling" value="false"/>
        </in-vm-acceptor>
        <jms-queue name="ImportantMessages" entries="java:jboss/exported/ImportantMessages"/>
        <jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
        <jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
        <connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory" connectors="in-vm"/>
        <connection-factory name="RemoteConnectionFactory" entries="java:jboss/exported/jms/RemoteConnectionFactory" connectors="http-connector"/>
        <pooled-connection-factory name="activemq-ra" entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="in-vm" transaction="xa"/>   
    </server>
</subsystem>

您不需要您配置的external-context

最后,这是您的客户最终应该看起来的样子:

public void sendObjectMessage() {
    Properties initialProperties = new Properties();
    initialProperties.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
    initialProperties.put(InitialContext.PROVIDER_URL, "http-remoting://127.0.0.1:8080");
    try {
        context = new InitialContext(initialProperties);
        factory = (QueueConnectionFactory) context.lookup("jms/RemoteConnectionFactory");
        destination = (Queue) context.lookup("ImportantMessages");
        connection = factory.createConnection();
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        producer = session.createProducer(destination);
        EventMessage eventMessage = new EventMessage("1", "Message from FirstClient");
        ObjectMessage objectMessage = session.createObjectMessage();
        objectMessage.setObject(eventMessage);
        connection.start();
        producer.send(objectMessage);
        System.out.println(this.getClass().getName() + "has sent a message : " + eventMessage);
    } catch (NamingException e) {
        e.printStackTrace();
    } catch (JMSException e) {
        e.printStackTrace();
    } finally {
        if (context != null) {
            try {
                context.close();
            } catch (NamingException ex) {
                ex.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (JMSException ex) {
                ex.printStackTrace();
            }
        }
    }
}

JBoss EAP 有一堆 "quickstart" 应用程序可用于演示如何使用其功能。这里是 JMS "Hello World" quickstart,您可能会发现它可供参考。

如果您使用 Maven 构建客户端应用程序,那么您应该使用以下依赖项:

<dependency>
    <groupId>org.jboss.eap</groupId>
    <artifactId>wildfly-jms-client-bom</artifactId>
    <type>pom</type>
</dependency>

此外,请记住 JBoss EAP 7.2 中嵌入的 ActiveMQ Artemis 默认情况下是安全的,因此您需要在消息传递子系统中禁用安全性(例如使用 <security enabled="false"/>)或添加一个用户到 ApplicationRealm(例如通过 bin/add-user.sh 脚本)并在 create your connection.

时传递正确的凭据