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

マイクロサービスと対話する場合、異なるグリーティングメッセージがユーザに表示されます。


3.1. 前提条件

  1. から

    Scala

    (現在2.11.8バージョンを使用しています)をインストールします.


こちら

。から

sbt

ビルドツール(現在0.13.11を使用しています)をインストールします。

** 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

コマンドを実行すると、次のプロジェクトテンプレートが生成されます。

  1. あいさつapi

  2. あいさつ

  3. お天気API

  4. 気象黙示録

マイクロサービスの実装を始める前に、Maven風のプロジェクトディレクトリレイアウトに従って、各プロジェクト内に

src/main/java

および

src/main/java/resources

フォルダを追加しましょう。

また、

project-root/target/lagom-dynamic-projects

内に2つの動的プロジェクトが生成されます。

  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つの型パラメータ

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プロジェクト]にあります。