1. 概要

この記事では、 Lagomフレームワークについて説明し、リアクティブマイクロサービス駆動型アーキテクチャを使用してサンプルアプリケーションを実装します。

簡単に言えば、リアクティブソフトウェアアプリケーションはメッセージ駆動型の非同期通信に依存しており、本質的に応答性復元力弾性です。

マイクロサービス主導のアーキテクチャとは、分離自律性単一責任、モビリティ[の目標を達成するために、システムをコラボレーションサービス間の境界に分割することを意味しました。 X220X]など。 これら2つの概念の詳細については、リアクティブマニフェストおよびリアクティブマイクロサービスアーキテクチャを参照してください。

2. なぜラゴム?

Lagomは、モノリスからマイクロサービス主導のアプリケーションアーキテクチャへの移行を念頭に置いて構築されたオープンソースフレームワークです。 マイクロサービス主導のアプリケーションの構築、実行、監視の複雑さを抽象化します。

舞台裏では、Lagomフレームワークは Play Framework Akka メッセージ駆動型ランタイム、 Kafka をデカップリングサービス、 Event Sourcing 、およびCQRSパターン、および ConductR は、コンテナー環境でのマイクロサービスの監視とスケーリングをサポートします。

3. LagomのHelloWorld

ユーザーからのあいさつリクエストを処理し、その日の天気統計とともにあいさつメッセージで返信するLagomアプリケーションを作成します。

また、GreetingWeatherの2つの個別のマイクロサービスを開発します。

あいさつは、あいさつリクエストの処理、気象サービスとのやり取りに焦点を当ててユーザーに返信します。 Weather microserviceは、今日の気象統計のリクエストに対応します。

既存のユーザーがGreetingマイクロサービスを操作している場合、別のグリーティングメッセージがユーザーに表示されます。

3.1. 前提条件

  1. こちらからScala(現在2.11.8バージョンを使用しています)をインストールします
  2. こちらからsbtビルドツール(現在0.13.11を使用)をインストールします

4. プロジェクトの設定

次に、動作するLagomシステムをセットアップする手順を簡単に見てみましょう。

4.1. SBTビルド

プロジェクトフォルダーlagom-hello-worldを作成し、続いてビルドファイルbuild.sbtを作成します。 Lagomシステムは通常、 sbt ビルドのセットで構成され、各ビルドは関連するサービスのグループに対応します。

organization in ThisBuild := "com.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は、マイクロサービスごとに2つの別々のプロジェクトの規則に従います:APIプロジェクトと実装プロジェクト。

APIプロジェクトには、実装が依存するサービスインターフェイスが含まれています。

マイクロサービスでLagomJavaAPIを使用し、それぞれCassandra、の永続エンティティ。

また、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コマンドを実行すると、次のプロジェクトテンプレートが生成されます。

  1. あいさつAPI
  2. あいさつ-impl
  3. 天気-API
  4. 天気-impl

マイクロサービスの実装を開始する前に、各プロジェクト内に src / main /javaフォルダーとsrc/ main / java /resourcesフォルダーを追加してMaven-をフォローしましょう。プロジェクトディレクトリのレイアウトのように。

また、2つの動的プロジェクトが project-root / target /lagom-dynamic-projects内に生成されます。

  1. lagom-internal-meta-project-cassandra
  2. 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つのタイプパラメーターRequestResponseを取ります。

Request パラメーターは着信要求メッセージのタイプであり、Responseパラメーターは発信応答メッセージのタイプです。

上記の例では、リクエストペイロードを使用しておらず、リクエストタイプは NotUsed であり、ResponseタイプはStringグリーティングメッセージです。

GreetingService は、 Service.descriptor()メソッドのデフォルトの実装を提供することにより、呼び出し中に使用される実際のトランスポートへのマッピングも指定します。 greetingserviceという名前のサービスが返されます。

handleGreetFrom()サービス呼び出しはREST識別子を使用してマッピングされます:GETメソッドタイプとパス識別子/api /greeting /:fromUserhandleGreetFrom( )メソッド。 サービス識別子の詳細については、このリンクを確認してください。

同じ行で、Weather-apiプロジェクトでWeatherServiceインターフェイスを定義します。 weatherStatsForToday()メソッドと describeor()メソッドは、ほとんど自明です。

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 は、さまざまな天気のサンプル値と、その日の天気予報を返すランダムルックアップを含む列挙型として定義されています。

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 –イベントソーシング

簡単に言えば、イベントソーシングを利用するシステムでは、すべての変更を不変のドメインイベントとして次々に追加してキャプチャすることができます。 現在の状態は、イベントの再生と処理によって導き出されます。 この操作は本質的に 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 タイプの着信イベントを処理するためのAPI 指示経由 setCommandHandler() メソッドと永続化状態は、タイプのイベントとして変化しますイベント 。 ドメインオブジェクトの状態は、 setEventHandler()メソッドを使用してイベントを現在の状態に適用することで更新されます。 initialBehavior()抽象メソッドは、エンティティのBehaviorを定義します。

initialBehavior()では、元のGreetingState「Hello」テキストを作成します。 次に、 ReceivedGreetingCommand コマンドハンドラーを定義できます。これにより、 ReceivedGreetingEvent イベントが生成され、イベントログに保持されます。

GreetingState は、ReceivedGreetingEventイベントハンドラーメソッドによって「HelloAgain」に再計算されます。 前述のように、セッターを呼び出すのではなく、処理中の現在のイベントから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");
            }
        };
    }
}

簡単に言うと、 @Inject Guice フレームワークによって提供される)を使用してPersistentEntityRegistryWeatherServiceの依存関係を挿入し、永続的な[ X195X]GreetingEntity。

handleGreetFrom()実装は、ReceivedGreetingCommandGreetingEntityに送信し、CompletionStageのCompletableFuture実装を使用してグリーティング文字列を非同期的に処理して返します。 API。

同様に、 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は内部でPlayFrameworkを使用しています。 したがって、 src / main / resources /application.confファイルにあるPlayの有効なモジュールのリストにモジュールを追加できます。

play.modules.enabled
  += com.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());
    }
}

あいさつモジュールについて上記で行ったのと同じ手順に従って、天気モジュールをLagomに登録します。

public class WeatherServiceModule 
  extends AbstractModule 
  implements ServiceGuiceSupport {
 
      @Override
      protected void configure() {
          bindServices(serviceBinding(
            WeatherService.class, 
            WeatherServiceImpl.class));
      }
}

また、天気モジュールをPlayの有効なモジュールのフレームワークリストに登録します。

play.modules.enabled
  += com.baeldung.lagom.helloworld.weather.impl.WeatherServiceModule

8. プロジェクトの実行

Lagomでは、1つのコマンドで任意の数のサービスを実行できます。

以下のコマンドを押すと、プロジェクトを開始できます。

sbt lagom:runAll

これにより、Embedded Service Locator 、Embedded 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

既存のユーザーに対して同じcurlリクエストを実行すると、グリーティングメッセージが変更されます。

Hello Again Amit! Today's weather stats: Going to Rain, Take Umbrella

9. 結論

この記事では、Lagomフレームワークを使用して、非同期で相互作用する2つのマイクロサービスを作成する方法について説明しました。

この記事の完全なソースコードとすべてのコードスニペットは、GitHubプロジェクト利用できます。