Apache Camel 超入門

こんにちは、ソリューションアーキテクトの蒸野(ムシノ)です。

今回から「Red Hat Fuse」について数回に分けて取り上げてみたいと思います。 まず、Fuseの製品紹介については、下記をご覧ください。 www.redhat.com

初回はFuseのベースとなっている「Apache Camel」の概要に絞って説明をしたいと思います。

「Apache Camelのユーザ会」では以下のように説明しています。

オープンソースのコンポーネントベースのルーティングエンジン
例えば自分のマシン上で作成したExcelやCSVデータがあるとします。 この時、自分のPCの作業場所から別の場所に移動/コピーしたり、他のコンピュータに送信したり、不足している情報を他のコンピュータから取得してExcelやCSVに追加する作業が必要となるケースがあります。 処理するデータ量が少なければ手作業でやっても全然差し支えありませんが、これを1日に何千件も処理する必要があるとしたらさすがに自動化したいと思いませんか? Javaやシェルスクリプトを使って自動化することも可能ですが、データの送信先が1台のPCから複数台に増えたら送信先を改めて追加する必要があり、更に送信するにしてもメールやFTPと別々のプロトコルで送る必要があったりした場合、プログラムが煩雑になってしまいます。Apache Camelはまさしくこのようなことを自動化するのに最適なフレームワークになります。

SOA全盛期のインテグレーション方式

    

SOAによるシステム統合が活発な時期には、インテグレーション方式として Enterprise Service Bus(ESB)がよく使われていました。 しなしながら同時に多くの課題も見受けられるようになっていきました。例えば、

  • ESB自体が巨大なモノリシックなアプリケーションとなっていった。
  • 当時は相当高価なソリューションであり一部の大企業しか導入ができない製品だった。
  • 企業ガバナンスとして通信をESBに集めるように求められるようになった結果、パフォーマンスボトルネックが問題になった。
  • ESBが単一障害点になってしまい、これを運用するチームも保守的体制になることでビジネス柔軟性を損なっていった。
  • 運用するチーム独自の接続方式標準化がなされ、利用側はその独自の標準方式に則って接続することで使いづらいシステム接続になっていった。
  • ESBにはデータ変換機能もあったが、性能を維持するため通信を通過させるためだけに利用するケースも散見され本来の機能を活かしきれていなかった。

Apache Camel

こういったESBの課題に対して、軽量ルーティングフレームワークとして登場したのが Apache Camelです。

先程同様「Apache Camelのユーザ会」を参考にしていますが、下記のように定義しています。

Apache Camelを使うことで以下のことが簡単に実現できます。
・各種プロトコルを使った送受信 ・キーワードを元に送信先の振り分け
・Apache Software Foundation(ASF)を始めとした100以上のOSSの活用

Camel を紹介するサイトでは、軽量なルーティングエンジンと言ったり、ライブラリやフレームワークとバラバラな表現となっていますが、大雑把に捉えれば軽量かつ、様々なシステムを統合するために豊富なコンポーネントを持ち、インテグレーションに必要なルーティング機能を提供してくれると理解すれば良いでしょう。よく誤解されがちな点として、100以上のOSSを組み合わていることから大規模なインテグレーションOSSと思われますが、実際には約数M程度のライブラリで構成された軽量なOSSです。また、様々なコンポーネントを追加することで拡張することができるプラガブルなアーキテクチャを採用していますので、必要なインテグレーションにあわせてコンポーネントを選択して利用することができます。現在は200を超えるコンポーネントが存在しており、ドキュメントも豊富にあります。

Apache Camelの得意分野

・定期的な処理、バッチ処理、クーロンで行っている処理
・各種プロトコルから各種プロトコルへの変換、データ変換

Apache Camelの不得意分野

・MVCモデルのView部分
・ワークフロー(状態を永続化させるフロー)

基本的なアーキテクチャ

Apache Camel のドキュメントにも紹介されているシンプルな全体アーキテクチャになります。
https://camel.apache.org/manual/architecture.html

Apache Camel のコア機能として CamelContext があり、この CamelContext にはCamelのランタイムすべてを含んでいます。 ルートは、CamelのDSLを使って定義されます。 プロセッサはルーティング中にメッセージを変換・操作・仲介、またはEnterprise Integration Patterns(EIP)を実行するために利用されます。 コンポーネントは、Camelの接続機能を強化し他のシステムとの接続性を容易にします。

次に、このルートについてのシンプルなアーキテクチャが、Japan Camel User Group で説明されていますので、こちらのほうも示しておきたいと思います。

まずルーティングエンジンは、一連のフローを定義する「ルート」を使用します。このルートをCamelルートとも言ったりします。 次に、開始点になる「スタートポイント」、および終了ポイントになる「エンドポイント」があります。これはシステムが外部とのメッセージを送受信するインターフェースと思えば理解しやすいかと思います。 エンドポイントは、データを受け取る「コンシューマ」と、データを送信する「プロデューサ」として区別されることもあります。 また、コンシューマはイベント駆動コンシューマと、ポーリングコンシューマが存在します。 最後に、メッセージの変換や加工、コンポーネントを呼び出し処理を行ったりする「プロセッサ」になります。このプロセッサは処理をチェーンさせた形で定義されます。

Java DSLで定義したルートの例

// Consumer

from("jetty:http://localhost:8080/orders?httpMethodRestrict=POST") 
    .process().body(this::process1)) // Processor 1
    .process().body(this::process2)) // Processor 2
    .to("jms:queue:order?timeToLive=86400000"); // Producer

ETLとEAIの違い

こういった特徴を説明していくとよくEAIやETLとどう違うのか?といった質問を受けるケースがあります。

ETLは、データの抽出・変換・書き出しを行う一連のプロセスであり、一般的には、DWHにデータを保存する際の前処理として行われます。 EAIとは業務システムやメインフレームシステムといったシステム間のデータ連携を行う方式であり、各システムへ接続するインターフェース、フォーマット変換、ルーティング処理、プロセス制御などの機能を有しており以前からインテグレーション製品としても良く知られていました。CamelはEAIやESBと同様のことが実現可能ですが、これに加え軽量で分散インテグレーションが可能なことが特徴とも言えます。Camelの登場によって、大規模なプロジェクトに留まらず一般的なプロジェクトでもインテグレーション機能が利用しやすくなったというところが大きな変換点と言えるかと思います。

特徴のまとめ

いろいろな特徴を持っているので簡単には説明しにくいところではありますが、Apache Camel 超概要ということで、5つだけピックアップして説明したいと思います。

特徴1:ルーティング処理

Camelの代表的な強力な機能の一つと言えるルーティング。Camelでは受け取ったデータは、分岐やフィルタリングさせたり、分割して並列処理させたり、それらを集約したりすることができます。 またHTTP通信のような同期処理から、非同期処理へと切り替えることや、並列実行なども可能であり、さらには分散したマイクロサービスに複数のキューを経由させながら、応答メッセージを受け取るようなルーティングも実現できます。特殊な用途としては、分散処理としてラウンドロビンで宛先を切り替えて簡易なロードバランサーのように振り分けたり、または一度に流れるデータの流量を調整するといったことも可能です。

特徴2:様々な変換処理が可能

Camel には150を超えるデータ変換機能があります。JSON、XML、CSV、Javaオブジェクトなどのデータタイプを扱うことができ、データタイプを相互に変換することができます。その他、Camel がサポートしないデータ変換が必要な場合には独自のコンバータを作成し利用することができます。

特徴3:様々なコンポーネントが用意されている

標準で200以上のコンポーネントが存在しており、FTPやSSH、HTTP(S)といった昔からある通信プロトコルや、RESTやSOAP、ファイル、データベース、メッセージキュー、メール、MQTTなどを相互に接続して連携させることが可能です。また、AWSや、ビッグデータやNoSQL関連プロダクトとのアクセス、FacebookやTwitterへの連携などのコンポーネントも用意されており、ほぼ標準コンポーネントで必要な連携が実現できます。また、必要があればモジュラーアーキテクチャにより独自のコンポーネントを実装することで統合ができます。コンポーネントによりますが、大方のコンポーネントは接続エンドポイントに対してURI設定を行うだけで利用できます。

特徴4:非同期処理

Camelはノンブロッキングによる非同期処理に対応します。例えば背後のシステムにAPIリクエストしたがレスポンスに時間がかかる場合、その応答までスレッドをブロックながら待ち続けるのはシステムの性能を大きく損なわせます。Camelはスレッドを有効に再利用することで高いスケーラビリティを発揮します。エンドポイントのコンポーネントが対応しているかなどのいくつかの条件が揃えば、Camelのエンジンは自動的に非同期処理を選択するため、何もせずとも非同期処理が実現できます。

特徴5:エラーハンドリング

システム連携で実現方法の課題となりやすいものの代表例がエラーハンドリングです。Camelは一時的なエラー時のリトライ処理の実現はもちろんのこと、中長期に渡る障害がシステム全体に波及していくことを防ぐためサーキットブレーカーやバルクヘッドといったより高度なエラーハンドリングも簡単な設定で利用することができます。また、エラーの滞留を防ぐために専用のキューにメッセージを移動させる機能もあります。

サンプルコード

このコードは「Japan Camel User Group 」にあるコードの再掲ですが、大雑把な処理内容としては下記処理を行っています。

  • ユーザ登録用のJSONリクエストをRESTサービスで受けつける
  • DBにユーザデータを登録する
  • テンプレートエンジンでメールの文面を作成して、ユーザにメールを送信する(別スレッドを用いて非同期で処理)
  • ユーザの追加を他システムに通知するためのイベント(XML)をキューに登録する
  • エラーハンドリングを行う
// usersのパスにpostが送信されたら処理を開始
// 次のようなリクエストを想定 {name:"username_1", email:"xxxx@redhat.com"}
rest("/users")
  .post()
    .route()
        .hystrix()
          // サーキットブレーカーの設定
          // 10秒以内に処理が終了しなかったらタイムアウトエラーにする
          // 何らかのエラーが15秒以内に2回以上発生したら本サービスを120秒間停止(サーキットブレーカーをOpen)する
          .hystrixConfiguration()
            .executionTimeoutInMilliseconds(10000)
            .metricsRollingPercentileWindowInMilliseconds(15000)
            .circuitBreakerRequestVolumeThreshold(2)
            .circuitBreakerSleepWindowInMilliseconds(120000)
          .end()
          // JSONをMapオブジェクトに変換
          .unmarshal().json(JsonLibrary.Jackson, Map.class)
          // DBにUserデータを保存 
          .to("sql:insert into users(name, email) values(:#name, :#email)")
          // [サブルート呼び出し(別スレッドを用いて非同期で処理)] ユーザへメールを送信 
          .wireTap("direct:userCreatedNotificationMail").end()
          // ユーザの追加を他システムに通知するためのイベント(XML)をキューに登録する
          .transform(new Map2XML("user-created")).to("jms:UserCreated")
          // 成功を応答する
          .transform().constant("{result:\"true\"}")
        // エラーやサービスがクローズされている場合には、失敗を応答する
        .onFallback()
          .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(503))
          .transform().constant("{result:\"false\"}")
        .end();

// [サブルート] メールの文面をテンプレートを用いて作成し、ユーザへメールを送信
from("direct:userCreatedNotificationMail")
  .setHeader("to", simple("${body[email]}"))
  .to("velocity:template/velocity/createdUserNotification.vm")
  .to("smtp://smtpserver?password=&username=");

用語説明

最後に説明時に登場しました重要キーワードの整理をしておきます。

Enterprise Integration Patterns(EIP)

システム統合のプロジェクトに携わった方であればご存知かとは思いますが、エンタープライズインテグレーションではインテグレーションの方式設計が欠かせません。 しかしながら様々なインテグレーション現場では独自に標準化していることもしばしばあり、非効率な処理を強制していることも少なくありません。

Enterprise Integration Patternsは、Gregor Hohpe 氏と Bobby Woolf 氏によりインテグレーションのベストプラクティスをまとめ、 エンタープライズ統合パターンとして著書にしたものです。かなり分厚い英書の書籍ですが、もしも Camel でのインテグレーションを考えているのであれば一度読んでみることをお勧めいたします。 60パターン以上のインテグレーションパターンが紹介されています。

www.enterpriseintegrationpatterns.com

ちなみに、Camelで実現可能なインテグレーションパターンは下記URLにまとまっています。 EIPのほとんどのパターンを実現し、EIPとCamel DSLはほぼ1対1で対応しています。

https://camel.apache.org/components/3.17.x/eips/enterprise-integration-patterns.html:

ドメイン固有言語(DSL)

ドメイン固有言語は一般的に、ある特定の領域に特化したプログラミング言語のことを示します。 Apache CamelはEnterprise Integration Patterns(EIP)とドメイン固有言語(DSL)の組み合わせで処理したいインテグレーション処理を定義します。 この時、Camelルートにドメイン固有言語(DSL)を使用して処理を記述します。例えば、Java DSL、Spring XML、XML DSL、Yaml DSLなどが利用できます。 他のインテグレーションフレームワークも近年DSLを備えているものが増えていますが、Camelと異なり独自の言語により実現していることがあります。CamelはJavaを始めとする複数のプログラム言語で記述することができます。

まとめ

如何でしたでしょうか?
今回は、Apache Camel の概要をテーマに記載させていただいたのでかなり概念的な内容でしたが、なぜ Camel を使うのか?どんな特徴があるのか?など理解いただけたのではないでしょうか。 次回以降は、Camelをベースにした製品であるFuseとその製品ファミリについてご紹介していきたいと思います。

参照リンク

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。