Apache Camelとの統合パターン

Apache Camelとの統合パターン

1. 概要

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

最初にApacheCamelの基本を確認する必要がある場合は、必ずthis articleにアクセスして基本をブラッシュアップしてください。

2. EIPについて

エンタープライズ統合パターンは、統合の課題に対するソリューションを提供することを目的とした設計パターンです。 Camelは、これらのパターンの多くの実装を提供します。 サポートされているパターンの完全なリストを表示するには、this linkにアクセスしてください。

この記事では、コンテンツベースのルーター、メッセージトランスレーター、マルチキャスト、スプリッター、デッドレターチャネルの統合パターンについて説明します。

2. コンテンツベースのルーター

コンテンツベースのルーターは、メッセージヘッダー、ペイロードの一部、または基本的にコンテンツと見なされるメッセージ交換のすべてに基づいて、メッセージを宛先にルーティングするメッセージルーターです。

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

1つのフォルダからファイルを消費し、ファイル拡張子に応じて2つの異なるフォルダに移動するルートを定義して、このEIPを説明しましょう。 ルートは、CamelのカスタムXML構文を使用して、Spring XMLファイルで参照されます。




    

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

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


    org.apache.camel
    camel-spring-javaconfig
    2.18.1

アーティファクトの最新バージョンはhereにあります。

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

@Configuration
public class ContentBasedFileRouterConfig extends CamelConfiguration {

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

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

拡張機能は、式と述語の評価に使用することを目的としたsimple()DSLステートメントを介してSimple Expression Languageを使用して評価されます。

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ルーターをサポートしており、ルーティングロジックのカスタムプロセッサを使用するか、特定のBeanを使用して変換を実行するか、transform()DSLステートメントを使用してメッセージを変換できます。

カスタムプロセッサを使用した例は、各受信ファイルのファイル名にタイムスタンプを付加するプロセッサを定義したprevious articleにあります。

次に、transform()ステートメントを使用してMessageTranslatorを使用する方法を示します。

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. マルチキャスト

マルチキャストを使用すると、route the same message to a set of different endpoints and process them in a different wayを実行できます。

これは、multicast() DSLステートメントを使用し、エンドポイントとその中の処理ステップを一覧表示することで可能になります。

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

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

例でマルチキャストEIPがどのように見えるかを見てみましょう。 ファイルをソースフォルダから2つの異なるルートにマルチキャストし、そこでコンテンツを変換して、異なる宛先フォルダに送信します。 ここでは、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. スプリッタ

スプリッターにより、split the incoming message into a number of pieces and processing each of them individually.が可能になります。これは、split()DSLステートメントを使用することで可能になります。

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

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

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. デッドレターチャンネル

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

Dead Letter Channel allows us to control what happens with a message once it fails to be delivered. Dead Letter Channelを使用して、スローされた例外を呼び出し元に伝播するかどうか、および失敗したExchangeをルーティングする場所を指定できます。

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

ルートで例外をスローすることにより、例でこれを示しましょう。

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を使用した統合パターンの概要を紹介し、いくつかの例でそれらを示しました。

これらの統合パターンの使用方法と、統合の課題を解決するのになぜ有益なのかを示しました。

この記事のコードはover on GitHubにあります。