Golang: gRPC Steps Explained
Step by step guide
High level overview of gRPC
- Define service using protocol buffer language
- Generate native code magic glue to tie readable protobuf definition and code (server & client application code you will write)
- Write Server side code
- Write Client side code
First, what is gRPC? We know RPC stands for Remote Procedure Call. I have also done an article here to cover how to use the net/rpc standard library package to build one. gRPC is an open-source universal RPC framework built by Google. Why universal? The data exchange format via the network between the server and client is based on protocol buffer format. This format is written only to be machine readable. BUT a human-readable protocol buffer language is used to defined this format. The universal part comes from the fact that codes manipulating this format can be generated in various languages, not just in Golang, but in java, python etc. This has the added advantage that your client and server go don’t have to use the same language codebase.
Let try to create a gRPC for a chinese food delivery service, using the protocol buffer language. At the time of writing it is in version 3. Note also that you should not use camelCase but could use “_” for descriptors.
syntax = "proto3";
option go_package = "domain.com/me/takeout"service Takeouts {
rpc GetDish(DishRequest) returns (DishReply) {}}message DishRequest {
string dish_name = 1;
int32 dish_id = 2;
int32 dish_special_request = 4;}message Dish {
string dish_name = 1;
int32 dish_id = 2;
int32 last_cooked = 3;
}message DishReply {
Dish dish = 1;
}
Let’s create a folder named service and keep the takeout.proto there.
Second, let’s use the protoc compiler to whip out up the magic clue mentioned in step 2. In the service folder, execute the below command:
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative takeout.proto
Third, time to write our server code in the server folder. There are two sub-steps: (1) Create the service:
type takeoutService struct {
takeouts.UnimplementedTakeoutsServer
}
The takeoutService type is the service handler for the Takeouts service. We define the GetDish() method of the server struct:
func (s *takeoutService) GetDish(ctx context.Context), in *takeout.DishRequest) (*takeout.DishReply, error) { //... some code to get the dish based on DishRequest d := takeout.Dish{ Dish_name: "General Tso's Chicken", ...
} return &takeout.DishReply{Dish: &d}, nil
}
And sub-step (2), which create the server and register the service (“or handler”). The takeout.Register<GetDish>ServiceServer, where the <GetDish> is the name of your rpc function.
lis, err := net.Listen("tcp", ":50051")
s := grpc.NewServer() //create gRPC Servertakeout.RegisterGetDishServiceServer(s, &takeoutService{})//register handlerlog.Fatal(s.Serve(lis))
// crash the program is cannot create the server
Fourth and final step — writing the client software. The final step has three substeps: (1) Establishing a connection to the server:
func setupGrpcConnection(addr string) (*grpc.ClientConn, err) {
return grpc.DialContext(context.background(),
addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(), //block the function until conn
//established
)} // a connection will be returned
You will have to use addr in the format such as “127.0.0.1:50051”.
(2) We need a client next and we can make use of the generated magic code glue again…
func getDishServiceClient(conn *grpc.ClientConn) takeout.GetDishServiceClient {
return takeout.NewGetDishServiceClient(conn)//this is from
//generated code
//connection from (1)}
The last sub-step is to call the GetDish method in the generated takeout package (3) below. Again, takeout.GetDishServiceClient is generated code.
func getDish(client takeout.GetDishServiceClient,
u *takeout.DishRequest,) (*takeout.DishReply, error){ return client.GetDish(context.Background(), u)
}
I must say that one of the downside of using Protocol buffer is that you must remember these steps. It’s a side effect of using the magic generated glue. It does get addictive. In the meantime, happy golang.