”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 掌握 Apache Camel 中的数据路由:利用 Splitter 模式

掌握 Apache Camel 中的数据路由:利用 Splitter 模式

发布于2024-11-03
浏览:700

Hello again! In my upcoming articles, I plan to explore several key patterns provided by Apache Camel that are invaluable for your ETL processes. This current article focuses on the Splitter Enterprise Integration Pattern (EIP).

Overview

Messages passed to integration applications often arrive in a format that is less than ideal for immediate processing. Frequently, these are composite messages containing multiple elements, each requiring individual processing. This is where the Splitter pattern proves beneficial by dividing incoming messages into a sequence of more manageable messages.

The diagram below (Figure 1) illustrates the simplicity of the Splitter pattern- it takes a single message and splits it into multiple distinct messages.

Mastering Data Routing in Apache Camel: Leveraging the Splitter Pattern

Figure 1 – Splitter Pattern (enterpriseintegrationpatterns.com)

Using Splitter in Apache Camel

Implementing the Splitter pattern in Apache Camel is straightforward. Incorporate the split method in the route definition. This method requires an expression to dictate how the message should be split.

Commonly, you might need to split a list, set, map, array, or any other collection. For such cases, use the body method to retrieve the message body before splitting.

The Splitter behaves akin to a comprehensive iterator that processes each entry consecutively. The sequence diagram in (Figure 2) offers detailed insights into this iterator's operation.

Mastering Data Routing in Apache Camel: Leveraging the Splitter Pattern

Figure 2 – A sequence diagram

Upon receiving a message, the Splitter evaluates an Expression which returns the message body. This result is then utilized to create a java.util.Iterator.

By default, the split method divides the message body based on its value type, as illustrated below:

  • java.util.Collection: Splits each element from the collection.

  • java.util.Map: Splits each map entry.

  • Object[]: Splits each array element.

  • Iterator: Splits each iteration.

  • Iterable: Splits each iterable component.

  • String: Splits each string character.

The Splitter then utilizes the iterator till all data is processed. Each resultant message is a copy of the original with its body replaced by the portion from the iterator.

Let's explore how to employ the Splitter pattern in Apache Camel with practical examples.

Example 1: Splitting an ArrayList of Strings

Consider a route that splits an ArrayList of String and logs each element. Here's how you define this route:

@ApplicationScoped
public class SplitterRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("direct:split_start_1")
        .log("Before Split line ${body}")
        .split(body())
        .log("Split line ${body}");
    }
}

Call the above route by passing an ArrayList of Strings as the message body:

List body = List.of("A", "B", "C");
producerTemplate.sendBody("direct:split_start_1", body);

Running the above code yields the following output:

2023-07-14 20:10:03,389 INFO  Before Split line [A, B, C]
2023-07-14 20:10:03,391 INFO  Split line A
2023-07-14 20:10:03,392 INFO  Split line B
2023-07-14 20:10:03,392 INFO  Split line C

Example 2: Splitting a String by Custom Delimiter

In this example, we split a String using a custom delimiter #:

@ApplicationScoped
public class SplitterRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("direct:split_start_2")
        .log("Before Split line ${body}")
        .split(body(), "#")
        .log("Split line ${body}");
    }
}

To call the route and pass the String as a message body:

producerTemplate.sendBody("direct:split_start_2", "A#B#C");

Running this code, you see the following in the log:

2024-07-14 20:19:19,229 INFO  Before Split line A#B#C
2024-07-14 20:19:19,229 INFO  Split line A
2024-07-14 20:19:19,229 INFO  Split line B
2024-07-14 20:19:19,230 INFO  Split line C

These examples demonstrate the versatility of the Splitter pattern in handling various types of message formats.

As you can see, the Splitter pattern is very useful when you need to split a message into multiple messages. But let's see another example where we are going to split an Object by its fields. For the demo purpose, we are going to provide the User object with the List of Address objects.

The User class is:

public record UserDetails(String userId, List
addresses) {}

and the Address class is:

public record Address(String addressId, String address) {}

The route definition is:

@ApplicationScoped
public class SplitterRoute extends RouteBuilder {

  @Override
  public void configure() throws Exception {
    from("direct:userDetails")
        .log("User Id: ${body.userId}")
        .split(method(UserService.class))
        .log("Split line ${body}");
  }

  static class UserService {
    public static List
getDetails(UserDetails userDetails) { return userDetails.addresses(); } } }

and to call the above route as pass the User object as a message body:

final UserDetails userDetails = new UserDetails(
    "user-1",
    List.of(
        new Address("1", "Address 1"),
        new Address("2", "Address 2")
    )
);

producerTemplate.sendBody("direct:userDetails", userDetails);

When you run the above code, you will see the following output in the log:

2024-07-14 21:09:02,225 INFO  [route36] (Quarkus Main Thread) User Id: user-1
2024-07-14 21:09:02,226 INFO  [route36] (Quarkus Main Thread) Split line Address[addressId=1, address=Address 1]
2024-07-14 21:09:02,226 INFO  [route36] (Quarkus Main Thread) Split line Address[addressId=2, address=Address 2]

The method component in Apache Camel is a versatile and powerful tool that allows routes to directly invoke methods on beans. This capability is particularly useful in enterprise applications where business logic needs to be cleanly encapsulated within service classes, yet seamlessly integrated within Camel routes for orchestration of data flows.

The route invokes a method from the UserService class. Apache Camel looks for this method in the UserService class to handle how the message should be split. The method should be designed to accept the incoming message body and return a collection, such as a List or Array, which Camel then iterates over. Each element of the array or list becomes a new exchange in Camel, which is processed independently for the remainder of the route.

By integrating dynamic splitting with conditional routing using choice() and when() constructs, Apache Camel provides a robust framework for bespoke data processing workflows.

Understanding choice() and when() in Apache Camel

In Apache Camel, choice() and when are powerful constructs that enable content-based routing. This paradigm mimics the if-else or switch-caseconstructs familiar in many programming languages, allowing routes to make decisions based on the content of messages.

The choice() construct serves as a container for one or more when() clauses. Each when() clause specifies a condition. When a condition evaluates to true, Apache Camel routes the message to the processing logic defined within that when() block. If none of the conditions in any of the when() clauses match, an optional otherwise() clause can be executed, akin to an else statement in traditional programming.

Detailed Example with choice() and when().

Imagine a scenario where an application processes orders and sends them to different processing streams based on their category, such as "books", "electronics", or "clothing". This setup allows for highly specialized processing for different categories of items.

Here's how you might set up such a route in Apache Camel:

  • Route Configuration: Define a route that starts from an endpoint (for example, direct:startOrderRoute). This route will examine the type of order and route the message accordingly.

  • Using choice() and when(): Inside the route, a choice() component examines the message. Based on the content, it routes the message to different endpoints using when().

  • Logging and Processing: Each category has a dedicated log that records the action taken, ensuring traceability.

Here's a sample implementation:

public class OrderRouteBuilder extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("direct:start")
            .choice()
                .when(body().contains("books"))
                    .log("Routing to Books processing: ${body}")
                    .to("direct:books")
                .when(body().contains("electronics"))
                    .log("Routing to Electronics processing: ${body}")
                    .to("direct:electronics")
                .when(body().contains("clothing"))
                    .log("Routing to Clothing processing: ${body}")
                    .to("direct:clothing")
                .otherwise()
                    .log("Unknown order type: ${body}")
            .end();

        from("direct:books")
            .log("Processing Books Order: ${body}");

        from("direct:electronics")
            .log("Processing Electronics Order: ${body}");

        from("direct:clothing")
            .log("Processing Clothing Order: ${body}");
    }
}

Now you can run the route by sending different order types to the direct:start endpoint:

producerTemplate.sendBody("direct:startOrderRoute", "An order for books");

and you will see the following output in the log:

2024-07-16 19:16:12,357 INFO  [route5] (Quarkus Main Thread) Received Order: An order for books
2024-07-16 19:16:12,357 INFO  [route5] (Quarkus Main Thread) Routing to Books processing: An order for books
2024-07-16 19:16:12,358 INFO  [route6] (Quarkus Main Thread) Processing Books Order: An order for books

As you can see, the message is routed to the books endpoint and processed accordingly. You can test the other categories in a similar manner.

Practical Usage and Tips

  • Complex Conditions: when() can handle complex expressions combining multiple conditions.

  • Using .endChoice(): For readability and to prevent misrouting, especially in a route with nested .choice() constructs, use .endChoice() to clearly indicate the end of a choice block.

  • Flexibility: This approach is highly flexible, allowing the addition of new conditions and endpoints as business requirements evolve.

Conclusion

The Splitter pattern stands out as a powerful tool in Apache Camel's arsenal, enabling developers to effectively handle complex, bulky data structures by breaking them down into more manageable pieces. This pattern not only simplifies data processing workflows but also enhances maintainability and scalability within integration solutions.

In this article, we've explored the practical implementation of the Splitter pattern in a range of contexts - from handling lists and custom delimited strings to operating on more complex structures like Java objects. Each example showcased how Apache Camel facilitates straightforward data manipulations, making seemingly complex integration tasks much simpler.

Additionally, the integration of choice() and when() constructs further refines the data routing capabilities in Apache Camel, offering precise control over message flow based on specific content criteria. This ability to route data conditionally mimics conventional programming logic, bringing a familiar and intuitive approach to route definitions.

As businesses continue to deal with increasingly diverse and voluminous data, understanding and implementing these patterns will become crucial. The flexibility and power of Apache Camel in managing data flows allow developers to build robust, efficient data ingestion and processing pipelines that are crucial for today's data-driven applications.

And last but not least, the all above examples are implemented in the Quarkus framework as using the Camel Quarkus extension.

版本声明 本文转载于:https://dev.to/yanev/mastering-data-routing-in-apache-camel-leveraging-the-splitter-pattern-3g5?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何在GO编译器中自定义编译优化?
    如何在GO编译器中自定义编译优化?
    在GO编译器中自定义编译优化 GO中的默认编译过程遵循特定的优化策略。 However, users may need to adjust these optimizations for specific requirements.Optimization Control in Go Compi...
    编程 发布于2025-07-19
  • 为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    在CSS 问题:不正确的代码: 全球范围将所有余量重置为零,如提供的代码所建议的,可能会导致意外的副作用。解决特定的保证金问题是更建议的。 例如,在提供的示例中,将以下代码添加到CSS中,将解决余量问题: body H1 { 保证金顶:-40px; } 此方法更精确,避免了由全局保证金重置引...
    编程 发布于2025-07-19
  • PHP阵列键值异常:了解07和08的好奇情况
    PHP阵列键值异常:了解07和08的好奇情况
    PHP数组键值问题,使用07&08 在给定数月的数组中,键值07和08呈现令人困惑的行为时,就会出现一个不寻常的问题。运行print_r($月)返回意外结果:键“ 07”丢失,而键“ 08”分配给了9月的值。此问题源于PHP对领先零的解释。当一个数字带有0(例如07或08)的前缀时,PHP将其...
    编程 发布于2025-07-19
  • 如何从PHP中的数组中提取随机元素?
    如何从PHP中的数组中提取随机元素?
    从阵列中的随机选择,可以轻松从数组中获取随机项目。考虑以下数组:; 从此数组中检索一个随机项目,利用array_rand( array_rand()函数从数组返回一个随机键。通过将$项目数组索引使用此键,我们可以从数组中访问一个随机元素。这种方法为选择随机项目提供了一种直接且可靠的方法。
    编程 发布于2025-07-19
  • 切换到MySQLi后CodeIgniter连接MySQL数据库失败原因
    切换到MySQLi后CodeIgniter连接MySQL数据库失败原因
    Unable to Connect to MySQL Database: Troubleshooting Error MessageWhen attempting to switch from the MySQL driver to the MySQLi driver in CodeIgniter,...
    编程 发布于2025-07-19
  • C++20 Consteval函数中模板参数能否依赖于函数参数?
    C++20 Consteval函数中模板参数能否依赖于函数参数?
    [ consteval函数和模板参数依赖于函数参数在C 17中,模板参数不能依赖一个函数参数,因为编译器仍然需要对非contexexpr futcoriations contim at contexpr function进行评估。 compile time。 C 20引入恒定函数,必须在编译时进行...
    编程 发布于2025-07-19
  • 如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求模拟浏览器行为,以及伪造的用户代理提供了一个用户 - 代理标头一个有效方法是提供有效的用户式header,以提供有效的用户 - 设置,该标题可以通过browser和Acterner Systems the equestersystermery和操作系统。通过模仿像Chro...
    编程 发布于2025-07-19
  • 如何同步迭代并从PHP中的两个等级阵列打印值?
    如何同步迭代并从PHP中的两个等级阵列打印值?
    同步的迭代和打印值来自相同大小的两个数组使用两个数组相等大小的selectbox时,一个包含country代码的数组,另一个包含乡村代码,另一个包含其相应名称的数组,可能会因不当提供了exply for for for the uncore for the forsion for for ytry...
    编程 发布于2025-07-19
  • Java中假唤醒真的会发生吗?
    Java中假唤醒真的会发生吗?
    在Java中的浪费唤醒:真实性或神话?在Java同步中伪装唤醒的概念已经是讨论的主题。尽管存在这种行为的潜力,但问题仍然存在:它们实际上是在实践中发生的吗? Linux的唤醒机制根据Wikipedia关于伪造唤醒的文章,linux实现了pthread_cond_wait()功能的Linux实现,利用...
    编程 发布于2025-07-19
  • 为什么尽管有效代码,为什么在PHP中捕获输入?
    为什么尽管有效代码,为什么在PHP中捕获输入?
    在php ;?>" method="post">The intention is to capture the input from the text box and display it when the submit button is clicked.但是,输出...
    编程 发布于2025-07-19
  • \“(1)vs.(;;):编译器优化是否消除了性能差异?\”
    \“(1)vs.(;;):编译器优化是否消除了性能差异?\”
    答案: 在大多数现代编译器中,while(1)和(1)和(;;)之间没有性能差异。编译器: perl: 1 输入 - > 2 2 NextState(Main 2 -E:1)V-> 3 9 Leaveloop VK/2-> A 3 toterloop(next-> 8 last-> 9 ...
    编程 发布于2025-07-19
  • 您可以使用CSS在Chrome和Firefox中染色控制台输出吗?
    您可以使用CSS在Chrome和Firefox中染色控制台输出吗?
    在javascript console 中显示颜色是可以使用chrome的控制台显示彩色文本,例如红色的redors,for for for for错误消息?回答是的,可以使用CSS将颜色添加到Chrome和Firefox中的控制台显示的消息(版本31或更高版本)中。要实现这一目标,请使用以下模...
    编程 发布于2025-07-19
  • 为什么PYTZ最初显示出意外的时区偏移?
    为什么PYTZ最初显示出意外的时区偏移?
    与pytz 最初从pytz获得特定的偏移。例如,亚洲/hong_kong最初显示一个七个小时37分钟的偏移: 差异源利用本地化将时区分配给日期,使用了适当的时区名称和偏移量。但是,直接使用DateTime构造器分配时区不允许进行正确的调整。 example pytz.timezone(...
    编程 发布于2025-07-19
  • PHP未来:适应与创新
    PHP未来:适应与创新
    PHP的未来将通过适应新技术趋势和引入创新特性来实现:1)适应云计算、容器化和微服务架构,支持Docker和Kubernetes;2)引入JIT编译器和枚举类型,提升性能和数据处理效率;3)持续优化性能和推广最佳实践。 引言在编程世界中,PHP一直是网页开发的中流砥柱。作为一个从1994年就开始发展...
    编程 发布于2025-07-19
  • 如何避免Go语言切片时的内存泄漏?
    如何避免Go语言切片时的内存泄漏?
    ,a [j:] ...虽然通常有效,但如果使用指针,可能会导致内存泄漏。这是因为原始的备份阵列保持完整,这意味着新切片外部指针引用的任何对象仍然可能占据内存。 copy(a [i:] 对于k,n:= len(a)-j i,len(a); k
    编程 发布于2025-07-19

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3