Hatソフトウェアエンジニアの佐藤匡剛です。2018の2日目の記事です。Calendarも同じように各自がテーマもバラバラに(笑)好きなことを書いていきます。Patterns』を紹介します。100ページ程度の短い良書なのですが、自費出版系のLeanpubから出しているので翻訳されそうもないので、せめてブログで紹介したいと思います。Camelを使ってシステム間統合のアプリケーションを作る際に有効なデザインパターンですが、Camelを使ったことのある方ならCamel自体がエンタープライズ統合パターン(EIP)の実装系であることはご存知でしょう。EIPはメッセージングをベースとしたシステム間統合で必須のデザインパターンですが、どちらかというとメッセージングやESBの基本的なコンセプトを再利用可能な形にまとめることが主眼となっています。インテグレーションエンジニアとして実践的にCamelやEIPを使いこなしていると、EIPの基本ブロックを超えて、もう少し抽象レベルの高い設計指針を与えてくれるデザインパターンはないかな、と思うことがあります。したニッチなニーズに応えてくれるパターン本は(私の知る限り)存在しませんでした。Patternsというパターンをずっと執筆しています。こちらも1メッセージの送受信を超えて、システム間の「会話(conversation)」(=一連のメッセージのやり取り)というより抽象レベルの高い振る舞いをパターン化するという試みで、いつ完成するのか分かりませんが出版される日が非常に楽しみです。Patterns』はそうしたインテグレーションエンジニアの要望に応えてくれる内容になっています。BilginがCamelのコンサルタントとして現場で何度も使ってきた、実践的なパターンをまとめたEIP++的なパターン集がこの本です。地味なものもありますが、あくまで現場で何度も使われてきたパターンのみを採用していて、あまり使う場面はないけどカッコイイから入れておこう、というようなミーハーなパターンは1つもないそうです。パターンの一覧それではどんなデザインパターンがあるのか見てみましょう。Handling)」「分散配備(Deployment)」の3カテゴリに分けられています。Patterns』は100ページちょっとしかないので、ある程度洋書を読み慣れた方なら比較的簡単に読めてしまいます。洋書をあまり読んだことのない方でも、分量的にチャレンジするのにちょうど良い本です。この本のパターンの切り口に興味を持たれた方は、実際に読んでみることをお勧めします。Camelデザインパターンと言っていますが、Camelによる実装の詳細以外の、パターンとしてのコンテキスト/問題/解決方法のセットはあらゆるシステム間統合のプロジェクトに適用可能なはずです。Integration、Muleなどを普段使っている方も本書を手にとってみるといいかもしれません。それでは、各カテゴリごとにパターン1つ1つを簡単に紹介していきます。正常系のパターンまずは、例外などの異常系の処理を考慮しない、正常パスにおいてどのようにインテグレーションのルーティングを構成するのがいいのか、ということに関する7つのパターンです。VETROパターンVETROというのは、Validate(データ検証)、Enrich(データ付加)、Transform(データ変換)、Route(ルーティング)、Operate(処理実行)の略です。インテグレーションのフロー(Camelルート)をいくつも開発していくと、たいていはこの5ステップから構成されている、という知見から得られたパターンです。たくさんのインテグレーションを開発しなければいけない場合、このVETROに構造を標準化していくとシステムの見通しがよくなりメンテナンス性が向上します。VETROパターンについては、私の前職からの先輩でESB伝道師であるフェンさんがとても良い記事をすでに書いているので、詳細はそちらを読んでみてください。Model(正規データモデル)パターンこちらはEIPに既に登場しているパターンですが、実プロジェクトでも有効なパターンであるということで再掲されています。複数のシステムを連携させるときに、個々のシステムの独自のデータモデルをそのまま使っているとインテグレーションの中で多対多のデータ変換が必要になります。連携するシステムの数が増えてくると制御不能になるので、インテグレーションのレイヤーでは正規データモデル(CDM)を標準にして、CDMと個々のデータモデルとの一対多のデータ変換に集約させる、というのがこのパターンのアイディアです。小さなアプリケーションではCDMの導入はむしろコストが高いですが、アプリケーションが大きくなっていくとCDM導入の検討が必要になっていきます。Component(端末コンポーネント)パターンCamelルートを開発していると、ルートの中にチャネル固有の技術的な部分(ファイル転送、SOAP、JMS)と純粋にインテグレーションのビジネスロジックの部分とが混在していきます。「関心事の分離」という観点からは、技術的な部分とビジネスロジックの部分を分けることが望ましいため、チャネル固有の技術的な部分を「エッジ(端末)コンポーネント」として別のCamelルートに切り出します。Segregation(コマンドクエリ責務分離)パターンドメイン駆動設計(DDD)の文脈でもよく取り上げられている、いわゆるCQRSのパターンです。コマンド(=データの更新系、CRUDの"CUD")とクエリ(=データの参照系、CRUDの"R")とでは必然的に必要とされるデータモデルも異なってくるので、システムを別々にした方がよいという考え方です。Camelによるインテグレーションのサービス開発でも、コマンドとクエリを別々のサービスに実装することが推奨されます。Route(再利用可能ルート)パターンシステム開発において、コードの再利用は必須のプラクティスです。Camelでも、本格的な実プロジェクトではインテグレーションフローの単位である「Camelルート」の再利用化を追究する必要があります。このパターンでは、「開発時(静的)」と「実行時(動的)」の2つの観点から、どうやってCamelルートの再利用化を促進できるかが議論されます。Reconfiguration(実行時設定変更)パターンインテグレーションの実プロジェクトでは、システム間連携のフローが実行時に完全に固定されてしまうのではなく、パラメータ変更等によって実行時にある程度柔軟に変更できる必要があることがあります。このパターンでは、Camelアプリケーションの設定をどうやって実行時に変更できるようにするか、が議論されます。方法としては、設定ファイルやOSGi、JMXによるもの、メッセージングによるもの、外部データベースによるもの、ルールエンジンやテンプレートエンジンなどをCamelコンポーネントを通して活用するもの、などがあります。Configuration(外部設定化)パターン1つ前のパターンとも関連しますが、(Camelに限らずですが)アプリケーションのパラメータを外部化することも必要です。インテグレーションのアプリケーションでは様々なシステムと様々なプロトコルで連携する必要があるため、多数のパラメータが必要になります。設定パラメータには、大きく環境に関するプロパティとチューニングに関するプロパティの2種類があります。このパターンでは、Camelでそうしたプロパティを扱うための方法が議論されます。例外処理のパターンインテグレーションのアプリケーションでとくに複雑な部分は、何らかの障害が発生した場合の例外処理の方法です。ここでは例外処理に関する7つのパターンが紹介されます。インテグレーションのプロジェクトで技術的に一番面白いのは、この例外処理の部分かもしれません。Integrity(データ整合性)パターンこれはいわば古典的なやり方で、インテグレーションのアプリケーションでデータの整合性を保つためにACID特性のあるトランザクションを使うというパターンです。すべての転送プロトコルでトランザクションがサポートされる訳ではなく、1)トランザクションなし、2)トランザクションあり、3)結果整合性あり、の3つに分類されます。このパターンでは、そのうち最初の2つを扱います。ファイル転送のようにトランザクションのない転送プロトコルでは、トランザクションではない何らかの別の仕組みを使ってACIDな処理を実現させます。RDBやJMSのようにトランザクションをサポートするデータソースに対しては、ローカルトランザクションとグローバルトランザクションの2つの方法が使えます。そのうちグローバルトランザクションについては導入コストが高いため、冪等フィルタを使って代替する方法も示されます。Saga(サーガ)パターンサーガとは、ワークフローや状態マシンの仕組みを使って分散トランザクションの挙動をエミュレートし、トランザクションを使わずに結果整合性を実現しようというパターンです。複数のシステムにまたがった複雑なフローが壮大な叙事詩(=サーガ)のようであるため、この名前が付いたのでしょう。Receiverというパターンが登場しますが、冪等性(idempotency)というのはインテグレーションの世界で重要な概念です。同じ操作を何度実行しても結果が変わらないことを、冪等といいます。冪等性を導入することで、リトライの際に重複したメッセージが何度も実行されてしまうエラーを排除できたり、結果整合性を保証するためのデータ復元の処理が容易になったりします。Retry(再試行)パターンインテグレーションにおいて一時的な通信エラーはつきものですが、そうしたエラーに対してシステムの耐障害性を高める古典的な方法がリトライです。リトライはインテグレーションにおける例外処理の基本ですが、一点注意が必要です。リトライは一時的な通信エラーには使えるが、長時間にわたるネットワーク障害(数時間から数日)やリカバリに人手が必要な障害には使えないという点です。Breakerパターンを使います。Throttling(スロットル調整)パターンシステムが、一日の特定の時間帯だけリクエストが増大するなど、一時的な負荷の増大に対してシステムのスループットを維持しなければいけない場合、一時的にリクエストの処理量を調整して対応する、というのがこのパターンです。リアクティブの世界では、バックプレッシャー(back-pressure)とも呼ばれるテクニックです。スロットル調整の方法は、大きく1)メッセージの受取拒否、2)スレッドのブロック、3)キューによる遅延処理、4)別パス実行による縮退運転、の4つがあります。HystrixやResilience4jなどのフレームワークで有名なパターンです。このパターンのミソは、先ほどのRetryパターンで対処できない比較的永続的な通信障害に対して、システムの耐障害性を高められることです。基本的なアイディアは、外部サービスへの通信に障害が発生した場合、一時的にそのサービスに依存しない予備経路にシステムを移行した上で、一定間隔でその外部サービスへの通信をチェックしてリカバリを試みる、というものです。Channel(エラーチャネル)パターンいわゆるDLQなどのメッセージングに固有の、キューを使った例外処理に関する考慮点を議論したパターンです。Channel(配信不能チャネル)の3種類に分類しています。前者2つがアプリケーション層のエラーチャネル、最後がメッセージングシステムのエラーチャネルです。Channelは、VETROパターンにおけるバリデーションに引っかかるなど、アプリケーション層で見つかったデータ不整合のあるメッセージをエラーログとして送るチャネルです。Channelは、アプリケーション層から外部のサードパーティシステムに送ったメッセージがサードパーティ側で処理失敗して差し戻されたメッセージを記録するチャネルです。Channelは、メッセージングシステムが設定ミス等によりメッセージの宛先を解決できず、配信不能になったメッセージを記録するチャネルです。分散配備のパターン最後のカテゴリは、分散されたインテグレーションのシステムをどのような構成でデプロイするか、に関するパターンです。アーキテクチャレベルでの、パフォーマンスや高可用性を実現するために考慮すべきパターンになります。Instance(サービスインスタンス)パターンこのパターンはロードバランシングと基本的に同義で、サービスをインスタンスの単位でスケールアウトできるようにしよう、ということです。それだけだと簡単ですが、パターンとしてはロードバランシングの際に以下の考慮点を議論しています。ロードバランシングではサービスはステートレスにするのが理想ですが、どうしてもステートフルなサービスが必要な場合はリソースの共有化やセッションアフィニティなどの仕組みの導入が必要になります。何を使って、どうやってロードバランシングするかを検討する必要があります。高可用性(HA)の観点からは、ロードバランサ自体が単一障害点になるのを避ける必要もあります。サービスを並列実行する場合、メッセージの処理順序は重要か、重要ならどうやって順序保証をするかの検討が必要です。サービスを並列実行する場合、呼び出す先の外部リソースが並列処理に対応しているか、リソースの競合が問題にならないかを調べる必要があります。呼び出し合うサービスやリソースの間には依存関係があるので、サービスの並列実行化によって問題が起こらないかを検討する必要があります。シングルトンで実行する必要があるサービスの場合、アクティブ/スタンバイの構成にする必要があります。Serviceパターンで詳しく議論されています。Service(シングルトンサービス)パターンシステム内で動作中のインスタンスがただ1つでなければならないサービスがあります。バッチ処理などがそうです。Instanceパターンによるスケールアウトはできませんが、アクティブ/スタンバイの構成を取ることで高可用性を向上することはできます。シングルトンサービスを実現する方法は色々とあります。Singletonは、まさにそれを実現するための機能です。最近ではKubernetesを使えば簡単にシングルトンなPodを実行できるようになりました。Levelling(負荷平準化)パターン上流のサービスから下流のサービスへメッセージが流れているときに、上流ではリクエスト処理の負荷が一様でなく、不定期に乱高下するような場合、それでも下流のメッセージ処理を一定水準に平準化したいことがあります。decoupling)ことによって、負荷平準化を実現します。メッセージングを普段から使っている当たり前のように感じますが、アーキテクチャレベルでの負荷平準化の観点から、通信の要所要所にキューを差し挟む、ということがこのパターンのポイントなのでしょう。Pipeline(並行パイプライン)パターンシステム全体のパフォーマンスを向上させるために、直列的に行われている処理のフローを一部並列化する、というのは有効なテクニックです。関数型プログラミングでも、副作用のない関数の呼出チェーンの一部を並列実行して処理を最適化するというテクニックを使いますが、それと同様です。このパターンのポイントは、アーキテクチャレベルから見た全体的なインテグレーションフローのパイプラインの中で、どこを並列化できるかを検討することにあります。Bulkhead(遮断壁)パターンBulkheadパターンの考え方は、システムをいくつかのパーティションに区切ってその境界を遮断壁によって守ることで、1つのパーティションで大きな障害が発生してもそれがシステムの残りの部分に波及しないようにする、ということです。インテグレーションシステムへの遮断壁の導入の仕方は、いくつかのレイヤーで考える必要があります。Consolidation(サービス統合整理)パターン最後は、サービスのグループ化に関するガイドラインのパターンです。以下3つの選択肢について、それぞれの利点と欠点が議論されます。Host(ホストごとに1つまたは複数のアプリケーションコンテナ)どのサービスをどのグループにまとめるかを決定する際は、以下の観点を考慮に入れます。Calendarは、先ほども登場したフェンさんの担当です。OpenShiftレッドハットでコンサルティングしているフェンです。HatでOpenShiftのサポートをしているid:nekopです。8...2019-05-13swagger-uiでAPIドキュメント公開方法レッドハットでコンサルティングしているフェンです。Hatの佐藤匡剛です。各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。