RESTおよびOpen APIとのgRPC
本日のゲスト投稿はCoreOSのBrandon Phillips氏からです。CoreOSはLinuxコンテナのためのオープンソースプロジェクトと製品を開発しています。コンセンサスとディスカバリのためのフラッグシップ製品であるetcdと、コンテナエンジンであるrktは、gRPCの早期採用者です。
CoreOSがgRPCを選択した主な理由の1つは、HTTP/2を使用しているため、アプリケーションが単一のTCPポート(Goで利用可能)でHTTP 1.1 REST/JSON APIと効率的なgRPCインターフェースの両方を提示できることです。これにより、開発者はREST Webエコシステムとの互換性を持ちながら、新しく高効率なRPCプロトコルを推進できます。Go 1.6の最近のリリースにより、Goはデフォルトで安定したnet/http2パッケージを ship します。
多くのCoreOSクライアントはHTTP 1.1とJSONで通信するため、gRPCのJSONおよびOpen API Specification(旧Swagger)との容易な相互運用性は非常に価値がありました。HTTP/1.1+JSONベースおよびOpen API Spec APIに慣れているユーザーのために、彼らはオープンソースライブラリの組み合わせを使用して、APIマルチプレクサを使用して、gRPCサービスをgRPCとHTTP RESTの両方の形式で利用できるようにし、ユーザーに両方の世界の利点を提供しました。詳細を掘り下げて、彼らがどのようにそれを達成したかを見てみましょう!
EchoServiceというgRPCアプリケーション
この記事では、gRPC API定義から小さな概念実証gRPCアプリケーションを構築し、RESTサービスゲートウェイを追加し、最後にこれらすべてを単一のTLSポートで提供します。アプリケーションの名前はEchoServiceで、シェルコマンドechoのWeb同等物です。サービスは、送信されたテキストをそのまま返します(「echo」します)。
まず、EchoServiceへの引数を、単一のフィールドvalueを含むEchoMessageというprotobufメッセージで定義します。このメッセージは、service.protoというprotobufファイルに定義します。これがEchoMessageです。
message EchoMessage {
string value = 1;
}
同じ.protoファイル内で、このデータ構造を受け取って返すgRPCサービスを定義します。
service EchoService {
rpc Echo(EchoMessage) returns (EchoMessage) {
}
}
このservice.protoファイルをProtocol Bufferコンパイラprotocで「そのまま」実行すると、GoのスタブgRPCサービスと、さまざまな言語のクライアントが生成されます。しかし、gRPCだけではRESTインターフェースも公開するサービスほど便利ではないため、gRPCサービススタブで止まることはありません。
次に、gRPC REST Gatewayを追加します。このライブラリは、gRPC EchoServiceの上にRESTfulプロキシを構築します。このゲートウェイを構築するために、EchoService .protoにメタデータを追加して、Echo RPCがRESTful POSTメソッドにマッピングされ、すべてのRPCパラメータがJSONボディにマッピングされることを示します。ゲートウェイはRPCパラメータをURLパスとクエリパラメータにマッピングできますが、簡潔さのためにここではこれらの複雑さを省略します。
service EchoService {
rpc Echo(EchoMessage) returns (EchoMessage) {
option (google.api.http) = {
post: "/v1/echo"
body: "*"
};
}
}
これは、ゲートウェイがprotocで生成された後、curlから次のようなHTTPリクエストを受け入れることができることを意味します。
curl -X POST -k https://:10000/v1/echo -d '{"value": "CoreOS is hiring!"}'
これまでのシステム全体は、単一のservice.protoファイルがgRPCサーバーとRESTプロキシの両方を生成する形で、次のようになります。

これらすべてをまとめるために、echoサービスはGoのhttp.Handlerを作成して、プロトコルがHTTP/2であり、Content-Typeが「application/grpc」であるかどうかを検出し、そのようなリクエストをgRPCサーバーに送信します。それ以外はすべてRESTゲートウェイにルーティングされます。コードは次のようになります。
if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
grpcServer.ServeHTTP(w, r)
} else {
otherHandler.ServeHTTP(w, r)
}
試してみるには、動作するGo 1.6開発環境と以下の簡単なコマンドが必要です。
go get -u github.com/philips/grpc-gateway-example
grpc-gateway-example serve
サーバーを実行したら、HTTP 1.1とgRPCインターフェースの両方でリクエストを試すことができます。
grpc-gateway-example echo Take a REST from REST with gRPC
curl -X POST -k https://:10000/v1/echo -d '{"value": "CoreOS is hiring!"}'
もう1つのボーナス:Open API仕様があるため、上記のサーバーをラップトップで実行している場合、https://:10000/swagger-ui/#!/EchoService/Echoで実行されているOpen API UIを閲覧できます。

gRPCを使用してRESTの世界に橋渡しする方法を見てきました。完全なプロジェクトを確認したい場合は、GitHubのrepoをご覧ください。単一のprotobufを使用してAPIを記述するこのパターンは、消費しやすく柔軟なAPIフレームワークにつながると考えており、より多くのプロジェクトでそれを活用できることに興奮しています。