Connect/goでRESTとgRPCに対応したAPIを作ってみる

Keisuke Morita
The Finatext Tech Blog
10 min readDec 2, 2023

--

この記事はFinatextグループ10周年記念アドベントカレンダーの3日目の記事です。昨日は新川さんが「異世界金融機関は九段下に」という記事を公開しています。

はじめに

こんにちは。Finatextでエンジニアをしている森田圭亮です。

現行のプロジェクトでConnect/gRPCを導入して開発予定なので、ConnectもgRPCも使用経験のない私が自身の理解度upのためにサンプルを作って負荷テストツールのk6を試してみたものを記事にしました。

導入の背景

これまではOpenAPIを定義してRESTfulに開発してましたがドキュメントのメンテナンスが大変だったり実装との間に差分が生じたりと問題がありました。
gRPC + Protocol Buffersを使えばドキュメントは別途定義不要、コードは自動生成、マイクロサービス間で型を強制などのメリットがあり導入することになりました。
また各マイクロサービスは外部からのREST通信にも対応する必要があるためConnectを使うことになりました。

gRPCでサンプル作成

まずgRPCとは何なのかについて公式ページに記載がありました。https://grpc.io/docs/what-is-grpc/introduction/#overview

In gRPC, a client application can directly call a method on a server application on a different machine as if it were a local object, making it easier for you to create distributed applications and services. As in many RPC systems, gRPC is based around the idea of defining a service, specifying the methods that can be called remotely with their parameters and return types.
・・・
gRPC clients and servers can run and talk to each other in a variety of environments — from servers inside Google to your own desktop — and can be written in any of gRPC’s supported languages. So, for example, you can easily create a gRPC server in Java with clients in Go, Python, or Ruby.

gRPCがサポートされている言語であればクライアントとサーバーを容易に作成できるようです。

よく分からなかったのでいろいろ調べてみた結果以下のことが分かりました。
・データフォーマットにProtocol Buffersを使ってバイナリにシリアライズするので送信データ量が減って効率的に通信できる
・.protoファイルにserviceを定義してコンパイルするとクライアントとサーバーのスタブが自動生成される
・ドキュメントは.protoで済むのでswagger定義の手間を省ける
・バックエンド間で通信する場合は特定の言語や環境に左右されず通信できる
・http/2を使用するのでストリームと呼ばれるもので複数のリクエストを同時に行える・HPACKという方式でヘッダーを圧縮して通信できる

私にとってはフルで恩恵を受けるにはまだまだ知識が必要なようです。

とりあえずサンプルを作成してみます。

.
├── cmd
│ └── server
│ └── main.go
├── greet
│ └── v1
│ └── greet.proto
├── go.mod
└── go.sum
greet.proto

rootにgen/を作成してから下記コマンドを実行するとgen/greet/v1配下にprotoで定義されたメソッドを呼び出すためのインターフェースなどがまとまったファイルが作られました。

protoc - go_out=gen - go_opt=paths=source_relative \ 
- go-grpc_out=gen - go-grpc_opt=paths=source_relative greet/v1/*.proto

main.goも最低限の実装にしてコマンドを実行するとレスポンスを返すようになりました。

main.go
grpcurl \                  
-protoset <(buf build -o -) -plaintext \
-d '{"name": "Jane"}' \
localhost:8080 greet.v1.GreetService/Greet

Connectでサンプルを作成

Connectとは何かについて記載がありました。https://connectrpc.com/docs/introduction#seamless-multi-protocol-support

Connect servers and clients support three protocols: gRPC, gRPC-Web, and Connect’s own protocol.
・・・
Connect supports its own protocol: a straightforward HTTP-based protocol that works over HTTP/1.1, HTTP/2, and HTTP/3. It takes the best parts of gRPC and gRPC-Web, including streaming, and packages them into a protocol that’s equally at home in browsers, monoliths, and microservices. By default, implementations support both JSON- and binary-encoded Protobuf

RESTクライアントとgRPCサーバーの通信、若しくはgRPC-WebのクライアントとgRPCサーバー間で通信するには変換プロキシが必要です。
以下の記事が参考になります。

(参照)
https://zenn.dev/t_horikoshi/articles/dfc2a02c7eb30f7f0819
https://future-architect.github.io/articles/20220624a/

しかしConnectはHTTP/1.1, HTTP/2, and HTTP/3上で動作する独自のプロトコルをサポートしているため、クライアント側の変更やプロキシを挟むことなく柔軟に通信することが可能です。

REST/gRPC両対応なのか試してみます。
チュートリアル通りにサンプルを作成した結果、grpcurlとcurl両コマンドでレスポンスが返ることを確認できました!

grpcurl \                                                         
-protoset <(buf build -o -) -plaintext \
-d '{"name": "Jane"}' \
localhost:8080 greet.v1.GreetService/Greet
curl \
--header "Content-Type: application/json" \
--data '{"name": "Jane"}' \
http://localhost:8080/greet.v1.GreetService/Greet

(おまけ) k6で負荷テスト

これまでに作成したConnectサーバーのサンプルに対して負荷テストを実行してみます。

そもそも負荷テストは何をするのか知らなかったので
AWSの記事(https://aws.amazon.com/jp/builders-flash/202309/distributed-test-on-aws-2/?awsf.filter-name=*all)を参照しました。

① 自分の管理するローカルマシンや仮想マシンに導入するタイプ (ソフトウェア型) と、② シナリオをサービスにアップロードすると、サービスがそれに基づいてコンピュートリソースを立ち上げて負荷をかけるタイプ (サービス型)、という 2 種類があります。
・・・
①のソフトウェア型の負荷ツールとして知られているものとしては Apache Bench、 JMeter、 Gatling、 Locust、k6 などが存在します。

今回はk6を使って簡単に動作を確認したいと思います。
k6とは何かについて記載がありました。https://k6.io/docs/#what-is-k6
JavaScriptでスクリプトを作成したらCLIから負荷をかけることができるようです。
WebページをレンダリングなどはできないのでAPIのパフォーマンステスト用で今回は導入する予定でした。
早速インストールして実行してみます。gRPC用も用意されてるのでConnectで作ったサンプルに対してgRPCモジュールとhttpモジュール両方でスクリプトを書いてみました。

gRPCモジュールで書いた方
httpモジュールで書いた方

下記はコマンドと実行結果です。

— vus: 並列でリクエストをするクライアントの数
— duration: 実行時間
メトリクスの詳細についてはhttps://k6.io/docs/using-k6/metrics/reference/#standard-built-in-metrics

コマンドと実行結果
コマンドと実行結果

↑ではユーザー数5人、1分間負荷をかけるテストを実行しました。
checksの部分を見てみるとどちらのリクエストも無事処理されていることを確認できました。

さいごに

この記事ではチュートリアル的な記事になり深掘りはできなかったので
今後プロジェクトが進行する中で気づいた点や改善点など機会があれば記事にできたらなと思います。

明日は齊藤さんによる「愛される会社であり続けるために。人気漫画から見えるヒント」についての記事です。お楽しみに!

Finatextグループでは、エンジニアを募集しています。以下のリンクが、事業内容やエンジニアの業務内容についてのまとめになっているので、興味がある方はぜひチェックしてみてください!

非常にチャレンジングな環境で優秀な方々と働ける環境が整っているかと思います!技術ブログも覗いてみてください!

--

--