1. 概述
Apache Camel 是一个Java框架,能轻松实现各种企业集成模式(EIP),为企业集成提供解决方案。
在集成模式中,常见任务是根据运行时的特定规则和条件确定消息路由。Apache Camel通过提供动态路由器EIP的实现简化了这一过程。
本文将深入探讨如何在Apache Camel中实现动态路由,并通过示例进行演示。
2. 理解动态路由器
有时我们需要在运行时根据特定规则将消息发送到不同路由。虽然路由单EIP(Routing Slip)能解决问题,但效率较低,因为它采用试错方式。
在路由单EIP中,消息包含按预定义顺序排列的接口列表。这需要预先配置接口列表,并通过试错方式将消息发送到每个接口。
动态路由器EIP提供了更好的运行时路由实现,尤其在存在多个接收者或无接收者的情况下。它提供了无需预配置严格接口的路由灵活性。
此外,它知道每个目标地址以及将消息路由到特定地址的规则。同时它拥有控制通道,潜在目标地址在启动时通过该通道宣布其存在和参与规则。
它还在规则库中存储所有可能目标的规则。当消息到达时,会检查规则库并满足接收者的请求。
以下是动态路由器EIP内部实现的示意图:
常见用例是动态服务发现,客户端应用可通过发送包含服务名称的消息访问服务。
动态路由器EIP的核心目标是将路由决策推迟到运行时,而不是预先构建静态规则。
3. Maven依赖
通过在pom.xml中添加camel-core和camel-test-junit5来启动一个简单的Apache Camel项目:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-junit5</artifactId>
<version>4.3.0</version>
</dependency>
Camel Core提供动态路由器EIP及其他路由功能,Camel Test JUnit5通过CamelSupport接口简化消息路由测试。
注意:也可将Camel项目作为Spring Boot项目启动。
4. 使用动态路由器在运行时添加路由
动态路由器EIP确保我们为集成系统指定规则,以便在运行时正确匹配到特定路由。它会检查传入消息体并将其匹配到路由。
4.1 配置
首先创建名为DynamicRouteBean的类,并添加定义规则和条件的方法:
class DynamicRouterBean {
public String route(String body, @ExchangeProperties Map<String, Object> properties) {
int invoked = (int) properties.getOrDefault("invoked", 0) + 1;
properties.put("invoked", invoked);
if (invoked == 1) {
switch (body.toLowerCase()) {
case "mock":
return "mock:dynamicRouter";
case "direct":
return "mock:directDynamicRouter";
case "seda":
return "mock:sedaDynamicRouter";
case "file":
return "mock:fileDynamicRouter";
default:
break;
}
}
return null;
}
}
上述代码根据传入消息体和当前调用次数动态确定路由。*route()*方法检查消息体是否匹配预定义关键词规则,并返回对应路由。
我们使用*@ExchangeProperties*注解Map对象。该映射用作容器,存储和检索当前交换状态并更新调用次数。
invoked变量表示route()方法被调用的次数。**如果消息匹配预定义条件且是首次调用,则返回对应路由。invoked == 1检查确保动态路由逻辑仅在首次调用时执行**。这简化了代码并防止不必要的重复执行。
*为防止动态路由器无限执行,路由到适当接口后route()方法返回null*。这确保基于消息识别路由后动态路由终止**。
简单说,route()方法会为每个交换调用,直到返回null。
最后在Camel路由构建器中配置动态路由器EIP:
class DynamicRouterRoute extends RouteBuilder {
@Override
void configure() {
from("direct:dynamicRouter")
.dynamicRouter(method(DynamicRouterBean.class, "route"));
}
}
创建继承RouteBuilder的DynamicRouterRoute类。重写configure方法,通过调用*dynamicRouter()方法添加动态路由bean,将动态路由器调用连接到自定义route()*方法。
也可在定义规则的方法上使用*@DynamicRouter*注解:
class DynamicRouterBean {
@DynamicRouter
String route(String body, @ExchangeProperties Map<String, Object> properties) {
// ...
}
}
该注解无需在Camel路由中显式配置*dynamicRouter()*方法:
// ...
@Override
void configure() {
from("direct:dynamicRouter").bean(DynamicRouterBean.class, "route");
}
// ...
使用bean()方法指定包含路由逻辑的类。由于route()方法带有@DynamicRouter注解,不再需要*dynamicRouter()*方法。
4.2 单元测试
编写单元测试验证条件是否满足。首先确保测试类继承CamelTestSupport:
class DynamicRouterRouteUnitTest extends CamelTestSupport {
@Override
protected RoutesBuilder createRouteBuilder() {
return new DynamicRouterRoute();
}
}
提供DynamicRouterRoute实例作为测试用的路由构建器。
测试消息体为"mock"的情况:
@Test
void whenMockEndpointExpectedMessageCountOneAndMockAsMessageBody_thenMessageSentToDynamicRouter() throws InterruptedException {
MockEndpoint mockDynamicEndpoint = getMockEndpoint("mock:dynamicRouter");
mockDynamicEndpoint.expectedMessageCount(1);
template.send("direct:dynamicRouter", exchange -> exchange.getIn().setBody("mock"));
MockEndpoint.assertIsSatisfied(context);
}
模拟动态接口并设置预期消息路由。设置预期消息计数为1。最后设置包含预期消息体的传入消息路由,并断言MockEndpoint路由满足条件。
测试"mock:directDynamicRouter"消息路由:
@Test
void whenMockEndpointExpectedMessageCountOneAndDirectAsMessageBody_thenMessageSentToDynamicRouter() throws InterruptedException {
MockEndpoint mockDynamicEndpoint = context.getEndpoint("mock:directDynamicRouter", MockEndpoint.class);
mockDynamicEndpoint.expectedMessageCount(1);
template.send("direct:dynamicRouter", exchange -> exchange.getIn().setBody("direct"));
MockEndpoint.assertIsSatisfied(context);
}
该测试验证当"direct"作为消息体发送时,会动态路由到"mock:directDynamicRouter"接口。设置预期消息计数为1,表示接口应接收的消息交换次数。
5. 总结
本文学习了如何使用动态路由器EIP在Apache Camel中运行时添加路由。与采用试错方式发送消息的路由单不同,动态路由器EIP提供了基于特定规则和条件路由到接口的可靠实现。
完整示例代码可在GitHub获取。