使用 Camel 从 WildFly 8 向 WebLogic 12 发送 JMS 消息

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

系统集成是一个很好的挑战。特别是当您正在寻找通信标准和可靠的解决方案时。在当今的微服务世界中,每个人都在谈论 REST 服务和基于 http 的协议。事实上,对于大多数企业项目来说,这永远不够,因为它们通常具有更复杂的需求集。一个合理的解决方案是基于 Java 消息服务的集成。虽然我们不再关注集中式基础架构和 ESB,但我们希望基于点对点的集成来定义服务。让我们看看我们是否可以完成这项工作并在 JBoss WildFly 和 Oracle WebLogic Server 之间发送消息。


商业案例——从 Java EE 到微服务

但我想先退后一步:为什么要有人?我认为,这种情况背后的主要动机之一是迁移路径缓慢。从单一的单一平台应用程序一路走下来,我们希望足够灵活,以便从那些巨大的安装中剥离出单独的服务,并将它们作为服务提供。假设这甚至是可能的,并且遗留应用程序具有不错的设计。或者我们想推进个人服务,比方说从技术角度来看。在这个特定的示例中,我们迫不及待地想让 Java EE 7 特性进入我们的应用程序,而 WebLogic 仍然主要停留在 EE 6 上。我们可以使用 REST 服务甚至 WebServices 来做到这一点,但我们可能想要更多。这就是 JMS 规范的 用武之地。


WildFly 中的 Oracle JMS 客户端库

为了在两个不同的服务器之间发送消息,您需要将各个客户端库集成到发送端。对于 WebLogic,这是 WebLogic JMS 瘦客户端 (wljmsclient.jar)。提供 Java EE 和 WebLogic JMS 功能,使用比 WebLogic Install 或 Full 客户端小得多的客户端占用空间,以及比 Thin T3 客户端稍微小的客户端占用空间。事实上,它包含的 Java EE JMS API 和实现将直接与 WildFly 提供的那些相冲突。要使用它们,我们必须将它们打包为一个模块,并在 HornetQ 中配置一个 JMS Bridge 以准确使用它。首先是添加新模块。将文件夹更改为 wildfly-8.2.0.Final\modules\system\layers\base 并在其下创建一个新的文件夹结构:custom\oracle\weblogic\main。从此处的 %MW_HOME%\server\lib 文件夹中复制 wlthint3client.jar。现在您必须添加一个模块描述符文件 module.xml:


 <module xmlns="urn:jboss:module:2.0" name="custom.oracle.weblogic">
    <resources>
        <resource-root path="wlthint3client.jar">
            <filter>
                <exclude-set>
                    <path name="javax.ejb"/>
                    <path name="javax.ejb.spi"/>
                    <path name="javax.transaction"/>
                    <path name="javax.jms"/>
                    <path name="javax.xml"/>
                    <path name="javax.xml.stream"/>
                </exclude-set>
            </filter>
        </resource-root>
    </resources>
&lt;dependencies&gt;
    &lt;module name="javax.api"/&gt;
    &lt;module name="sun.jdk" export="false" services="import"&gt;
        &lt;exports&gt;
            &lt;include-set&gt;
                &lt;path name="sun/security/acl"/&gt;
                &lt;path name="META-INF/services"/&gt;
            &lt;/include-set&gt;
        &lt;/exports&gt;
    &lt;/module&gt;
    &lt;module name="com.sun.xml.bind" /&gt;
    &lt;module name="org.omg.api"/&gt;
    &lt;module name="javax.ejb.api" export="false"   /&gt;
    &lt;module name="javax.transaction.api"  export="false" /&gt;
    &lt;module name="javax.jms.api"  export="false" /&gt;
    &lt;module name="javax.xml.stream.api" export="false"  /&gt;
    &lt;module name="org.picketbox" optional="true"/&gt;
    &lt;module name="javax.servlet.api" optional="true"/&gt;
    &lt;module name="org.jboss.logging" optional="true"/&gt;
    &lt;module name="org.jboss.as.web" optional="true"/&gt;
    &lt;module name="org.jboss.as.ejb3" optional="true"/&gt;
    &lt;module name="org.hornetq" /&gt;
&lt;/dependencies&gt;

</module>

该文件定义了所有必需的资源和依赖项以及相关的排除项。如果这样做了,我们终于需要消息桥了。


HornetQ JMS 消息桥

JMS 桥 的功能是使用来自源 JMS 目的地的消息,并将它们发送到目标 JMS 目的地。通常源或目标位于不同的服务器上。该桥还可以用于桥接来自其他非 HornetQ JMS 服务器的消息,只要它们符合 JMS 1.1。打开 standalone-full.xml 并将以下配置添加到消息传递子系统:


 <module xmlns="urn:jboss:module:2.0" name="custom.oracle.weblogic">
    <resources>
        <resource-root path="wlthint3client.jar">
            <filter>
                <exclude-set>
                    <path name="javax.ejb"/>
                    <path name="javax.ejb.spi"/>
                    <path name="javax.transaction"/>
                    <path name="javax.jms"/>
                    <path name="javax.xml"/>
                    <path name="javax.xml.stream"/>
                </exclude-set>
            </filter>
        </resource-root>
    </resources>
&lt;dependencies&gt;
    &lt;module name="javax.api"/&gt;
    &lt;module name="sun.jdk" export="false" services="import"&gt;
        &lt;exports&gt;
            &lt;include-set&gt;
                &lt;path name="sun/security/acl"/&gt;
                &lt;path name="META-INF/services"/&gt;
            &lt;/include-set&gt;
        &lt;/exports&gt;
    &lt;/module&gt;
    &lt;module name="com.sun.xml.bind" /&gt;
    &lt;module name="org.omg.api"/&gt;
    &lt;module name="javax.ejb.api" export="false"   /&gt;
    &lt;module name="javax.transaction.api"  export="false" /&gt;
    &lt;module name="javax.jms.api"  export="false" /&gt;
    &lt;module name="javax.xml.stream.api" export="false"  /&gt;
    &lt;module name="org.picketbox" optional="true"/&gt;
    &lt;module name="javax.servlet.api" optional="true"/&gt;
    &lt;module name="org.jboss.logging" optional="true"/&gt;
    &lt;module name="org.jboss.as.web" optional="true"/&gt;
    &lt;module name="org.jboss.as.ejb3" optional="true"/&gt;
    &lt;module name="org.hornetq" /&gt;
&lt;/dependencies&gt;

</module>

如您所见,它直接引用模块并具有源和目标定义。源是在消息传递子系统中定义的 WildFly 本地消息队列:


 <module xmlns="urn:jboss:module:2.0" name="custom.oracle.weblogic">
    <resources>
        <resource-root path="wlthint3client.jar">
            <filter>
                <exclude-set>
                    <path name="javax.ejb"/>
                    <path name="javax.ejb.spi"/>
                    <path name="javax.transaction"/>
                    <path name="javax.jms"/>
                    <path name="javax.xml"/>
                    <path name="javax.xml.stream"/>
                </exclude-set>
            </filter>
        </resource-root>
    </resources>
&lt;dependencies&gt;
    &lt;module name="javax.api"/&gt;
    &lt;module name="sun.jdk" export="false" services="import"&gt;
        &lt;exports&gt;
            &lt;include-set&gt;
                &lt;path name="sun/security/acl"/&gt;
                &lt;path name="META-INF/services"/&gt;
            &lt;/include-set&gt;
        &lt;/exports&gt;
    &lt;/module&gt;
    &lt;module name="com.sun.xml.bind" /&gt;
    &lt;module name="org.omg.api"/&gt;
    &lt;module name="javax.ejb.api" export="false"   /&gt;
    &lt;module name="javax.transaction.api"  export="false" /&gt;
    &lt;module name="javax.jms.api"  export="false" /&gt;
    &lt;module name="javax.xml.stream.api" export="false"  /&gt;
    &lt;module name="org.picketbox" optional="true"/&gt;
    &lt;module name="javax.servlet.api" optional="true"/&gt;
    &lt;module name="org.jboss.logging" optional="true"/&gt;
    &lt;module name="org.jboss.as.web" optional="true"/&gt;
    &lt;module name="org.jboss.as.ejb3" optional="true"/&gt;
    &lt;module name="org.hornetq" /&gt;
&lt;/dependencies&gt;

</module>

而目标是在WebLogic Server 中定义的远程队列和连接工厂。我假设您知道该怎么做,如果不知道,请 参阅此文档 。差不多就是这样。现在我们需要向我们的本地队列发送一条消息,这将通过网桥发送到 WebLogic 队列。


测试桥梁 - 用骆驼

将消息驱动的 bean 部署到 WebLogic(是的,您必须将其作为 ejb jar 打包到 ear 中以及所有这些)。这个特定的示例只是将消息文本转储到记录器。


 <module xmlns="urn:jboss:module:2.0" name="custom.oracle.weblogic">
    <resources>
        <resource-root path="wlthint3client.jar">
            <filter>
                <exclude-set>
                    <path name="javax.ejb"/>
                    <path name="javax.ejb.spi"/>
                    <path name="javax.transaction"/>
                    <path name="javax.jms"/>
                    <path name="javax.xml"/>
                    <path name="javax.xml.stream"/>
                </exclude-set>
            </filter>
        </resource-root>
    </resources>
&lt;dependencies&gt;
    &lt;module name="javax.api"/&gt;
    &lt;module name="sun.jdk" export="false" services="import"&gt;
        &lt;exports&gt;
            &lt;include-set&gt;
                &lt;path name="sun/security/acl"/&gt;
                &lt;path name="META-INF/services"/&gt;
            &lt;/include-set&gt;
        &lt;/exports&gt;
    &lt;/module&gt;
    &lt;module name="com.sun.xml.bind" /&gt;
    &lt;module name="org.omg.api"/&gt;
    &lt;module name="javax.ejb.api" export="false"   /&gt;
    &lt;module name="javax.transaction.api"  export="false" /&gt;
    &lt;module name="javax.jms.api"  export="false" /&gt;
    &lt;module name="javax.xml.stream.api" export="false"  /&gt;
    &lt;module name="org.picketbox" optional="true"/&gt;
    &lt;module name="javax.servlet.api" optional="true"/&gt;
    &lt;module name="org.jboss.logging" optional="true"/&gt;
    &lt;module name="org.jboss.as.web" optional="true"/&gt;
    &lt;module name="org.jboss.as.ejb3" optional="true"/&gt;
    &lt;module name="org.hornetq" /&gt;
&lt;/dependencies&gt;

</module>

现在我们需要 WildFly 服务器上的生产者。这样做,我实际上是在使用 WildFly-Camel JMS 集成


 <module xmlns="urn:jboss:module:2.0" name="custom.oracle.weblogic">
    <resources>
        <resource-root path="wlthint3client.jar">
            <filter>
                <exclude-set>
                    <path name="javax.ejb"/>
                    <path name="javax.ejb.spi"/>
                    <path name="javax.transaction"/>
                    <path name="javax.jms"/>
                    <path name="javax.xml"/>
                    <path name="javax.xml.stream"/>
                </exclude-set>
            </filter>
        </resource-root>
    </resources>
&lt;dependencies&gt;
    &lt;module name="javax.api"/&gt;
    &lt;module name="sun.jdk" export="false" services="import"&gt;
        &lt;exports&gt;
            &lt;include-set&gt;
                &lt;path name="sun/security/acl"/&gt;
                &lt;path name="META-INF/services"/&gt;
            &lt;/include-set&gt;
        &lt;/exports&gt;
    &lt;/module&gt;
    &lt;module name="com.sun.xml.bind" /&gt;
    &lt;module name="org.omg.api"/&gt;
    &lt;module name="javax.ejb.api" export="false"   /&gt;
    &lt;module name="javax.transaction.api"  export="false" /&gt;
    &lt;module name="javax.jms.api"  export="false" /&gt;
    &lt;module name="javax.xml.stream.api" export="false"  /&gt;
    &lt;module name="org.picketbox" optional="true"/&gt;
    &lt;module name="javax.servlet.api" optional="true"/&gt;
    &lt;module name="org.jboss.logging" optional="true"/&gt;
    &lt;module name="org.jboss.as.web" optional="true"/&gt;
    &lt;module name="org.jboss.as.ejb3" optional="true"/&gt;
    &lt;module name="org.hornetq" /&gt;
&lt;/dependencies&gt;

</module>

这就是全部的魔力。计时器将 JSON 文本消息发送到桥接到 WebLogic 的本地队列。



更多提示

如果您想在没有网桥的情况下测试 WebLogic 队列,则必须将 wljmsclient 包含到您的项目中。由于这在 Maven 存储库 (AFAIK) 中不可用,您可以简单地在本地安装它:


 <module xmlns="urn:jboss:module:2.0" name="custom.oracle.weblogic">
    <resources>
        <resource-root path="wlthint3client.jar">
            <filter>
                <exclude-set>
                    <path name="javax.ejb"/>
                    <path name="javax.ejb.spi"/>
                    <path name="javax.transaction"/>
                    <path name="javax.jms"/>
                    <path name="javax.xml"/>
                    <path name="javax.xml.stream"/>
                </exclude-set>
            </filter>
        </resource-root>
    </resources>
&lt;dependencies&gt;
    &lt;module name="javax.api"/&gt;
    &lt;module name="sun.jdk" export="false" services="import"&gt;
        &lt;exports&gt;
            &lt;include-set&gt;
                &lt;path name="sun/security/acl"/&gt;
                &lt;path name="META-INF/services"/&gt;
            &lt;/include-set&gt;
        &lt;/exports&gt;
    &lt;/module&gt;
    &lt;module name="com.sun.xml.bind" /&gt;
    &lt;module name="org.omg.api"/&gt;
    &lt;module name="javax.ejb.api" export="false"   /&gt;
    &lt;module name="javax.transaction.api"  export="false" /&gt;
    &lt;module name="javax.jms.api"  export="false" /&gt;
    &lt;module name="javax.xml.stream.api" export="false"  /&gt;
    &lt;module name="org.picketbox" optional="true"/&gt;
    &lt;module name="javax.servlet.api" optional="true"/&gt;
    &lt;module name="org.jboss.logging" optional="true"/&gt;
    &lt;module name="org.jboss.as.web" optional="true"/&gt;
    &lt;module name="org.jboss.as.ejb3" optional="true"/&gt;
    &lt;module name="org.hornetq" /&gt;
&lt;/dependencies&gt;

</module>

另一件重要的事情是,如果您尝试在桥以外的任何其他范围内使用自定义模块,您将在 WildFly 上遇到类加载问题。所以,请密切注意,不要在其他地方使用它。

该网桥配置了相对较大的故障重试间隔和最大重试次数。这是一种解决方法。如果 WildFly 启动太快,并且桥在实际配置队列之前尝试访问本地 sourceQ,则会导致异常。

在我的 GitHub 帐户中找到完整的源代码