** * This article was created on November 7, 2019 Let's try gRPC with Go and Docker --LiBz Tech Blog Is the same as **
Hello! I'm Watanabe, an engineer. It's been a full year since I joined LiB in November. This is my fourth post on this blog.
In the previous "Story of introducing a very Rails-like serverless framework" Ruby on Jets "to the production environment" , Jets developer ** tongueroo ** and Ruby creator ** Yukihiro Matsumoto It was very encouraging to have many people share it, including **! Thank you everyone.
This time I will write about the RPC framework ** gRPC ** developed by Google.
It is an open source version of the RPC (Remote Procedure Call) framework called stubby used within Google.
-** High-speed, low-dose, type-guaranteed communication between different languages using Protocol Buffers ** (interface description languages like xml, json) --For server / client ** Automatic source code generation ** --Multi-language support (C ++, Java, Go, Python, Ruby, Node.js, C #, Objective-C, PHP, etc ..) --Communication protocol ** Communication by HTTP / 2 ** --Bidirectional communication using Streaming (conventional communication is 1Request-1Response)
Due to the above features, ** gRPC can solve the problems of connection and data transfer between microserviced systems, and can communicate efficiently **.
--The interface needs to be unified among multiple systems
--Systems change frequently, making it difficult to ensure consistency across all systems
**-> Server / client source code can be automatically generated from Protocol Buffer (.proto file), and multiple languages can be selected **
--Documents for understanding the specifications of multiple systems (specifications, wiki, Swagger, etc ..) Update omissions, description errors
**-> Communication between services is generated from the proto file, so the proto file has the correct specifications (the source code is generated based on the proto file, so no omissions or mistakes occur) **
--Performance tends to deteriorate due to frequent API requests
**-> HTTP / 2 can handle multiple requests and responses with one TCP connection **
--In HTTP / 1, only one resource can be acquired with one connection (the number of connections is limited even if multiple TCPs are pasted at the same time on the browser side).
**-> Because of HTTP / 2 communication, you can continue to use the connection at the time of the first connection without discarding the connection **
Of course, there are not only advantages but also disadvantages. The fact that the communication standard is HTTP / 2 means that ** browsers and load balancers that do not support it cannot receive requests **.
For browsers, reverse proxies such as grpc-gateway and grpc-web There is no problem if you use a library that does Inter-service communication is the HTTP2 protocol, and only browser-server communication is the HTTP1 protocol, so some people may feel uncomfortable with the mixture.
Checking the operation of the gRPC server is also a little troublesome. You can check the operation like the conventional API with curl, but you can not use curl with gRPC. (Recently, you can check the operation of gRPC like curl gRPCurl and gRPC UI which is a GUI client for gRPC. hinastory / items / 131cb603af34e3235ccf) There seems to be a tool)
Let's try gRPC
Introduction to gRPC starting with Go Try gRPC with Golang
Install the above three.
gRPC
$ go get -u google.golang.org/grpc
protoc
It depends on the OS. Please install from here.
protoc-gen-go
$ go get -u github.com/golang/protobuf/protoc-gen-go
Dockerfile
Since we will use Docker this time, we will also prepare Dockerfile and docker-compose.yml.
FROM golang:1.13.1
RUN apt-get update && apt-get install -y unzip
# Install protobuf
# @see https://github.com/yoshi42662/go-grpc/blob/master/server/Dockerfile
RUN mkdir -p /tmp/protoc && \
curl -L https://github.com/protocolbuffers/protobuf/releases/download/v3.10.0/protoc-3.10.0-linux-x86_64.zip > /tmp/protoc/protoc.zip && \
cd /tmp/protoc && \
unzip protoc.zip && \
cp /tmp/protoc/bin/protoc /usr/local/bin && \
chmod go+rx /usr/local/bin/protoc && \
cd /tmp && \
rm -r /tmp/protoc
WORKDIR /study-grpc
COPY . /study-grpc
RUN go get -u google.golang.org/grpc
RUN go get -u github.com/golang/protobuf/protoc-gen-go
docker-compose.yml
For the time being, it's good if the container is running, so I set it to command: bash
docker-compose.yml
version: '3.7'
services:
study-grpc:
build: .
container_name: "study-grpc"
ports:
- 1234:1234
volumes:
- .:/study-grpc
command: bash
tty: true
Define the interface in the proto file and generate the code.
I have created pb / cat.proto
.
syntax = "proto3";
service Cat {
rpc GetMyCat (GetMyCatMessage) returns (MyCatResponse) {}
}
message GetMyCatMessage {
string target_cat = 1;
}
message MyCatResponse {
string name = 1;
string kind = 2;
}
Note that if you forget to write syntax =" proto3 "
, it will be interpreted as proto2.
gRPC generally uses proto3. The difference between proto2 and proto3 is here The article was easy to understand.
The number part of string name = 1
is the tag number. The tag number is used to distinguish the fields. It is said that once the number is assigned, it is better not to change it, so if there is a change, a new number will be assigned.
Then go inside the container and compile the proto file to generate the source code.
#Container startup
$ docker-compose up
#Go inside the container
$ docker exec -it study-grpc bash
#protoc command execution
$ protoc --go_out=plugins=grpc:. ./pb/cat.proto
It is OK if pb / cat.pb.go
is generated.
Create server.go
.
package main
import (
"context"
"errors"
"google.golang.org/grpc"
"log"
"net"
cat "study-grpc/pb"
)
type myCatService struct{}
func (s *myCatService) GetMyCat(ctx context.Context, message *cat.GetMyCatMessage) (*cat.MyCatResponse, error) {
switch message.TargetCat {
case "tama":
return &cat.MyCatResponse{
Name: "tama",
Kind: "Maine Coon",
}, nil
case "mike":
return &cat.MyCatResponse{
Name: "mike",
Kind: "Norwegian Forest Cat",
}, nil
default:
return nil, errors.New("Not Found YourCat..")
}
}
func main() {
port, err := net.Listen("tcp", ":1234")
if err != nil {
log.Println(err.Error())
return
}
s := grpc.NewServer()
cat.RegisterCatServer(s, &myCatService{})
s.Serve(port)
}
Create client.go
.
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"log"
cat "study-grpc/pb"
)
func main() {
conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
if err != nil {
log.Fatal("connection error:", err)
}
defer conn.Close()
client := cat.NewCatClient(conn)
message := &cat.GetMyCatMessage{TargetCat: "mike"}
res, err := client.GetMyCat(context.Background(), message)
if err != nil {
log.Fatal(err)
}
fmt.Printf("result:%s\n", res)
}
Let's build and execute the created server.go
and client.go
.
#Go inside the container
$ docker exec -it study-grpc bash
# server.build go&Run
$ go build server.go
$ ./server
# client.build go&Run
$ go build client.go
$ ./client
#If it is troublesome to build, go run is ok
$ go run server.go
$ go run client.go
Execution result
result:name:"mike" kind:"Norwegian Forest Cat"
What did you think?
Just hearing the name makes me think "gRPC? ProtocolBuffer? It seems difficult," but I think it was easier than I had imagined when I actually moved my hand.
It has a track record of being adopted not only by Google but also by a number of major companies, and the number of cases in Japanese companies is steadily increasing.
There are many challenges to microservices, but gRPC, which solves the communication part, is one of the options, and I definitely want to remember it.
The code used this time is summarized in this repository.
Recommended Posts