[gRPC] Making gRPC API with Protocol buffers
protobuf, backend, api, server, client, grpc ·
Post Series (click to expand)
Implementing gRPC server with Protobuf, Golang, Flutter
π Part 2 - [gRPC] Simple gRPC calls with Golang and Flutter
Protocol Buffers μ΄μ©νμ¬ gRPC API λ§λ€κΈ°
Protocol Buffers λ₯Ό μ¬μ©νμ¬ κ°λ¨ν gRPC λͺ μΈλ₯Ό μμ±νκ³ , protobuf compiler λ₯Ό ν΅ν΄ μμ±λ μ½λλ₯Ό dart(flutter), golang μμ μ΄μ©νλ λ°©λ²μ μ€λͺ νλ€.
Backgrounds
gRPC
gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.
Google μ΄ κ°λ°ν Remote Procedure Call (RPC) framework. μλ²-ν΄λΌμ΄μΈνΈ κ° μΈν°νμ΄μ€λ₯Ό ꡬμΆν λ, HTTP REST + json λ°©μκ³Ό ν¨κ» κ°μ₯ λ§μ΄ μ¬μ©λλ λ°©μμ΄λ€.
Protocol Buffers
Protocol buffers provide a language-neutral, platform-neutral, extensible mechanism for serializing structured data in a forward-compatible and backward-compatible way. Itβs like JSON, except itβs smaller and faster, and it generates native language bindings. Β
Β
Protocol buffers are a combination of the definition language (created in .proto files), the code that the proto compiler generates to interface with data, language-specific runtime libraries, and the serialization format for data that is written to a file (or sent across a network connection).
μμ Google μ΄ κ°λ°ν ν΅μ κ·κ²©μ΄λ€. μ§λ ¬ν/μμ§λ ¬νλ‘ μ€μ network layer μμλ byte format μΌλ‘ ν΅μ νλ©°, μ¬μ©λ² μ체λ JSON κ³Ό κ±°μ μ μ¬νμ§λ§ μ체 μΈμ΄λ‘ message κ·κ²©μ μ μνκ³ protocol buffer compiler λ‘ μ¬μ©νλ €λ language μ λν binding code λ₯Ό μμ±, μν¬νΈνμ¬ μ¬μ©νλ€. JSON μ λΉν΄ λΉ λ₯΄κ³ κ°λ²Όμμ gRPC μ ν¨κ» μ£Όλ‘ microservice μ ꡬνμ λ§μ΄ μ¬μ©νλ νΈμ΄λ€.
Exp.
λ΄ κ²½μ°μλ νμ¬μμλ HTTP + protobuf, HTTP + json λ°©μμ μ¬μ©νκ³ μκ³ , κ°μΈ νλ‘μ νΈμμλ gRPC + protobuf λ₯Ό μ¬μ©νλ€. κ°κ° μ₯λ¨μ μ λλ ·μ΄ λνλλ λ°λ©΄, μ’ ν©μ μΌλ‘λ μ΄λ€ λ°©μμ΄ λ λ«λ€λ₯Ό νκ°λ¦νκΈ°λ νλ κ² κ°λ€.
- HTTP + json
- μ€λΉνλ κ³Όμ μ΄ κ±°μ μμ΄ λ°λ‘ ꡬνμ λ€μ΄κ° μ μλ€.
- HTTP μλ²μ json body parsing λΆλΆμ λ³λλ‘ κ΅¬νν΄μΌ νλ€.
- body κ° human-readable β κ°μ₯ ν° μ₯μ ! λμΌλ‘ λλ²κΉ κ°λ₯
- λ€μν HTTP API test tool (postman, Advanced REST client λ±) μ μ¬μ© κ°λ₯, μλ² μ¬μ΄λλ§ κ°λ°νλ€λ©΄ λ³λμ ν μ€νΈμ© ν΄λΌμ΄μΈνΈλ₯Ό λ§λ€μ§ μμλ λλ€.
- gRPC + protobuf
- ꡬνμ΄ κ³§ λ¬Έμν. .proto νμΌμ μ΄μ©νμ¬ rpc λ₯Ό μ μνμ¬ μ΄ νμΌμ΄ 곧 κ·Έλλ‘ API λ¬Έμκ° λλ€.
- κ° κ°λ° ννΈ (νλ‘ νΈμλ / λ°±μλ) κ° API spec μ 곡μ κ° μ½λ€.
- API λͺ μΈλ₯Ό μν μ€λΉ μμ μ΄ νμνλ€.
- byte-oriented μ΄κΈ° λλ¬Έμ unmarshal κ³Όμ μ κ±°μ³μΌ μ¬λμ΄ μ΄ν΄ν μ μλ ννλ‘ λ³νλλ€.
- Network λΆνκ° μ λ€.
- HTTP + protobuf
- κ°κ°μ λ¨μ λ§ ν©μ³ λμ κ² κ°μ μ‘°ν© π€―
- κΈ°μ‘΄ protobuf λ₯Ό μ¬μ©νλ νλ‘μ νΈλ₯Ό ν¬ν ν΄μΌ νλλ° client λ server side μ μΈμ΄κ° gRPC λ₯Ό μ§μνμ§ μλ κ²½μ° μ΄μ© μ μμ΄ μ¬μ©νλ€λκ° νλ μΈμλ λ±ν μΈλͺ¨κ° μ λ μ€λ₯΄μ§ μλλ€.
API specification with Protocol Buffers
Defining APIs/messages with proto3 language
λ¨Όμ proto3 language λ₯Ό μ΄μ©νμ¬ ν΅μ μ μ¬μ©ν message λ€μ μ μνλ€. proto3 language μ ꡬ체μ μΈ μ¬μ© λ°©λ²μ 곡μ λ¬Έμ λ₯Ό μ°Έμ‘°νμ.
syntax = "proto3";
package protocols;
option go_package = "protocols/goapi";
message User {
string id = 1;
string email = 2;
string name = 3;
}
service UserApi {
rpc GetUser(GetUserRequest) returns (GetUserReply) {}
rpc AddUser(AddUserRequest) returns (AddUserReply) {}
}
message GetUserRequest {
string email = 1;
}
message GetUserReply {
repeated User users = 1;
}
message AddUserRequest {
string email = 1;
string name = 2;
}
message AddUserReply {
User user = 1;
}
option go_package
: golang μμ go modules λ₯Ό ν΅ν΄ μν¬νΈνμ¬ μ¬μ©νκΈ° μν μ΅μ . golang binding code μ package name μ μ§μ ν΄μ€λ€.service
μ μμμ rpc νΈμΆ λͺ μΈλ₯Ό μμ±νκ³- rpc νΈμΆμ μ¬μ©λ κ°κ°μ
message
λͺ μΈλ₯Ό μ μν΄μ€λ€.
κ°μ message format μ μ¬λ¬ κ΅°λ°μ μ¬μ©ν κ²½μ° νλμ common message format μ μ¬μ¬μ©νμ§ μκ³ , κ°κ°μ μ©λμ λ°λΌ message name μ λ€λ₯΄κ² νμ¬ μ λΆ μ μν΄ μ£Όλ κ²μ΄ μ’λ€. κ·Έ νΈμ΄ λμ€μ API μꡬμ¬νμ λ³νμ λ°λΌ message ꡬ쑰λ₯Ό μμ νλλΌλ λ€λ₯Έ API μ μν₯μ λ―ΈμΉμ§ μκ³ νμν λΆλΆλ§ μμ ν μ μκΈ° λλ¬Έμ΄λ€.
Compile to generate language-bindings
μ΄μ μλ²μ ν΄λΌμ΄μΈνΈλ₯Ό ꡬννκΈ° μν΄ μ¬μ©λ binding code λ₯Ό μμ±νλ€.
Installing protoc compiler and plugins
.proto νμΌμ μ»΄νμΌνλ €λ©΄ protoc
protobuf compiler λ₯Ό μ€μΉν΄μΌ νλ€. λν μ¬μ©νλ €λ νλ‘κ·Έλλ° μΈμ΄λ³ plugin λ μ€μΉλμ΄ μμ΄μΌ νλ€.
- Protocol Buffer compiler
- Dart Protocol Buffer plugin
dart pub global activate protoc_plugin
- Golang Protocol Buffer plugin
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
Compiling
νλ‘μ νΈλ₯Ό μ§ννλ©΄μ, API μ μμ λ° μΆκ° λ±μΌλ‘ μΈν΄ λ°λ³΅μ μΌλ‘ μ¬μ©λ κ²μ΄λ―λ‘ shell script λ₯Ό μμ±νλ κ²μ μΆμ²νλ€.
#!/bin/bash
SRC_DIR=$(pwd)
DART_OUT=$SRC_DIR/dartapi/lib
GO_DIR=$SRC_DIR/goapi
rm -rf "$GO_DIR"
rm -rf "$DART_OUT"
mkdir -p "$GO_DIR"
mkdir -p "$DART_OUT"
protoc \
-I="$SRC_DIR" \
--dart_out=grpc:"$DART_OUT" \
--go_out="$SRC_DIR" \
--go_opt=module=protocols \
--go-grpc_out="$SRC_DIR" \
--go-grpc_opt=module=protocols \
"$SRC_DIR"/*.proto
golang κ΄λ ¨ μ΅μ
μ΄ μ’ λ§λ€. golang binding code μ output μμΉλ₯Ό νμ¬ λλ ν 리 SRC_DIR
λ‘ μ§μ ν΄ μ£Όκ³ , option μΌλ‘ module prefix (μ€λͺ
μ°Έκ³ )λ₯Ό μ€μ ν΄μ€λ€. μ΄λ κ² μ€μ νλ©΄ μλμ κ°μ κ΅¬μ‘°λ‘ output μ΄ μμ±λλ€.
βββ dartapi
βΒ Β βββ lib
βΒ Β βββ api_user.pb.dart
βΒ Β βββ api_user.pbenum.dart
βΒ Β βββ api_user.pbgrpc.dart
βΒ Β βββ api_user.pbjson.dart
βββ goapi
β βββ api_user.pb.go
β βββ api_user_grpc.pb.go
βββ api_user.proto
βββ gen.sh
server side μμ go modules λ₯Ό ν΅ν΄ μν¬νΈν μ μλλ‘ go.mod νμΌμ μΆκ°νλ€. νλ‘μ νΈ λ£¨νΈ λλ ν 리μμ,
go mod init protocols && go mod tidy
νλ©΄ μλμ κ°μ΄ go.mod νμΌμ΄ μμ±λλ€.
module protocols
go 1.17
require (
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.28.0
)
require (
github.com/golang/protobuf v1.5.2 // indirect
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect
golang.org/x/text v0.3.0 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
)
λ§μ°¬κ°μ§λ‘, flutter(dart) μμ pubspec μΌλ‘ μν¬νΈκ° κ°λ₯νλλ‘ /dartapi λλ ν 리μ pubspec.yaml νμΌμ μΆκ°ν΄μ€λ€.
name: dartapi
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: ">=2.15.0 <3.0.0"
dependencies:
protobuf: ^2.0.1
μ΄μ μλ²μ ν΄λΌμ΄μΈνΈλ₯Ό κ°κ°μ μΈμ΄λ‘ ꡬνν μ€λΉκ° λλ¬λ€. λ€μ νΈμμλ μ΄λ κ² μμ±ν binding code λ₯Ό μ΄λ»κ² μν¬νΈνκ³ μ¬μ©νλμ§ μμ보μ.