系统集成是一个很好的挑战。特别是当您正在寻找通信标准和可靠的解决方案时。在当今的微服务世界中,每个人都在谈论 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>
<dependencies>
<module name="javax.api"/>
<module name="sun.jdk" export="false" services="import">
<exports>
<include-set>
<path name="sun/security/acl"/>
<path name="META-INF/services"/>
</include-set>
</exports>
</module>
<module name="com.sun.xml.bind" />
<module name="org.omg.api"/>
<module name="javax.ejb.api" export="false" />
<module name="javax.transaction.api" export="false" />
<module name="javax.jms.api" export="false" />
<module name="javax.xml.stream.api" export="false" />
<module name="org.picketbox" optional="true"/>
<module name="javax.servlet.api" optional="true"/>
<module name="org.jboss.logging" optional="true"/>
<module name="org.jboss.as.web" optional="true"/>
<module name="org.jboss.as.ejb3" optional="true"/>
<module name="org.hornetq" />
</dependencies>
</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>
<dependencies>
<module name="javax.api"/>
<module name="sun.jdk" export="false" services="import">
<exports>
<include-set>
<path name="sun/security/acl"/>
<path name="META-INF/services"/>
</include-set>
</exports>
</module>
<module name="com.sun.xml.bind" />
<module name="org.omg.api"/>
<module name="javax.ejb.api" export="false" />
<module name="javax.transaction.api" export="false" />
<module name="javax.jms.api" export="false" />
<module name="javax.xml.stream.api" export="false" />
<module name="org.picketbox" optional="true"/>
<module name="javax.servlet.api" optional="true"/>
<module name="org.jboss.logging" optional="true"/>
<module name="org.jboss.as.web" optional="true"/>
<module name="org.jboss.as.ejb3" optional="true"/>
<module name="org.hornetq" />
</dependencies>
</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>
<dependencies>
<module name="javax.api"/>
<module name="sun.jdk" export="false" services="import">
<exports>
<include-set>
<path name="sun/security/acl"/>
<path name="META-INF/services"/>
</include-set>
</exports>
</module>
<module name="com.sun.xml.bind" />
<module name="org.omg.api"/>
<module name="javax.ejb.api" export="false" />
<module name="javax.transaction.api" export="false" />
<module name="javax.jms.api" export="false" />
<module name="javax.xml.stream.api" export="false" />
<module name="org.picketbox" optional="true"/>
<module name="javax.servlet.api" optional="true"/>
<module name="org.jboss.logging" optional="true"/>
<module name="org.jboss.as.web" optional="true"/>
<module name="org.jboss.as.ejb3" optional="true"/>
<module name="org.hornetq" />
</dependencies>
</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>
<dependencies>
<module name="javax.api"/>
<module name="sun.jdk" export="false" services="import">
<exports>
<include-set>
<path name="sun/security/acl"/>
<path name="META-INF/services"/>
</include-set>
</exports>
</module>
<module name="com.sun.xml.bind" />
<module name="org.omg.api"/>
<module name="javax.ejb.api" export="false" />
<module name="javax.transaction.api" export="false" />
<module name="javax.jms.api" export="false" />
<module name="javax.xml.stream.api" export="false" />
<module name="org.picketbox" optional="true"/>
<module name="javax.servlet.api" optional="true"/>
<module name="org.jboss.logging" optional="true"/>
<module name="org.jboss.as.web" optional="true"/>
<module name="org.jboss.as.ejb3" optional="true"/>
<module name="org.hornetq" />
</dependencies>
</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>
<dependencies>
<module name="javax.api"/>
<module name="sun.jdk" export="false" services="import">
<exports>
<include-set>
<path name="sun/security/acl"/>
<path name="META-INF/services"/>
</include-set>
</exports>
</module>
<module name="com.sun.xml.bind" />
<module name="org.omg.api"/>
<module name="javax.ejb.api" export="false" />
<module name="javax.transaction.api" export="false" />
<module name="javax.jms.api" export="false" />
<module name="javax.xml.stream.api" export="false" />
<module name="org.picketbox" optional="true"/>
<module name="javax.servlet.api" optional="true"/>
<module name="org.jboss.logging" optional="true"/>
<module name="org.jboss.as.web" optional="true"/>
<module name="org.jboss.as.ejb3" optional="true"/>
<module name="org.hornetq" />
</dependencies>
</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>
<dependencies>
<module name="javax.api"/>
<module name="sun.jdk" export="false" services="import">
<exports>
<include-set>
<path name="sun/security/acl"/>
<path name="META-INF/services"/>
</include-set>
</exports>
</module>
<module name="com.sun.xml.bind" />
<module name="org.omg.api"/>
<module name="javax.ejb.api" export="false" />
<module name="javax.transaction.api" export="false" />
<module name="javax.jms.api" export="false" />
<module name="javax.xml.stream.api" export="false" />
<module name="org.picketbox" optional="true"/>
<module name="javax.servlet.api" optional="true"/>
<module name="org.jboss.logging" optional="true"/>
<module name="org.jboss.as.web" optional="true"/>
<module name="org.jboss.as.ejb3" optional="true"/>
<module name="org.hornetq" />
</dependencies>
</module>
另一件重要的事情是,如果您尝试在桥以外的任何其他范围内使用自定义模块,您将在 WildFly 上遇到类加载问题。所以,请密切注意,不要在其他地方使用它。
该网桥配置了相对较大的故障重试间隔和最大重试次数。这是一种解决方法。如果 WildFly 启动太快,并且桥在实际配置队列之前尝试访问本地 sourceQ,则会导致异常。