1概要
この記事では、https://www.lightbend.com/platform/development/lagom-framework[Lagom framework]を調べて、 リアクティブマイクロサービス駆動型アーキテクチャを使用して サンプルアプリケーションを実装します。
簡単に言うと、リアクティブソフトウェアアプリケーションはメッセージ駆動の非同期通信に依存しており、本質的に非常に Responsive 、 Resilient 、および Elastic です。
マイクロサービス主導のアーキテクチャでは、 独立 、 独立 、 単一の責任 、 移動 などの目標を達成するために、システムをコラボレーションサービス間の境界に分割することを意味しました。[The Reactive Manifesto]およびhttps://info.lightbend.com/COLL-20XX-Reactive-Microservices-Architecture-RES-LP.html[Reactive Microservices Architecture]。
2なぜラゴムですか?
Lagomは、モノリスからマイクロサービス主導のアプリケーションアーキテクチャへの移行を念頭に置いて構築されたオープンソースフレームワークです。マイクロサービス駆動型アプリケーションの構築、実行、および監視の複雑さを抽象化します。
舞台裏では、Lagomフレームワークはhttps://www.playframework.com/[Play Framework]、http://akka.io/[Akka]メッセージ駆動型ランタイム、https://kafka.apache.org/を使用します。[Kafka]デカップリングサービスについては、https://martinfowler.com/eaaDev/EventSourcing.html[Event Sourcing]、およびhttps://martinfowler.com/bliki/CQRS.html[CQRS]パターン、およびhttps://onductr .lightbend.com/[Conduct(R)]コンテナ環境におけるマイクロサービスの監視とスケーリングのサポート。
** 3こんにちはワールドin Lagom
**
ユーザーからのあいさつ要求を処理し、その日の天気統計とともにあいさつメッセージを返信するLagomアプリケーションを作成します。
そして私たちは Greeting と__Weatherという2つのマイクロサービスを開発する予定です
挨拶 は、挨拶要求の処理に焦点を当て、天気予報サービスと対話してユーザーに返信します。 Weather マイクロサービスは、今日の天気統計の要求に応えます。
既存のユーザが Greeting マイクロサービスと対話する場合、異なるグリーティングメッセージがユーザに表示されます。
** 4プロジェクト設定
**
それでは、実用的なLagomシステムをセットアップするための手順を簡単に見てみましょう。
4.1. SBTビルド
プロジェクトフォルダ lagom-hello-world を作成し、続いてビルドファイル build.sbt を作成します。 Lagomシステムは通常、それぞれのビルドが関連するサービスのグループに対応する sbt 個のビルドのセットで構成されています。
organization in ThisBuild := "org.baeldung"
scalaVersion in ThisBuild := "2.11.8"
lagomKafkaEnabled in ThisBuild := false
lazy val greetingApi = project("greeting-api")
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq(
lagomJavadslApi
)
)
lazy val greetingImpl = project("greeting-impl")
.enablePlugins(LagomJava)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq(
lagomJavadslPersistenceCassandra
)
)
.dependsOn(greetingApi, weatherApi)
lazy val weatherApi = project("weather-api")
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq(
lagomJavadslApi
)
)
lazy val weatherImpl = project("weather-impl")
.enablePlugins(LagomJava)
.settings(
version := "1.0-SNAPSHOT"
)
.dependsOn(weatherApi)
def project(id: String) = Project(id, base = file(id))
まず始めに、現在のプロジェクトで組織の詳細、 scala バージョン、および無効化 Kafka を指定しました。 Lagomは、各マイクロサービス について、APIプロジェクトと実装プロジェクトという2つの別々のプロジェクトの規則に従います。
APIプロジェクトには、実装が依存するサービスインターフェイスが含まれています。
マイクロサービスでLagom Java APIを使用し、 Cassandraに永続エンティティに関連するイベントをそれぞれ格納するために、 lagomJavadslApi 、 lagomJavadslPersistenceCassandraなどの関連するLagomモジュールに依存関係を追加しました。
また、 greeting-impl プロジェクトは weather-api プロジェクトに依存して、ユーザーに挨拶をしながら天気情報を取得して提供します。
Lagomプラグインのサポートは、 plugins.sbt ファイルを持つプラグインフォルダを作成することによって追加されます。 Lagomプラグインのエントリがあります。アプリケーションの構築、実行、およびデプロイに必要なすべてのサポートを提供します。
また、このプロジェクトにEclipse IDEを使用する場合は、 sbteclipse プラグインが便利です。以下のコードは両方のプラグインの内容を示しています。
addSbtPlugin("com.lightbend.lagom" % "lagom-sbt-plugin" % "1.3.1")
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "3.0.0")
-
project/build.properties ** ファイルを作成し、使用する sbt バージョンを指定します。
sbt.version=0.13.11
** 4.2. プロジェクト生成
**
プロジェクトルートから sbt コマンドを実行すると、次のプロジェクトテンプレートが生成されます。
-
あいさつapi
-
あいさつ
-
お天気API
-
気象黙示録
マイクロサービスの実装を始める前に、Maven風のプロジェクトディレクトリレイアウトに従って、各プロジェクト内に src/main/java および src/main/java/resources フォルダを追加しましょう。
また、 project-root/target/lagom-dynamic-projects 内に2つの動的プロジェクトが生成されます。
-
lagom-internal-meta-project-cassandra
-
lagom-internal-meta-project-service-locator
これらのプロジェクトは、Lagomによって内部的に使用されています。
** 5サービスインターフェース
**
greeting-api プロジェクトでは、次のインタフェースを指定します。
public interface GreetingService extends Service {
public ServiceCall<NotUsed, String> handleGreetFrom(String user);
@Override
default Descriptor descriptor() {
return named("greetingservice")
.withCalls(restCall(Method.GET, "/api/greeting/:fromUser",
this::handleGreetFrom))
.withAutoAcl(true);
}
}
GreetingService は、ユーザーからのグリーティング要求を処理するために handleGreetFrom() を公開します。これらのメソッドの戻り値の型として ServiceCall APIが使用されます。 ServiceCall は2つの型パラメータ Request と Response を取ります。
Request パラメーターは着信要求メッセージのタイプ、 Response パラメーターは発信応答メッセージのタイプです。
上記の例では、リクエストペイロードを使用していません。リクエストタイプは NotUsed 、 Response タイプは String グリーティングメッセージです。
GreetingService は、 Service.descriptor() メソッドのデフォルト実装を提供することによって、呼び出し中に使用される実際のトランスポートへのマッピングも指定します。 greetingservice という名前のサービスが返されます。
handleGreetFrom() サービス呼び出しはRest識別子を使用してマッピングされます。
GET メソッドのタイプとパスID /api/greeting/:fromUser は handleGreetFrom() methodにマップされます。サービス識別子の詳細については、https://www.lagomframework.com/documentation/1.3.x/java/ServiceDescriptors.html[このリンク]をチェックしてください。
同じ行で、 weather-api プロジェクトに WeatherService インターフェースを定義します。 weatherStatsForToday() methodと descriptor() methodはほとんど自明です。
public interface WeatherService extends Service {
public ServiceCall<NotUsed, WeatherStats> weatherStatsForToday();
@Override
default Descriptor descriptor() {
return named("weatherservice")
.withCalls(
restCall(Method.GET, "/api/weather",
this::weatherStatsForToday))
.withAutoAcl(true);
}
};
WeatherStats は、さまざまな天気のサンプル値とその日の天気予報を返すためのランダムルックアップを含むenumとして定義されます。
public enum WeatherStats {
STATS__RAINY("Going to Rain, Take Umbrella"),
STATS__HUMID("Going to be very humid, Take Water");
public static WeatherStats forToday() {
return VALUES.get(RANDOM.nextInt(SIZE));
}
}
6. Lagom Persistence - イベントソーシング
簡単に言うと、 Event Sourcing を利用するシステムでは、すべての変更を不変のドメインイベントとして次々に追加することができます。 現在の状態は、イベントの再生と処理によって導出されます この操作は、基本的に関数型プログラミングパラダイムから知られている foldLeft 操作です。
イベントソーシングは、イベントを追加し、既存のイベントの更新や削除を回避することによって、高い書き込みパフォーマンスを達成するのに役立ちます。
それでは、Greeting-implプロジェクトの永続エンティティ GreetingEntity を見てみましょう。
public class GreetingEntity extends
PersistentEntity<GreetingCommand, GreetingEvent, GreetingState> {
@Override
public Behavior initialBehavior(
Optional<GreetingState> snapshotState) {
BehaviorBuilder b
= newBehaviorBuilder(new GreetingState("Hello "));
b.setCommandHandler(
ReceivedGreetingCommand.class,
(cmd, ctx) -> {
String fromUser = cmd.getFromUser();
String currentGreeting = state().getMessage();
return ctx.thenPersist(
new ReceivedGreetingEvent(fromUser),
evt -> ctx.reply(
currentGreeting + fromUser + "!"));
});
b.setEventHandler(
ReceivedGreetingEvent.class,
evt -> state().withMessage("Hello Again "));
return b.build();
}
}
Lagomは、 PersistentEntity <Command、Entity、Event> Command 型のイベントは setCommandHandler() メソッドを介して生成され、状態の変化は Event 型のイベントとして保持されます。ドメインオブジェクトの状態は、 setEventHandler() メソッドを使用して現在の状態にイベントを適用することによって更新されます。 initialBehavior() 抽象メソッドは、エンティティの Behavior__を定義します。
initialBehavior() では、オリジナルの GreetingState “ Hello”テキストを作成します。
それから ReceivedGreetingCommand コマンドハンドラを定義できます。これは ReceivedGreetingEvent Eventを生成し、イベントログに保持されます。
GreetingState は、 ReceivedGreetingEvent イベントハンドラメソッドによって「Hello Again」に再計算されます。 ** 前述したように、セッターを呼び出すのではなく、現在処理中のイベントから State の新しいインスタンスを作成しています。
Lagomは、サポートされているすべてのコマンドとイベントをまとめて保持するために、 GreetingCommand および GreetingEvent インターフェースの規則に従います。
public interface GreetingCommand extends Jsonable {
@JsonDeserialize
public class ReceivedGreetingCommand implements
GreetingCommand,
CompressedJsonable,
PersistentEntity.ReplyType<String> {
@JsonCreator
public ReceivedGreetingCommand(String fromUser) {
this.fromUser = Preconditions.checkNotNull(
fromUser, "fromUser");
}
}
}
public interface GreetingEvent extends Jsonable {
class ReceivedGreetingEvent implements GreetingEvent {
@JsonCreator
public ReceivedGreetingEvent(String fromUser) {
this.fromUser = fromUser;
}
}
}
** 7. サービス実施+
**
7.1. グリーティングサービス
public class GreetingServiceImpl implements GreetingService {
@Inject
public GreetingServiceImpl(
PersistentEntityRegistry persistentEntityRegistry,
WeatherService weatherService) {
this.persistentEntityRegistry = persistentEntityRegistry;
this.weatherService = weatherService;
persistentEntityRegistry.register(GreetingEntity.class);
}
@Override
public ServiceCall<NotUsed, String> handleGreetFrom(String user) {
return request -> {
PersistentEntityRef<GreetingCommand> ref
= persistentEntityRegistry.refFor(
GreetingEntity.class, user);
CompletableFuture<String> greetingResponse
= ref.ask(new ReceivedGreetingCommand(user))
.toCompletableFuture();
CompletableFuture<WeatherStats> todaysWeatherInfo
= (CompletableFuture<WeatherStats>) weatherService
.weatherStatsForToday().invoke();
try {
return CompletableFuture.completedFuture(
greetingResponse.get() + " Today's weather stats: "
+ todaysWeatherInfo.get().getMessage());
} catch (InterruptedException | ExecutionException e) {
return CompletableFuture.completedFuture(
"Sorry Some Error at our end, working on it");
}
};
}
}
簡単に言うと、(@Guice フレームワークによって提供される) @ Inject を使用して PersistentEntityRegistry および WeatherService 依存関係を注入し、永続的な GreetingEntity__を登録します。
handleGreetFrom()実装は、 CompletionStage APIの実装 CompletableFuture を使用してグリーティング文字列を非同期に処理して返すために ReceivedGreetingCommand を GreetingEntity__に送信しています。
同様に、今日の天気統計を取得するために Weather マイクロサービスを非同期呼び出しします。
最後に、両方の出力を連結して最終結果をユーザーに返します。
サービス記述子インターフェース GreetingService の実装をLagomに登録するには、 AbstractModule を拡張して ServiceGuiceSupport を実装する GreetingServiceModule クラスを作成しましょう:
public class GreetingServiceModule extends AbstractModule
implements ServiceGuiceSupport {
@Override
protected void configure() {
bindServices(
serviceBinding(GreetingService.class, GreetingServiceImpl.class));
bindClient(WeatherService.class);
}
}
また、Lagomは内部的にPlay Frameworkを使用しています。そのため、 src/main/resources/application.conf ファイルにあるPlayの有効なモジュールのリストに自分のモジュールを追加することができます。
play.modules.enabled
+= org.baeldung.lagom.helloworld.greeting.impl.GreetingServiceModule
7.2. 気象サービス
GreetingServiceImpl を見てみると、 WeatherServiceImpl はかなりわかりやすく、一目瞭然です。
public class WeatherServiceImpl implements WeatherService {
@Override
public ServiceCall<NotUsed, WeatherStats> weatherStatsForToday() {
return req ->
CompletableFuture.completedFuture(WeatherStats.forToday());
}
}
私達は、私達がラゴムに天気モジュールを登録するために挨拶モジュールのために上でしたのと同じステップに従います:
public class WeatherServiceModule
extends AbstractModule
implements ServiceGuiceSupport {
@Override
protected void configure() {
bindServices(serviceBinding(
WeatherService.class,
WeatherServiceImpl.class));
}
}
また、WeatherモジュールをPlayの有効なモジュールのフレームワークリストに登録します。
play.modules.enabled
+= org.baeldung.lagom.helloworld.weather.impl.WeatherServiceModule
** 8プロジェクトを実行する
**
Lagomは、単一のコマンドで** 任意の数のサービスを同時に実行することを許可します。
下記のコマンドを押すとプロジェクトを開始できます。
sbt lagom:runAll
これは埋め込まれた サービスロケータ 、埋め込まれた Cassandra を起動し、それからマイクロサービスを並行して起動します。コードが変更されたときにも、同じコマンドで個々のマイクロサービスが再ロードされるため、手動で再起動する必要はありません。
私たちは自分の論理に集中することができ、Lagomはコンパイルとリロードを処理します。正常に開始されると、次のような出力が表示されます。
................[info]Cassandra server running at 127.0.0.1:4000[info]Service locator is running at http://localhost:8000[info]Service gateway is running at http://localhost:9000[info]Service weather-impl listening for HTTP on 0:0:0:0:0:0:0:0:56231 and how the services interact via[info]Service greeting-impl listening for HTTP on 0:0:0:0:0:0:0:0:49356[info](Services started, press enter to stop and go back to the console...)
正常に開始されたら、グリーティングのカールリクエストを行うことができます。
curl http://localhost:9000/api/greeting/Amit
コンソールに次のような出力が表示されます。
Hello Amit! Today's weather stats: Going to Rain, Take Umbrella
既存のユーザーに対して同じカール要求を実行すると、グリーティングメッセージが変更されます。
Hello Again Amit! Today's weather stats: Going to Rain, Take Umbrella
** 9結論+
**
この記事では、Lagomフレームワークを使用して非同期的に対話する2つのマイクロサービスを作成する方法について説明しました。
この記事の完全なソースコードとすべてのコードスニペットは、https://github.com/eugenp/tutorials/tree/master/lagom[GitHubプロジェクト]にあります。