1. 概述

Apache Camel 是一个Java框架,能轻松实现各种企业集成模式(EIP),为企业集成提供解决方案。

在集成模式中,常见任务是根据运行时的特定规则和条件确定消息路由。Apache Camel通过提供动态路由器EIP的实现简化了这一过程。

本文将深入探讨如何在Apache Camel中实现动态路由,并通过示例进行演示。

2. 理解动态路由器

有时我们需要在运行时根据特定规则将消息发送到不同路由。虽然路由单EIP(Routing Slip)能解决问题,但效率较低,因为它采用试错方式。

在路由单EIP中,消息包含按预定义顺序排列的接口列表。这需要预先配置接口列表,并通过试错方式将消息发送到每个接口。

动态路由器EIP提供了更好的运行时路由实现,尤其在存在多个接收者或无接收者的情况下。它提供了无需预配置严格接口的路由灵活性。

此外,它知道每个目标地址以及将消息路由到特定地址的规则。同时它拥有控制通道,潜在目标地址在启动时通过该通道宣布其存在和参与规则

它还在规则库中存储所有可能目标的规则。当消息到达时,会检查规则库并满足接收者的请求。

以下是动态路由器EIP内部实现的示意图

动态路由器EIP内部实现

常见用例是动态服务发现,客户端应用可通过发送包含服务名称的消息访问服务

动态路由器EIP的核心目标是将路由决策推迟到运行时,而不是预先构建静态规则。

3. Maven依赖

通过在pom.xml中添加camel-corecamel-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"));
    }
}

创建继承RouteBuilderDynamicRouterRoute类。重写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获取。


原始标题:Add Camel Route at Runtime in Java | Baeldung