日別アーカイブ: 2019年5月3日

MicroProfile Context Propagation in Open Liberty development builds

このエントリは以下のエントリをベースにしています。
This entry is based on the following one written by Nathan Rauh (Software Engineer, IBM).
https://openliberty.io/blog/2019/03/01/microprofile-concurrency.html

(訳注)
MicroProfile Context Propagationはスタンドアロン仕様として現在仕様作成中の段階です。

completion stages と completable futures はいずれもJDK 8で導入されました。これらを使うと、APIの利用方法に応じて、依存アクションのパイプラインを直列もしくは並列に実行できます。依存するステージの完了をもって従属ステージの実行を呼び出します。completion stagesがもたらすこの機能は、アプリケーションがイベントが発生するとそのイベントに反応するモデルをアプリケーションで実装できるようにする上で非常に有用ですが、組み込み実装(completable future)は、スレッドコンテキストの 一貫性(Consistency)と信頼性(Reliability)、そしてLibertyのグローバルスレッドプールで実行される非同期依存ステージアクションのデフォルト化という、重要な部分を無視します。MicroProfile Context Propagationでは、 ManagedExecutorThreadContextというAPIを導入し、こうした問題に対処しようとしています。MicroProfile Context Propagationの仕様は現在Open Liberty開発ビルドでご利用頂けます。

OpenLiberty development builds
https://openliberty.io/downloads/

Managed executors in MicroProfile Context PropagationのManaged executorを使うと、アクションがどのスレッドで起こるかに関わらず予測可能なスレッドコンテキストで実行されるcompletion stageを利用できます。この機能がない場合、completion stageアクションのスレッドコンテキストは、リクエストスレッド、依存するステージのスレッド、結果をリクエストするスレッド、前のステージを強制的に終了するスレッドのどれでアクションが実行されるかによって決まります。どのコンテキストでアクションが実行されるかの保証はありません。しかしながらMicroProfile Context Propagationを使えば、コンテキストは常にcompletion stageを作成するスレッドからキャプチャされ、アクション実行時に適用されるため、スレッドコンテキストが完全に確定的になります。

その他、コンテキストをキャプチャするのではなく、特定のコンテキストタイプを消去するように設定できます。例えば、セキュリティコンテキストのクリアとは、completion stageのアクションが実行されている間、スレッドに関連付けられているユーザーがいないことを意味します。マネージドexecutorがcompletion stageを作成すると、マネージドexecutorは作成したcompletion stageと関連付けられたままゆえ、スレッドコンテキストの伝播と特定のexecutorを指定しなくても非同期で実行するようにリクエストされる依存stageを決定します。マネージドexecutorは作成された各従属stageと関連付けられたままなので、それらの従属stageから作成された各従属stageなどはパイクラインの全てのステージで予測可能なスレッドコンテキストの伝播が可能になります。

以下の例では、マネージドexecutorを使ってアプリケーションの名前空間を非同期操作で伝播しようとしています。

CompletableFuture<Integer&gt; stage = executor.supplyAsync(supplier1)
    .thenApply(function1)
    .thenApply(function2)
    .thenApply(i -&gt; {
        try {
            DataSource ds = InitialContext.doLookup("java:module/env/jdbc/ds1");
            ...
            return result;
        } catch (Exception x) {
           throw new CompletionExeption(x);
        }
    });

Managed executors in MicroProfile Context PropagationのマネージドexecutorはJava EE Concurrencyのと完全に互換であり、将来プログラミングモデルを統合できる可能性を残しています。特に、org.eclipse.microprofile.concurrent.ManagedExecutorインターフェースはjava.util.concurrent.ExecutorServiceを継承しているため、ManagedExecutorServiceと同じ実行/送信/呼び出し操作が可能です。

MicroProfile Context Propagation では、管理対象でないcompletion stageの利用を簡単にするためのorg.eclipse.microprofile.concurrent.ThreadContextインターフェースを提供しています。マネージドexecutorが作成していないcompletion stageを有している場合でも、MicroProfile Context Propagation ThreadContextの対応するメソッドを使用してexecutorのアクションを事前にコンテキスト化すると、予測可能なスレッドコンテキストで実行できます。

CompletableFuture<Long&gt; stage = CompletableFuture.supplyAsync(supplier1)
    .thenApplyAsync(function1)
    .thenApply(threadContext.contextualFunction(function2));

How to obtain ManagedExecutor and ThreadContext instances

ManagedExecutor と ThreadContext のインスタンスを取得する方法には数種類あります。

  • MicroProfile Context Propagationはプログラミングのための fluent builder pattern を提供します。
ManagedExecutor executor = ManagedExecutor.builder()
    .maxAsync(10)
    .propagated(ThreadContext.APPLICATION, ThreadContext.SECURITY)
    .cleared(ThreadContext.ALL_REMAINING)
    .build();
  • executorインスタンスが不要になったタイミングで、アプリケーションは作成したexecutorインスタンスをシャットダウンする必要があります。
  • Java EE Concurrencyを使っている場合、既存のManagedExecutorServiceをManagedExecutorにキャストできます。
  • MicroProfile Context Propagationを使えば、CDI(cdi-2.0以上が必要です)を使ってインスタンスの構成や注入も可能です。以下はCDI beanでの使用例です。
// CDI qualifier which is used to identify the executor instance
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
public @interface AppContext {}

// Example producer field, defined in a CDI bean,
@Produces @ApplicationScoped @AppContext
ManagedExecutor appContextExecutor = ManagedExecutor.builder()
    .propagated(ThreadContext.APPLICATION)
    .build();

// Example disposer method, also defined in the CDI bean,
void disposeExecutor(@Disposes @AppContext exec) {
    exec.shutdownNow();
}

// Example injection point, defined in a CDI bean,
@Inject @AppContext
ManagedExecutor executor;

...

CompletableFuture<Integer&gt; stage = executor
    .supplyAsync(supplier1)
    .thenApply(function1)
    .thenApplyAsync(value -&gt; {
        try {
            // access resource reference in application's java:comp namespace,
            DataSource ds = InitialContext.doLookup("java:comp/env/jdbc/ds1");
            ...
            return result;
        } catch (Exception x) {
            throw new CompletionException(x);
        }
    });

MicroProfile Context PropagationはJava SEのCompletableFutureを使って構築されており、その周りに必要なインフラストラクチャを構築します。これにより、信頼性の高いスレッドコンテキストの下、Libertyのスレッド処理のパフォーマンスに支えられて、イベントに反応する堅牢なアプリケーションを構築できます。

End-to-End Example App Running MicroProfile on Kubernetes and Istio

このエントリは以下のエントリをベースにしています。
This entry is based on the following one written by Niklas Heidloff (Developer Advocate, IBM).
https://microprofile.io/2019/04/24/end-to-end-example-app-running-microprofile-on-kubernetes-and-istio/

具体的なMicroProfileの機能を説明するよいサンプルがたくさんありますが、これらはIstioの機能を説明する上でもよいサンプルです。特にBookInfoアプリケーションはその好例でしょう。MicroProfileとIstioを活用する包括的なアプリケーションが見当たらなかったので、今回同僚のHerald Uebeleと共に作成しました。このサンプルはオープンソースとして公開しています。この中にはJava開発者がクラウドネイティブのパターンを学んだりマイクロサービスを始めたりするにあたり、数多くの有用な情報が含まれています。

Cloud Native Starter for Java EE based Microservices on Kubernetes and Istio
https://github.com/nheidloff/cloud-native-starter

Kubernetes, Istio and Java

クラウドネイティブアプリケーションを構築する際に開発者はトラフィックルーティング、レジリエンシ(回復性、Resiliency)、分散監視(Distributed Monitoring)、サービスの発見(Service Discovery)などへの対処方法を見つけ出すのは困難ですが、幸いにしてこうした新たな課題のほとんどはコンテナオーケストレーション基盤のKubernetesとサービスメッシュのIstioがよろしくやってくれます。この機能は一般的に、実装した言語に関係なく、アプリケーションに変更を入れる必要もなく、マイクロサービスに対して効果があります。

Kubernetes
https://kubernetes.io/
Istio
https://istio.io/

しかしながら、いくつかの機能はオーケストレーションプラットフォームやサービスメッシュで解決できません。そのかわりにマイクロサービスのビジネスロジックで取り扱う必要があります。具体的には、アプリケーション固有のフェールオーバー機能やメトリック、きめ細かい認可といったものが該当します。

Jaav開発者はEclipse MicroProfileを使ってこの機能を実装できます。MicroProfileはマイクロサービスベースのアーキテクチャを構築するためのJava EE (Enterprise Edition) の拡張で、KubernetesとIstioの機能を補完します。

Eclipse MicroProfile
https://microprofile.io/

KubernetesやIstioが扱えないアプリケーション固有のロジックに加え、通常マイクロサービスを開発する際に必要な便利な機能も提供しています。具体的には、REST APIの呼び出し機構、REST APIの実装機能をドキュメント込みで提供しています。

Design Principles

サンプルアプリケーションは以下の設計原則に従います。

できる限りプラットフォームを活用する(アプリケーション固有のロジックのためにフレームワークを使う)

KubernetesとIstioをトラフィック管理のような機能のために活用することの利点は、これらの機能が言語非依存なところにあります。クラウドネイティブアプリケーションは、多くの場合polyglotである可能性があります。Polyglotゆえに開発者は最善の言語を特定のタスクのために選択できます。可能な場合はいつでも、サンプルアプリケーションはKubernetesとIstioのプラットフォーム機能を利用します。

アプリケーション固有のロジックについては、サンプルアプリケーションでは種々のMicroProfileの機能を使っています。IstioとMicroProfileを組み合わせて利用する方法は以下のエントリをご覧ください。

MicroProfile, the microservice programming model made for Istio
https://www.eclipse.org/community/eclipse_newsletter/2018/september/MicroProfile_istio.php

アプリケーションのコアサービスのためにのみオープンソースコンポーネントを使う

できるだけ多くの開発者に届くよう、サンプルアプリケーションではアプリケーションのコアサービスに対してのみオープンソースプロジェクトを使用しています。具体的には、JavaスタックはAdoptOpenJDKのOpenJ9やOpenJDK、OpenLiberty、MicroProfileを使っています。KubernetesやIstioは明らかにオープンソースプロジェクトです。オープンソースとして利用できないアプリケーションコンポーネントはオプションで交換可能にしています。

AdoptOpenJDK
https://adoptopenjdk.net/
OpenJ9
https://www.eclipse.org/openj9/
OpenLiberty
https://openliberty.io/

最初の体験(Experience)はできるだけ簡単にする

様々なクラウドネイティブの機能について、利用可能なサンプルやスニペット、記事やチュートリアルがたくさんあり、多くは非常に素晴らしいものですが、こうした機能を一つのアプリケーション内で使うと問題がありました。これらのサンプルは時として異なるJavaスタック、異なるバージョンを使っていたり、記事が古くなっていたりしていました。

このサンプルアプリケーションではいくつかの機能を一緒に動作させています。詳細は後述します。非常に簡単にデプロイするスクリプトもあります。これらは1サービスあたり基本的に1個のスクリプトで、Cloud Foundryアプリケーションにおける ‘cf push’ に類似しています。

異なる環境でアプリケーションが動作するようにする

オンプレミス、ハイブリッドクラウドもしくはパブリッククラウドで動作するため、幸いにしてこれはKubernetesの一つの主要な利点です。リポジトリにはMinikubeやIBM Cloud Kubernetes Serviceといったマネージドサービスにアプリケーションをデプロイする方法がございます(訳注:もちろん他社クラウドでも動作します)。

IBM Cloud Kubernetes Service
https://www.ibm.com/cloud/container-service

Functionality of the Sample Application

このプロジェクトでは以下の機能を紹介しています。

Demos
https://github.com/nheidloff/cloud-native-starter#demos

以下のスライドでこのアプリケーションの機能の概要を紹介しています。

How to develop your first cloud-native Applications with Java
https://github.com/nheidloff/cloud-native-starter/blob/master/documentation/FirstCloudNativeApplicationsJava.pdf

下図はサービスとコンポーネントの関係を示したものです。

WebアプリケーションはBFF (Backend for Frontend) サービスのAPIを呼び出して著者と作品を表示します。

Call to Action

クラウドネイティブアプリケーションをよく知りたい場合には、サンプルアプリケーションのコードを入手して手順に従いローカルのMinikube環境を作成し、マイクロサービスをデプロイしてください。すでにKubernetesクラスタをお持ちであれば、セットアップに30分もかからないでしょう。

GitHub Repository
https://github.com/nheidloff/cloud-native-starter
Setup
https://github.com/nheidloff/cloud-native-starter#setup

Istioプラグインが付いているIBM Cloud Kubernetes Serviceというマネージドサービスでもこのアプリケーションを実行できます。IBMではIBM Cloud Liteアカウントを用意しており、これを使うと無料かつクレジットカードも不要で、時間制限なくお使いいただけます。Kubernetesサービスを使うには、Haraldもしくは当方にプロモーションコードを問い合わせてください。その上で、手順に従いIBM Cloudにサービスをデプロイしてください。

いつものように、フィードバックをお寄せください。このサンプルアプリケーションのことや、改善点をお知らせください。Twitter(@nheidloff)のダイレクトメッセージをオープンにしてありますので、そちらにどうぞ。