Apache Camelとの統合パターン

1概要

この記事では、Apache Camelでサポートされているいくつかの重要なエンタープライズ統合パターン(EIP)について説明します。統合パターンは、システムを統合する標準化された方法のためのソリューションを提供することによって役立ちます。

あなたが最初にApache Camelの基本を調べる必要があるなら、間違いなく この記事 にアクセスして基本について学んでください。

** 2 EIPについて

**

エンタープライズ統合パターンは、統合の課題に対する解決策を提供することを目的とした設計パターンです。 Camelはこれらのパターンの多くの実装を提供します。サポートされているパターンの完全なリストを見るには、http://camel.apache.org/enterprise-integration-patterns.html[このリンク]にアクセスしてください。

この記事では、Content Based Router、Message Translator、Multicast、Splitter、およびDead Letter Channel統合パターンについて説明します。

** 2コンテンツベースルータ

**

Content Based Routerは、メッセージヘッダー、ペイロードの一部、または基本的にメッセージ交換からのコンテンツと見なすものに基づいてメッセージをその宛先にルーティングするメッセージルーターです。

それは choice() DSLステートメントで始まり、その後に1つ以上の when() DSLステートメントが続きます。各 when() には述語式が含まれており、満たされると、含まれている処理ステップが実行されます。

1つのフォルダからファイルを消費し、ファイル拡張子に応じてそれらを2つの異なるフォルダに移動するルートを定義することによって、このEIPを説明しましょう。私たちのルートは、CamelのためのカスタムXMLシンタックスを使ってSpring XMLファイルで参照されます:

<bean id="contentBasedFileRouter"
  class="org.baeldung.camel.file.ContentBasedFileRouter"/>

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <routeBuilder ref="contentBasedFileRouter"/>
</camelContext>

ルート定義は ContentBasedFileRouter クラスに含まれており、ファイルはその拡張子に応じてソースフォルダから2つの異なる宛先フォルダにルーティングされます。

あるいは、Spring XMLファイルを使用するのではなく、ここでSpring Java構成アプローチを使用することもできます。それには、プロジェクトに追加の依存関係を追加する必要があります。

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-spring-javaconfig</artifactId>
    <version>2.18.1</version>
</dependency>

最新バージョンの成果物はhttps://search.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22camel-spring-javaconfig%22[here]にあります。

その後、 CamelConfiguration クラスを拡張し、 ContentBasedFileRouter を参照する routes() メソッドをオーバーライドする必要があります。

@Configuration
public class ContentBasedFileRouterConfig extends CamelConfiguration {

    @Bean
    ContentBasedFileRouter getContentBasedFileRouter() {
        return new ContentBasedFileRouter();
    }

    @Override
    public List<RouteBuilder> routes() {
        return Arrays.asList(getContentBasedFileRouter());
    }
}

拡張子は、式や述語を評価するために使われることを意図していた simple ()DSLステートメントを通してhttp://camel.apache.org/simple.html[単純表現言語]を使って評価されます。

public class ContentBasedFileRouter extends RouteBuilder {

    private static final String SOURCE__FOLDER
      = "src/test/source-folder";
    private static final String DESTINATION__FOLDER__TXT
      = "src/test/destination-folder-txt";
    private static final String DESTINATION__FOLDER__OTHER
      = "src/test/destination-folder-other";

    @Override
    public void configure() throws Exception {
        from("file://" + SOURCE__FOLDER + "?delete=true").choice()
          .when(simple("${file:ext} == 'txt'"))
          .to("file://" + DESTINATION__FOLDER__TXT).otherwise()
          .to("file://" + DESTINATION__FOLDER__OTHER);
    }
}

ここでは、 when() ステートメントで与えられた述語を満たさないすべてのメッセージをルーティングするために otherwise() DSLステートメントを追加で使用しています。

3メッセージトランスレータ

すべてのシステムがそれ自身のデータフォーマットを使用するので、他のシステムから来るメッセージを目的のシステムがサポートするデータフォーマットに変換することがしばしば必要とされます。

Camelは MessageTranslator routerをサポートしています。これにより、ルーティングロジック内のカスタムプロセッサ、変換を実行するための特定のBean、または transform() DSLステートメントを使用してメッセージを変換できます。

カスタムプロセッサを使用した例はリンクにあります:/apache-camel-intro[前の記事]ここで、それぞれの受信ファイルのファイル名にタイムスタンプを付加するプロセッサを定義しました。

transform() ステートメントを使用してMessage Translatorを使用する方法を説明しましょう。

public class MessageTranslatorFileRouter extends RouteBuilder {
    private static final String SOURCE__FOLDER
      = "src/test/source-folder";
    private static final String DESTINATION__FOLDER
      = "src/test/destination-folder";

    @Override
    public void configure() throws Exception {
        from("file://" + SOURCE__FOLDER + "?delete=true")
          .transform(body().append(header(Exchange.FILE__NAME)))
          .to("file://" + DESTINATION__FOLDER);
    }
}

この例では、ソースフォルダから各ファイルの transform() ステートメントを介してファイルコンテンツにファイル名を追加し、変換されたファイルを宛先フォルダに移動しています。

4マルチキャスト

マルチキャストを使用すると、同じメッセージを異なるエンドポイントのセットにルーティングし、それらを異なる方法で処理することができます。

これは、 multicast() DSLステートメントを使用してから、エンドポイントをリストし、その中のステップを処理することによって可能になります。

デフォルトでは、異なるエンドポイントでの処理は並行して行われませんが、これは parallelProcessing() DSLステートメントを使用して変更できます。

Camelはデフォルトでマルチキャストの後に送信メッセージとして最後の返信を使用します。ただし、マルチキャストからの返信を組み立てるために使用する別の集約方法を定義することは可能です。

例でマルチキャストEIPがどのように見えるかを見てみましょう。ファイルをソースフォルダから2つの異なるルートにマルチキャストして、そこでコンテンツを変換し、それらを異なる宛先フォルダに送信します。ここではhttps://camel.apache.org/direct.html[ direct: component]を使用しています。これにより、2つのルートをリンクさせることができます。

public class MulticastFileRouter extends RouteBuilder {
    private static final String SOURCE__FOLDER
      = "src/test/source-folder";
    private static final String DESTINATION__FOLDER__WORLD
      = "src/test/destination-folder-world";
    private static final String DESTINATION__FOLDER__HELLO
      = "src/test/destination-folder-hello";

    @Override
    public void configure() throws Exception {
        from("file://" + SOURCE__FOLDER + "?delete=true")
          .multicast()
          .to("direct:append", "direct:prepend").end();

        from("direct:append")
          .transform(body().append("World"))
          .to("file://" + DESTINATION__FOLDER__WORLD);

        from("direct:prepend")
           .transform(body().prepend("Hello"))
           .to("file://" + DESTINATION__FOLDER__HELLO);
    }
}

5スプリッタ

スプリッタを使用すると、受信メッセージを** いくつかの部分に分割し、それぞれを個別に処理することができます。

マルチキャストとは対照的に、Splitterは着信メッセージを変更しますが、Multicastはそのままにします。

これを例で説明するために、ファイルの各行を分割して個別のファイルに変換し、それを別の保存先フォルダに移動するルートを定義します。それぞれの新しいファイルは、ファイルの内容と同じファイル名で作成されます。

public class SplitterFileRouter extends RouteBuilder {
    private static final String SOURCE__FOLDER
      = "src/test/source-folder";
    private static final String DESTINATION__FOLDER
      = "src/test/destination-folder";

    @Override
    public void configure() throws Exception {
        from("file://" + SOURCE__FOLDER + "?delete=true")
          .split(body().convertToString().tokenize("\n"))
          .setHeader(Exchange.FILE__NAME, body())
          .to("file://" + DESTINATION__FOLDER);
    }
}

6. デッドレターチャンネル

一般的であり、データベースのデッドロックなど、問題が発生することがあり、メッセージが期待どおりに配信されないことがあります。ただし、場合によっては、一定の遅延で再試行すると効果があり、メッセージが処理されます。

  • デッドレターチャネルを使用すると、メッセージの配信に失敗した場合の処理​​を制御できます** デッドレターチャネルを使用して、スローされた例外を発信者に伝達するかどうか、および失敗したExchangeのルーティング先を指定できます。

メッセージの配信に失敗した場合、Dead Letter Channel(使用されている場合)はメッセージを送達不能エンドポイントに移動します。

ルート上で例外を投げることで例でこれを実証しましょう:

public class DeadLetterChannelFileRouter extends RouteBuilder {
    private static final String SOURCE__FOLDER
      = "src/test/source-folder";

    @Override
    public void configure() throws Exception {
        errorHandler(deadLetterChannel("log:dead?level=ERROR")
          .maximumRedeliveries(3).redeliveryDelay(1000)
          .retryAttemptedLogLevel(LoggingLevel.ERROR));

        from("file://" + SOURCE__FOLDER + "?delete=true")
          .process(exchange -> {
            throw new IllegalArgumentException("Exception thrown!");
        });
    }
}

ここでは、失敗した配信を記録して再配信戦略を定義する errorHandler を定義しました。 retryAttemptedLogLevel() を設定することで、各再配信の試行は指定されたログレベルで記録されます。

これが完全に機能するためには、さらにロガーを設定する必要があります。

このテストを実行した後、次のログステートメントがコンソールに表示されます。

ERROR DeadLetterChannel:156 - Failed delivery for
(MessageId: ID-ZAG0025-50922-1481340325657-0-1 on
ExchangeId: ID-ZAG0025-50922-1481340325657-0-2).
On delivery attempt: 0 caught: java.lang.IllegalArgumentException:
Exception thrown!
ERROR DeadLetterChannel:156 - Failed delivery for
(MessageId: ID-ZAG0025-50922-1481340325657-0-1 on
ExchangeId: ID-ZAG0025-50922-1481340325657-0-2).
On delivery attempt: 1 caught: java.lang.IllegalArgumentException:
Exception thrown!
ERROR DeadLetterChannel:156 - Failed delivery for
(MessageId: ID-ZAG0025-50922-1481340325657-0-1 on
ExchangeId: ID-ZAG0025-50922-1481340325657-0-2).
On delivery attempt: 2 caught: java.lang.IllegalArgumentException:
Exception thrown!
ERROR DeadLetterChannel:156 - Failed delivery for
(MessageId: ID-ZAG0025-50922-1481340325657-0-1 on
ExchangeId: ID-ZAG0025-50922-1481340325657-0-2).
On delivery attempt: 3 caught: java.lang.IllegalArgumentException:
Exception thrown!
ERROR dead:156 - Exchange[ExchangePattern: InOnly,
BodyType: org.apache.camel.component.file.GenericFile,
Body:[Body is file based: GenericFile[File.txt]]]----

お気づきのように、再配信の試行ごとに、配信が成功しなかったExchangeを示すログが記録されています。

===  **  7. 結論

**

この記事では、Apache Camelを使用した統合パターンの紹介をいくつかの例で示しました。

これらの統合パターンの使用方法と、それらが統合の課題を解決するために有益である理由を説明しました。

この記事のコードはhttps://github.com/eugenp/tutorials/tree/master/spring-apache-camel[over on GitHub]にあります。