サーバーサイドエンジニアの田実です!
Yappliでは各APIの通信にgRPC, Protobufを採用しています。 本記事ではYappliにおけるgRPC, Protobufの利用方法・運用方法についてご紹介します!
利用箇所
以下のAPI通信においてgRPCを利用しています。
用途 | プロトコル | クライアント | サーバー |
---|---|---|---|
ネイティブアプリ用API | gRPC(gRPC-Gateway経由) | アプリ | Go |
Webフロントエンド用API | gRPC-Web | TypeScript | Go |
サーバー間通信 | gRPC | Go | Go |
gRPCだけではなくgRPC-Gateway, gRPC-WebとgRPC系をフル活用しています!
運用方法
全容はこんな感じになります。
それぞれのインターフェースを変更する場合は、開発者がprotoファイルを修正してprotoファイル用のリポジトリにpushします。 pushされるとCircleCIがhookされ、lintなどのチェックを行った後、protocでコードの自動生成を行います。
Goの場合は以下のように、全てのprotoファイルに対してprotocを実行してコードの自動生成を行っています。
for s in $(find ${protoファイルの場所} -type f); do protoc \ -I . \ -I repos/protobuf/src \ -I repos/grpc-gateway \ -I repos/googleapis \ --go_out=plugins=grpc:${出力先} ./${s} done
protocによってgRPCクライアント・サーバーが自動生成された後は、gRPCクライアント・サーバー用のリポジトリにプッシュします。
CircleCIだと以下のようにしてCI上から特定のリポジトリに自動でコミット・プッシュしています。
cd {出力先} git add . git diff --cached | wc -l | grep -w 0 && exit 0 git commit -m "auto commit from ${CIRCLE_PROJECT_USERNAME}/{protoのリポジトリ}@${CIRCLE_SHA1}" git push origin ${CIRCLE_BRANCH}:${CIRCLE_BRANCH}
これらの自動生成されたコードを利用元のコードからパッケージ・ライブラリとして取り込んで利用します。
例えばGoだと以下のようにしてインポートを行ってクライアント・サーバーのコードを動かしています。
import "github.com/{owner}/{gRPC クライアント・サーバーのリポジトリ}/xxx/yyy"
アップデートしたい場合は以下のコマンドを叩いてgo.mod, go.sumを更新します。
$ go get -u "github.com/{owner}/{repo}@{ブランチ名}"
ブランチごとにコード自動生成の処理が動いているので開発中はブランチ名を指定して取り込めるようになっています。
フロントエンドの場合はpackage.jsonで対象リポジトリのコミットハッシュを書き換えた後 npm install
することで反映できます。
"devDependencies": { ... "{リポジトリ名}": "git+ssh://git@github.com/{owner}/{repo}#{commit_hash}",
gRPC-Gatewayに関しては自動でクライアントの生成はしていないのですが、APIドキュメントの自動生成をしています。詳細は以下の記事を参照してください! tech.yappli.io
Prototoolでフォーマットされてない場合はCI上でエラーになるようにしています。 具体的には以下のコマンドでフォーマットを行い、git diffで差分が発生していたらフォーマットされていないものと見なしてエラーを投げています。
$ prototool format -w
ちなみにPrototoolは現在開発されておらず、代わりにBufを使うように推奨されているので今後置き換えていく予定(多分)
まとめ
YappliにおけるgRPC, Protocol Buffersの運用方法についてご紹介しました。 gRPCを採用する際の運用方法として参考にしていただければ幸いです!