序
これから、PlayFramework(以下、Play)を中心に作られたマイクロサービスを相手にする予定。Playは、バージョンが0.1あがるたび、けっこうな数のbreaking changeがある。ということで、旧バージョンで実装されたいくつかのサービスを最新版にバージョンアップさせることを視野に、現時点での最新版のPlayFramework2.7に(再)入門しておく。なお、私はバージョン2.6はほとんど使っていないので、バージョン2.6の知識のキャッチアップも随時することにする。
play2.7を立ち上げておきたい人に。
Playは、sbtベースでプロジェクトを簡単に立ち上げられる(seed templateコマンドは以下)。
Java向けのPlay最新版のテンプレート:
sbt new playframework/play-java-seed.g8
Scala向けのPlay最新版のテンプレート:
sbt new playframework/play-scala-seed.g8
ただ、依存するライブラリを各リポジトリから集めてくる関係で初回起動時には結構時間がかかる。Playをお試しする際には、とりあえず、上のいずれかのコマンドを叩いた上で、sbt runしてから必要な情報を収集するのが吉。
私の場合、Playが開発者モードで起動するまで6分ほど、ブラウザで9000番ポートでアクセスして初回のコンパイルを終えて応答が返ってくるまで30秒ほどかかった。回線状況次第ではもっと時間がかかる。
バージョン2.7のPlayは何が新しいのか。
『Play Framework Blog』の「Play 2.7.0 is here!」に目を通しておこう。そこでは、Play 2.7の8項目の新機能が列挙されている。以下、私の感想:
gRPCのサポート
akka-grpcをベースとしたplay-grpcが提供された。マイクロサービスを発展させていくという見地からは、嬉しいことだ(現時点ではIncubating扱いだけれど)。
grpcが標準でサポートされるようになることで、Protocol Buffersなどの標準的なスキーマ言語を用いて、他のサービスとの連携を行いやすくなる。Protocol Buffersなどを使ったことがない人は、スキーマ言語のイメージを以下で掴んでおこう。
我々が単一RDBに接続する単一のWebアプリだけを書いていられる時代はとうの昔に終わった。データはあちこちのいろんなストレージ技術で保存されているかもしないし、バックエンドも単一サービスではなくて分割されているかもしれないし、クライアントはweb版, iOS版, Android版があってひょっとしたらそれぞれ別の言語で実装されており、外部開発者向けにAPIも公開しなければならない。
だから、そこら中でデータをシリアライズするしデシリアライズするし、通信の両端で解釈に矛盾が無いようにすり合わせる必要がある。でも、すりあわせの目的でいちいち人間と自然言語で会話するのは苦痛なので、私たちは機械処理可能なコードで語りたい。だから、どういうデータがやってくるのかきちんと宣言的DSLで定義しておきたい。そこでスキーマ言語だ。
(出展 スキーマ言語はなぜ重要なのか)
コードのイメージを掴むために、Play2.7のgrpcでのハローワールドgrpcの例題を見ておこう。
Protocol Buffersによるハローワールドgrpcのスキーマ定義:
//(抜粋)
package helloworld;
service GreeterService {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
GreeterServiceのPlay2.7による実装例。
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package example.myapp.helloworld.grpc.helloworld
import akka.stream.Materializer
import javax.inject.Inject
import javax.inject.Singleton
import scala.concurrent.Future
/** User implementation, with support for dependency injection etc */
@Singleton
class GreeterServiceImpl @Inject()(implicit mat: Materializer) extends AbstractGreeterServiceRouter(mat) {
//HelloRequestを受け取りFuture[HelloReply]を返すsayHelloメソッド
override def sayHello(in: HelloRequest): Future[HelloReply] =
Future.successful(HelloReply(s"Hello, ${in.name}!"))
}
// #service-impl
akkaを使ったことがない方には、いくつか見なれないタームがあるだろうが、sayHelloメソッドだけは見ておこう。メソッドのレスポンスがFuture[HelloReply]であることから、サービス間は非同期でやり取りされることがわかる。この例題は今の所scalaベースだけれど、JavaでもFutureベースで書くことができる。上のsayHelloメソッドはJavaではこんな感じに書けるらしい。
@Override
public CompletionStage<HelloReply> sayHello(HelloRequest in) {
String message = String.format("Hello, %s!", in.getName());
HelloReply reply = HelloReply.newBuilder().setMessage(message).build();
return CompletableFuture.completedFuture(reply);
}
grpcのサンプルコードとドキュメントは、以下に整備されつつある。
https://developer.lightbend.com/docs/play-grpc
https://github.com/playframework/play-grpc
play2.7のgrpcがきっかけになり、JVM系のメジャーなフレームワーク(Spring系かな)が相互にgrpcでつながるようになるとうれしい。というか、サービスが大きくなったときに、ガベージコレクションの影響が大きいところをC++やGoで置き換えるのが容易になる、といっところかもしれないけれど。
Akkaと親和的なシャットダウン(Coordinated Shutdown)
Play2.6から導入されているAkka Coordinated Shutdownが、2.7ではPlayのライフサイクルに取り込まれたということらしい。これは実際にPlay2.7で、スケールアップ・スケールダウンを随時行うようなサービスを作った際にありがたみが判るのだろう。
Caffeineによる新たなキャッシュ実装(Play Cache API)
Play Cache APIsが、Java 8ベースのCaffeineをベースにするようになったとのこと。CaffeineはSpringBootなどでも使われ始めている模様。このPAIにより、外部にredisなどを立ち上げてキャッシュしなければならない場合が減るのかもしれない。
「Enhanced Content Security Policy」のサポート
「Content Security Policy(https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)」に不勉強なため、コメントできないが、これを機会に入門しておきたい。
参考 Contents Security Policy(CSP)のお勉強
JavaでForms APIを改善 & "request data"に直接アクセス可に
これはJavaでうれしいことの模様。フォームの簡単にかける、Httpリクエストへの返す際のコードが簡潔になる(Http.Contextなしで書けるように)など。
うれしい一方で、playではバージョンアップするたびに以前の書き方が通用しなくなるのはしばしばあるので、書き方のお作法の変更は注意しておきたい。リリースハイライトの後半に、そのあたりが列挙されている。
https://www.playframework.com/documentation/2.7.x/Highlights27
REST APIで用いるHTTP Error Handlersの刷新
Play 2.7でREST APIを書く際にありがたみが分かるものだろう。個人的には、非同期ベースの利点が活きるgraphqlに今後のPlayが積極対応していってほしいところ。
今後の入門 : grpc + Playの可能性を探る。
ゴールデンウィークくらいをゴールに、grpcをグルー(つなぎ)として用いるマイクロサービスの例題をPlay2.7で書いていきたい。