Lagomとは、リアクティブなマイクロサービス向けに設計されたサーバサイド用フレームワークです。リアクティブなアプリを構築するためのいろいろな機能を持っています。
Lagomでは基本的に全て非同期で通信を行います。これによって低レイテンシとなり、外部サービスの影響を受けづらくなります。
イベントソーシングとは、「Akka Persistens」(Lagomが使用するPersistensモジュール)の基礎となる設計手法です。この仕組みはエンティティーに対する変化をCRUD(Create、Read、Update、Delete)で表現するのではなく、エンティティーに対するイベントを永続化し、そのイベントを再生することで最新の状態を復元するものです。
また、このイベントソーシングと相性の良い考え方が「CQRS(Command Query Responsibility Segregation:コマンドクエリ責務分離)」です。
CQRSでは、「データの読み込み」と「データの書き込み」は明確に違うものだという前提で、データストアへの処理をクエリ(データ読み込み)とコマンド(データ書き込み)に分けます。通常、コマンドは処理の成功/失敗のみの情報を返します。このコマンドがイベントソーシングにおける「イベント」に当たります。またクエリは、冪等性が保証されなければいけません。
この仕組みを使うと、データの読み込みと書き込みを明確に分離することが可能になります。Lagomは、これをフレームワーク側でサポートしています。
システムに障害は付きものです。重要なのは「いかに障害を小さくとどめて素早く復旧できるか」にあります。
サーキットブレーカーとは、障害が発生したサービスをシステムから分離させることでシステム全体を止めずに運用を続けることができる機能です。
Lagomでは、サーキットブレーカーで下記3つの状態を持ちます。
このサーキットブレーカーの仕組みは簡単に使えます。タイムアウト時間やリトライ回数など、サービスごとに細かく設定することが可能です。
これら以外に、Lagomでは簡単にサービスを実行できる開発環境と「ConductR」による管理機能を持っています。また、デフォルトのデータストアとして組み込みの「Apache Cassandra」がバンドルされています。
APIが提供されているプログラミング言語は「Java」と「Scala」です。
次に、Lagomを実際にインストールしてアプリを作成してみましょう。
まずはここへアクセスし、プロジェクト名を指定して「Project作成」ボタンをクリックします。
プロジェクトをダウンロードしたらsbtコマンドで起動できます。
% cd path/your/project/lagom-scala-sbt % ./sbt runAll
アプリが起動したら、「http://localhost:9000/api/hello/World」にブラウザからアクセスしてみてください。「Hello, World!」と表示されるはずです。
curlコマンドでも試してみましょう。
% curl http://localhost:9000/api/hello/Taro Hello, Taro!
POSTメソッドで下記のようにすれば、データが登録されます。
% curl -X POST -d '{"message":"Hi"}' http://localhost:9000/api/hello/Hanako
#GET
% curl http://localhost:9000/api/hello/Hanako
Hi, Hanako!
サンプルプロジェクトは、4つのサブプロジェクトで構成されます。
hello-lagom-apiとhello-lagom-implがサービスのセットになっており、hello-lagom-apiがサービス定義、hello-lagom-implがサービス実装です。hello-lagom-stream-apiとhello-lagom-stream-implはWebSocketを使用したサンプルです。
では、hello-lagom-apiの「HellolagomService」を見てみましょう。hello-lagom-apiプロジェクトの方では、サービスインタフェースの定義と、エンドポイントとのひも付けを行います。
例えばサンプルのHellolagomServiceでは、下記のようにpathCallでAPIのパスと関数を指定します。ここではGETで/api/hello/
//サンプルを少し編集
override final def descriptor = {
import Service._
named("hello-lagom")
.withCalls(
pathCall("/api/hello/:id", hello _)
)
.withAutoAcl(true)
}
helloメソッドは、ここでは抽象メソッドとして記述されています。サービス用メソッドはServiceCall型を返すメソッドで、引数としてidを受け取り、型パラメーターはリクエスト型、レスポンス型を指定します。
/** * Example: curl http://localhost:9000/api/hello/Alice */ def hello(id: String): ServiceCall[NotUsed, String]
ここではリクエストパラメーターを受け取らないので「NotUsed」です。レスポンスは「String」となっています。
次に、hello-lagom-apiの実装であるhello-lagom-implを見てみましょう。hello-lagom-implの「HellolagomServiceImpl」では、hello-lagom-apiのHellolagomServiceトレイトを実装し、PersistentEntityRegistry(データ永続化用クラス)を持っています。
//サンプルを少し編集
class HellolagomServiceImpl(persistentEntityRegistry: PersistentEntityRegistry) extends HellolagomService {
override def hello(id: String) = ServiceCall { _ =>
val ref = persistentEntityRegistry.refFor[HellolagomEntity](id)
ref.ask(Hello(id))
}
}
helloメソッドではストレージからidをキーにしてエンティティー(PersistentEntityRef)を探し、その結果を返します。
※デフォルトでは、組み込み用Cassandraを永続化に使用しています
試しに、APIを追加してみましょう。最初に、hello-lagom-apiのHellolagomServiceに抽象メソッドを追加します。
def myApi(id: String): ServiceCall[NotUsed, String]
次に、descriptorで上記メソッドと追加するパスをひも付けましょう。
override final def descriptor = {
import Service._
named("hello-lagom")
.withCalls(
pathCall("/api/hello/:id", hello _),
pathCall("/api/myApi/:id", myApi _)
)
.withAutoAcl(true)
}
hello-lagom-implのHellolagomServiceImplでmyApiメソッドを実装します。
override def myApi(id: String) = ServiceCall { _ =>
Future{"myApi id = " + id}
}
ソースの変更は自動でリロードされるので、そのまま下記コマンドを実行してみましょう。追加したAPIが動作しています。
% curl http://localhost:49848/api/myApi/Foo myApi id = Foo
以上がLagomにおけるシンプルなAPI実装の基本です。
今回はマイクロサービス向けフレームワーク「Lagom」の基礎について解説しました。
このフレームワークはマイクロサービス開発を容易にするための機能を多く持っています。本稿では紹介しませんでしたが、サーキットブレーカーやクラスタリング設定も可能になっているので、興味のある方は公式ドキュメントをご確認ください。
次回は、Lightbend Platformの構成要素の1つ、ストリーミングデータ処理フレームワーク「Apache Spark」について解説する予定です。
Java EE 8/9はマイクロサービス、リアクティブに向かう――MVCは生き残れるのか
EJB、SOA、マイクロサービスへと至る大規模システム向けアーキテクチャの変遷Copyright © ITmedia, Inc. All Rights Reserved.