What is gRPC?
gRPC stands for “Google Remote Procedure Call,” an open-source remote procedure call (RPC) framework developed by Google for efficient communication between systems.
Protocol Buffers is a data serialization format developed by Google that expresses data in a smaller size and is faster to send and receive than traditional formats such as JSON and XML. gRPC is a communication framework that utilizes Protocol Buffers.
https://grpc.io/docs/what-is-grpc/introduction/
Click here for an explanation of why protobuf reduces the amount of data.
How to define RPC methods in .proto files
Service Definition:
First, define a service using the service
keyword. A service is a collection of a series of RPC methods.
// test.proto
service MyService {
// RPCメソッドはここに定義される。
}
RPC method definition:
Define methods in the service using the rpc
keyword. The method must specify an input message type and an output message type.
The function name is attached only to the request (the method that sends the request) and not to the response type. This is because the response is defined as the return type of the method.
// test.proto
service MyService {
// 単一のリクエストとレスポンスを持つRPCメソッド
rpc MyMethod(MyRequest) returns (MyResponse);
}
Message Definition:
Defines the message types used in the input and output of the RPC method.
// test.proto
message MyRequest {
// リクエストパラメータを定義
}
message MyResponse {
// レスポンスパラメータを定義
}
streaming
https://grpc.io/docs/languages/go/basics/
Difference between streaming and single request/response models
gRPC also supports streaming RPC.
In traditional RPC communication, the client sends a request to the server, and the server returns a single response. However, streaming allows multiple instances (multiple, consecutive exchanges of data of the same message type) to be sent and received in a single connection.
For example, in server streaming RPC, the client sends a single request and receives multiple responses from the server in chronological order.
Example: A case in which a server sends real-time information on stock prices to a client.
message StockRequest {
string stock_symbol = 1;
}
message StockResponse {
string stock_symbol = 1;
float price = 2;
string timestamp = 3;
}
service StockService {
rpc GetStockUpdates(StockRequest) returns (stream StockResponse);
}
The client sends a StockRequest
, requesting an update on "AAPL"
(Apple Inc. stock symbol). The server receives it and sends a stream of StockResponse
messages about Apple’s stock price.
The response from the server is sent in succession as follows.
{ stock_symbol: "AAPL", price: 146.28, timestamp: "2021-07-14T10:00:00Z" }
{ stock_symbol: "AAPL", price: 146.48, timestamp: "2021-07-14T10:01:00Z" }
{ stock_symbol: "AAPL", price: 146.58, timestamp: "2021-07-14T10:02:00Z" }
{ stock_symbol: "AAPL", price: 146.68, timestamp: "2021-07-14T10:03:00Z" }
Each StockResponse
message is a different instance of the same message type. Each has different data (in this case, stock price and timestamp), but the message structure is the same. In this way, the server can continue to send information to the client in real-time. The client receives these messages and processes them accordingly.
Three streaming types (client, server, and bidirectional)
There are three types of streaming: client streaming RPC, server streaming RPC, and bidirectional streaming RPC.
When streaming is used, precede the message definition with the stream
keyword.
Client Streaming RPC
- The client sends a stream to the server and the server sends back a single response. Useful for sending large amounts of data to the server.
- Example: File upload process: The client splits the file into small chunks and sends the chunks to the server in sequence. The server receives the chunks and reconstructs the original file. When the upload is complete, the server sends a single response to the client regarding the success or failure of the upload.
rpc ClientStreaming(stream MyRequest) returns (MyResponse);
Server streaming RPC:
- The client sends a request and the server sends back a stream of responses. Useful when the server sends continuous data to the client.
- Example: Streaming video playback, real-time data feeds (e.g., stock price information)
rpc ServerStreaming(MyRequest) returns (stream MyResponse);
Bidirectional Streaming RPC
- The client and server send and receive streams to and from each other. Useful when data needs to be exchanged in both directions.
- Example: Applications requiring real-time interaction: communication in chat applications/real-time games
rpc BidirectionalStreaming(stream MyRequest) returns (stream MyResponse);
These definitions are written in a .proto
file and the protobuf compiler ( protoc)
is used to generate source code for the selected language.
Generate Go code from.protofile
After you have finished writing messages and RPC methods in the .proto file, use the protoc
command to generate Go source code from the .proto
file. You need to install protoc-gen-go and
protoc-gen-go-grpc
plug-ins beforehand.
$ protoc --go_out=. --go-grpc_out=. path/to/yourfile.proto
This command generates a Go code from the specified .proto
file and outputs it to the current directory. --go_out=.
and --go-grpc_out=.
options specify that the file should be generated in the current directory. If you want to generate files in a different directory, change it.
For example, if the file is to be generated in the pb
directory, the following.
$ protoc --go_out=pb --go-grpc_out=pb path/to/yourfile.proto
For example, when the following example.proto
file is compiled into code for the Go language, two files are generated.
// ファイル名: example.proto
syntax = "proto3";
package pb; //名前空間の衝突を避けるためにpackageを定義
message Request {
string query = 1;
}
message Response {
string result = 1;
}
service ExampleService {
rpc GetResponse(Request) returns (Response);
}
example.pb.go
This file contains Go structures corresponding to Request and
Response
messages and functions to manipulate them (getter functions, etc.). These are generated directly from the protocol buffer definitions.
Protobuf is a tool that can generate code for a specific programming language from a language-independent schema definition. The generated code is in a format appropriate for the language, including message structure, serialization/deserialization logic, accessors, etc. In the case of the Go language, the messages in the .proto
file are compiled as structures (struct). These structs contain field tags to which Protobuf field numbers are mapped.
Incidentally, in Dart, the message definitions in the .proto
file is compiled as a class, and each field defined in the message is compiled as a property of the class. The role is the same as that of Go language structures.
// example.pb.go(概要)
package pb
type Request struct {
Query string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
}
func (x *Request) Reset() {
*x = Request{}
}
func (x *Request) String() string {
return protoimpl.X.MessageStringOf(x)
}
type Response struct {
Result string `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"`
}
func (x *Response) Reset() {
*x = Response{}
}
func (x *Response) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (m *Request) GetQuery() string {
if m != nil {
return m.Query
}
return ""
}
example_grpc.pb.go
:.
This file contains the gRPC-related code for the ExampleService
service. It contains both client and server interface definitions. On the server side, it provides the basis for implementing the service interface, and on the client side, it provides functions for calling the server’s methods.
// example_grpc.pb.go(概要)
package pb
type ExampleServiceClient interface {
GetResponse(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}
type ExampleServiceServer interface {
GetResponse(context.Context, *Request) (*Response, error)
}
func RegisterExampleServiceServer(s *grpc.Server, srv ExampleServiceServer) {
s.RegisterService(&_ExampleService_serviceDesc, srv)
}
// その他のクライアントやサーバーの実装の詳細
When implementing ExampleService
on the server side, implement the GetResponse
method according to the ExampleServiceServer
interface. On the client side, call the server methods using the ExampleServiceClient
interface.
Example of RPC method usage in Go language (Go gRPC API)
Example of GetResponse
RPC method usage with gRPC server and client
Server-side implementation
On the server side, the ExampleServiceServer
the interface must be implemented.
pb
corresponds to the package name defined in the .proto
file, which will be the package name in the generated Go code. types such as
UnimplementedExampleServiceServer
, Request
, and Response
are all defined in the pb
package.
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "path/to/your/pb" // pbパッケージへのパスを適切に設定
)
type server struct {
pb.UnimplementedExampleServiceServer
}
func (s *server) GetResponse(ctx context.Context, in *pb.Request) (*pb.Response, error) {
log.Printf("Received: %v", in.GetQuery())
return &pb.Response{Result: "Hello " + in.GetQuery()}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterExampleServiceServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
Client-side implementation
On the client side, connect to the server and call the GetResponse
method.
The client code connects to the server and calls GetResponse
RPC to get the response to the query “world”. The server adds “Hello” to the query and returns it, so the client receives the result “Hello world” and outputs it to the log.
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
pb "path/to/your/pb" // pbパッケージへのパスを適切に設定
)
func main() {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewExampleServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.GetResponse(ctx, &pb.Request{Query: "world"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetResult())
}
コメント