Apache Camel Conditional Routing – Apache Camel条件性路由

最后修改: 2022年 9月 2日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Apache Camel is a powerful open-source integration framework implementing several of the known Enterprise Integration Patterns.

Apache Camel是一个强大的开源集成框架,实现了几个已知的企业集成模式

Typically when working with message routing using Camel, we’ll want a way to process messages differently based on their content. For this, Camel provides a powerful feature called the Content Based Router from the collection of EIP patterns.

通常情况下,在使用Camel进行消息路由时,我们需要一种方法来根据消息的内容进行不同的处理。为此,Camel从EIP模式的集合中提供了一个强大的功能,称为基于内容的路由器

In this tutorial, we’ll take a look at several ways we can route messages based on some condition.

在本教程中,我们将看一下我们可以根据某些条件来路由消息的几种方式。

2. Dependencies

2.依赖性

All we’ll need to get started is the camel-spring-boot-starter added to our pom.xml:

我们所需要开始的是camel-spring-boot-starter添加到我们的pom.xml

<dependency>
    <groupId>org.apache.camel.springboot</groupId>
     <artifactId>camel-spring-boot-starter</artifactId>
     <version>3.18.1</version>
</dependency>

Then, we’ll need to add the camel-test-spring-junit5 dependency to our pom.xml:

然后,我们需要将camel-test-spring-junit5依赖性添加到我们的pom.xml

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-test-spring-junit5</artifactId>
    <version>3.18.1</version>
</dependency>

As the name suggests, this dependency is specifically for our unit tests.

顾名思义,这个依赖是专门为我们的单元测试准备的。

3. Defining a Simple Camel Spring Boot Application

3.定义一个简单的Camel Spring Boot应用程序

Throughout this tutorial, the focus of our examples will be a simple Apache Camel Spring Boot application.

在本教程中,我们的例子的重点将是一个简单的Apache Camel Spring Boot应用程序。

So let’s start by defining our application entry point:

因此,让我们从定义我们的应用程序入口点开始。

@SpringBootApplication
public class ConditionalRoutingSpringApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConditionalRoutingSpringApplication.class, args);
    }
}

As we can see, this is a standard Spring Boot application.

我们可以看到,这是一个标准的Spring Boot应用程序。

4. Conditional Routing

4.条件性路由

To quickly recap, a route in Apache Camel is a fundamental building block, normally formed of a sequence of steps, executed in order by Camel, that consumes and processes a message.

简单地说,Apache Camel中的路线是一个基本构件,通常由一系列步骤组成,由Camel按顺序执行,用于消费和处理消息。

For example, a route will typically receive a message, using a consumer perhaps from a file on disk or a message queue. Then, Camel executes the rest of the steps in the route, which either processes the message somehow or sends it to other endpoints.

例如,一个路由通常会接收一个消息,使用一个消费者也许来自磁盘上的一个文件或一个消息队列。然后,Camel执行路由中的其他步骤,这些步骤要么以某种方式处理消息,要么将其发送到其他端点。

Without a doubt, we’ll want a way to conditionally route a message based on some fact. For this, Camel provides the choice and when constructs. We can think of this as the equivalent of the if-else statement in Java.

毫无疑问,我们需要一种方法来根据一些事实有条件地路由一个消息。为此,Camel提供了choicewhen结构。我们可以将其视为等同于Java中的if-else语句

With that in mind, let’s go ahead and create our first route with some conditional logic.

考虑到这一点,让我们继续前进,用一些条件逻辑创建我们的第一个路由。

5. Creating a Route

5.创建路线

In this example, we’ll define an elementary route with some conditional logic based on the contents of the message body received:

在这个例子中,我们将根据收到的消息体的内容,定义一个带有一些条件逻辑的基本路由。

@Component
public class ConditionalBodyRouter extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        
        from("direct:start-conditional")
          .routeId("conditional-body-route")
          .choice()
            .when(body().contains("Baeldung"))
              .setBody(simple("Goodbye, Baeldung!"))
              .to("mock:result-body")
            .otherwise()
              .to("mock:result-body")
          .end();
    }
}

As we can see in our trivial example, we configure our route to consume messages from a direct endpoint called start-conditional.

正如我们在微不足道的例子中所看到的,我们将路由配置为从一个名为start-conditional直接端点消费消息。

Now let’s walk through the key parts of our route:

现在,让我们走过我们的路线的关键部分。

  • First, we begin the route using the choice() method – this tells Camel that the following lines will contain some conditions to evaluate
  • Next, the when() method indicates a new condition to be evaluated – in this example, we simply check to see if the message body contains the string Baeldung. We can add as many when conditions as we need
  • To conclude our route, we use the otherwise() method to define what to do when none of the previous when conditions are met.
  • Finally, the route terminates using the end() method, which closes the choice block.

To summarise, when we run our route, if our message body contains the string Baeldung, we’ll set the message body to Goodbye, Baeldung! and send the result to a mock endpoint called result-body.

总而言之,当我们运行我们的路由时,如果我们的消息体包含字符串Baeldung,我们将把消息体设置为Goodbye, Baeldung!并将结果发送到一个名为result-body模拟端点

Alternatively, we’ll just route the original message to our mock endpoint.

另外,我们将直接把原始消息路由到我们的模拟端点。

6. Testing the Route

6.测试路线

With the last section in mind, let’s go ahead and write a unit test to explore how our route behaves:

考虑到最后一节,让我们继续写一个单元测试来探索我们的路由是如何表现的。

@SpringBootTest
@CamelSpringBootTest
class ConditionalBodyRouterUnitTest {

    @Autowired
    private ProducerTemplate template;

    @EndpointInject("mock:result-body")
    private MockEndpoint mock;

    @Test
    void whenSendBodyWithBaeldung_thenGoodbyeMessageReceivedSuccessfully() throws InterruptedException {
        mock.expectedBodiesReceived("Goodbye, Baeldung!");

        template.sendBody("direct:start-conditional", "Hello Baeldung Readers!");

        mock.assertIsSatisfied();
    }
}

As we can see, our test consists of three simple steps:

我们可以看到,我们的测试包括三个简单的步骤。

  • First, let’s set an expectation that our mock endpoint will receive the given message body
  • Then we’ll send a message to our direct:start-conditional endpoint using our template. Note, we’ll ensure our message body contains the string Baeldung
  • To conclude our test, we use the assertIsSatisfied method to validate that our initial expectation on our mock endpoint has been satisfied

This test confirms that our conditional routing is working correctly. Awesome!

这个测试证实了我们的条件性路由工作是正确的。真棒!

Be sure to see our previous tutorial to learn more about how to write reliable, self-contained unit tests for our Camel routes.

请务必参阅我们之前的教程,以了解更多关于如何为我们的Camel路由编写可靠的、独立的单元测试

7. Building Other Conditional Predicates

7.建立其他条件谓语

So far, we’ve explored one option on how to build our when predicate – inspecting the message body of our exchange. However, there are several other options available to us.

到目前为止,我们已经探讨了如何建立我们的当谓词的一个选项 – 检查我们交换的消息体。然而,我们还有其他几种选择。

For example, we can also control our conditions by inspecting the value of a given message header:

例如,我们也可以通过检查特定消息头的值来控制我们的条件:

@Component
public class ConditionalHeaderRouter extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        
        from("direct:start-conditional-header")
            .routeId("conditional-header-route")
            .choice()
              .when(header("fruit").isEqualTo("Apple"))
                .setHeader("favourite", simple("Apples"))
                .to("mock:result")
              .otherwise()
                .setHeader("favourite", header("fruit"))
                .to("mock:result")
            .end();
    }
}

This time around, we’ve modified the when method to look at the value of a header called fruit. It is also perfectly possible to use the Simple language Camel provides in our when conditions.

这一次,我们修改了when方法,以查看一个名为fruit的头的值。也完全可以在我们的when条件中使用Simple language Camel提供的语言。

8. Working With Java Beans 

8.使用Java Bean

Moreover, we can also use the Camel Bean language when we want to use the result of a Java method call in our predicates.

此外,当我们想在谓词中使用Java方法调用的结果时,我们也可以使用Camel Bean语言

First, we need to create a Java bean containing a method that returns a boolean:

首先,我们需要创建一个Java Bean,包含一个返回布尔值的方法。

public class FruitBean {

    public static boolean isApple(Exchange exchange) {
        return "Apple".equals(exchange.getIn().getHeader("fruit"));
    }
}

Here we also optionally add the Exchange as an argument so Camel will pass the Exchange to our method automatically.

这里我们还可以选择添加Exchange作为参数,这样Camel就会自动将Exchange传递给我们的方法。

Then we can go ahead and use our FruitBean from our when block:

然后我们可以继续使用我们的FruitBean从我们的when块。

@Component
public class ConditionalBeanRouter extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        
        from("direct:start-conditional-bean")
            .routeId("conditional-bean-route")
            .choice()
              .when(method(FruitBean.class, "isApple"))
                .setHeader("favourite", simple("Apples"))
                .to("mock:result")
              .otherwise()
                .setHeader("favourite", header("fruit"))
                 .to("mock:result")
              .endChoice()
            .end();
    }
}

9. Conclusion

9.结语

In this article, we learned how we could route messages based on some kind of condition in our routes. First, we created a simple Camel application with one route to inspect the message body.

在这篇文章中,我们学习了如何根据路由中的某种条件来路由消息。首先,我们创建了一个简单的Camel应用程序,有一个路由来检查消息体。

Then we learned about several other techniques for building predicates in our routes using message headers and Java beans.

然后,我们学习了其他一些技术,在我们的路由中使用消息头和JavaBean来构建谓词。

As always, the full source code of the article is available over on GitHub.

一如既往,文章的完整源代码可在GitHub上获得