系统集成是一个很好的挑战。 特别是当您在寻找通信标准和可靠的解决方案时。 在当今的微服务世界中,每个人都在谈论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)。 与WebLogic Install或Full客户端相比,使用较小的客户端资源提供了Java EE和WebLogic JMS功能,而与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目标。 通常,源或目标目标位于不同的服务器上。 只要它们兼容JMS 1.1,该桥接器还可用于桥接来自其他非HornetQ JMS服务器的消息。 打开standalone-full.xml并将以下配置添加到消息传递子系统:
<jms-bridge name="wls-bridge" module="custom.oracle.weblogic">
<source>
<connection-factory name="java:/ConnectionFactory"/>
<destination name="java:/jms/sourceQ"/>
</source>
<target>
<connection-factory name="jms/WFMessagesCF"/>
<destination name="jms/WFMessages"/>
<context>
<property key="java.naming.factory.initial"
value="weblogic.jndi.WLInitialContextFactory"/>
<property key="java.naming.provider.url"
value="t3://127.0.0.1:7001"/>
</context>
</target>
<quality-of-service>AT_MOST_ONCE</quality-of-service>
<failure-retry-interval>2000</failure-retry-interval>
<max-retries>10</max-retries>
<max-batch-size>500</max-batch-size>
<max-batch-time>500</max-batch-time>
<add-messageID-in-header>true</add-messageID-in-header>
</jms-bridge>
如您所见,它直接引用模块,并具有源和目标定义。 源是在消息收发子系统中定义的WildFly本地消息队列:
<jms-queue name="sourceQ">
<entry name="java:/jms/sourceQ"/>
</jms-queue>
而目标是在WebLogic Server中定义的远程队列以及连接工厂。 我假设您知道该怎么做,否则请参阅本文档 。 就是这样。 现在,我们需要向本地队列发送一条消息,而该消息将通过网桥发送到WebLogic队列。
测试桥–使用骆驼
将消息驱动的bean部署到WebLogic(是的,您必须将其作为ejb jar打包到耳朵中,所有这些)。 这个特定的示例只是将消息文本转储到记录器中。
@MessageDriven(mappedName = "jms/WFMessages", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class LogMessageBean implements MessageListener {
private final static Logger LOGGER = Logger.getLogger(LogMessageBean.class.getName());
public LogMessageBean() {
}
@Override
public void onMessage(Message message) {
TextMessage text = (TextMessage) message;
try {
LOGGER.log(Level.INFO, text.getText());
} catch (JMSException jmxe) {
LOGGER.log(Level.SEVERE, jmxe.getMessage());
}
}
}
现在,我们需要WildFly服务器上的生产者。 这样做,我实际上是在使用WildFly-Camel JMS集成 。
@Startup
@ApplicationScoped
@ContextName("jms-camel-context")
public class JMSRouteBuilder extends RouteBuilder {
@Override
public void configure() throws Exception {
// Initial Context Lookup
Context ic = new InitialContext();
ConnectionFactory cf = (ConnectionFactory) ic.lookup("/ConnectionFactory");
// Create the JMS Component
JmsComponent component = new JmsComponent();
component.setConnectionFactory(cf);
getContext().addComponent("jms", component);
// Build A JSON Greeting
JsonObject text = Json.createObjectBuilder()
.add("Greeting", "From WildFly 8").build();
// Send a Message from timer to Queue
from("timer://sendJMSMessage?fixedRate=true.=10000")
.transform(constant(text.toString()))
.to("jms:queue:sourceQ")
.log("JMS Message sent");
}
}
这就是整个魔术。 计时器将JSON文本消息发送到本地队列,该消息将桥接到WebLogic。
更多提示
如果要在没有网桥的情况下测试WebLogic队列,则必须将wljmsclient包含到您的项目中。 由于这在Maven存储库(AFAIK)中不可用,因此您可以简单地在本地安装它:
mvn install:install-file -Dfile=%MW_HOME%/wlserver/server/lib/wlthint3client.jar -DgeneratePom=true -DgroupId=custom.com.oracle -DartifactId=wlthint3client -Dversion=12.1.3 -Dpackaging=jar
另一个重要的事情是,如果您尝试在桥以外的其他范围内使用自定义模块,则会在WildFly上遇到类加载问题。 因此,请密切注意,不要在其他地方使用它。
该网桥配置了较大的故障重试间隔和最大重试次数。 这是一种解决方法。 如果WildFly启动太快,并且网桥在实际配置队列之前尝试访问本地sourceQ,则会导致异常。