Multiplexing
gRPC 서비스에서 우리는 하나의 client에 대해서 여러개의 gRPC 서비스로 지원을 해야 하는 경우가 있습니다. 이럴 경우, 서비스들을 하나의 서버에서 각각 띄우지 않아도, gRPC에서는 리퀘스트에 대해서 multiplex를 통해 하나의 gRPC 서버에서 여러개의 서비스를 지원할 수 있습니다.
아래 예제는 이미지를 받는 서비스와 글을 받는 서비스를 따로 구성한 뒤에 multiplex 기능을 통해 하나로 합친 코드의 구현 부분입니다.
func main() {
lis, err := net.Listen("tcp", ":4317")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterArticleTaskServer(s, article.InitArticleServer()) // <1>
pb.RegisterImageTaskServer(s, image.InitImageServer()) // <2>
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
일반적이 하나의 서비스를 등록하는 부분과 다른 부분은 <1> 이후에 <2>을 추가로 실행해주기만 하면 됩니다.
client도 서버와 마찬가지로 2개를 등록해주면 됩니다.
func main() {
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
ac := pb.NewArticleTaskClient(conn)
ic := pb.NewImageTaskClient(conn)
...
}
Metadata
gRPC 어플리케이션은 gRPC 서비스들과 consumer들 사이의 통신을 RPC를 통해서 주로 수행합니다. 대부분의 경우에서 데이터는 서비스들의 비지니스 로직과 consumer들에 연관이 있으며 원격 메소드의 정해진 인자들로 구성됩니다. 하지만 인자들이 아닌 데이터롤 통신을 해야할 때도 있는데, 이 경우에는 gRPC 메타 데이터를 이용할 수 있습니다. gRPC 메타데이터는 client, service 둘 다 사용될 수 있습니다. 주로 사용될 수 있는 부분은 아무래도 보안적인 영역에서 헤더를 교환할 때이며, 이는 추후 gRPC의 보안에 대한 포스팅에서 다루도록 하겠습니다.
메타 데이터는 key, value의 형식으로 되어 있으며 다음과 같은 정의할 수 있습니다.
md := metadata.New(map[string]string{"key1": "val1", "key2": "val2"})
md := metadata.Pairs(
"key1", "val1",
"key1, "val1-2", // key1 : []string
"ket2", "val2",
)
또한 바이너리 데이터를 value로 보낼 수 있는데, base64 인코딩을 한 후에 보내게 됩니다.
그리고 메타 데이터를 읽을 때는 metadata.FromIncomingContext(ctx)를 통해 map 형식으로 데이터를 읽을 수 있습니다.
md, metadataAvailable := metadata.FromIncomingContext(ctx)
Load Balancing
보통 어플리케이션에서 로드밸런싱을 사용하기 위해서 nginx, envoy등 다양한 로드밸런싱 솔루션이 있으면 이를 통해 proxy layer를 구성하여 로드밸런싱 기능을 사용하는 것은 gRPC 환경에서도 좋은 방식입니다. (다만, http/2를 지원하는 솔루션이어야 합니다.) 하지만 gRPC에서도 클라이언트 사이드에서의 load balancing 기능을 제공하며 이번 포스팅에서는 내장된 로드밸런싱 기능에 대한 예제만 써보도록 하겠습니다.
// gRPC: Up and Running example code
pickfirstConn, err := grpc.Dial(
fmt.Sprintf("%s:///%s",
// exampleScheme = "example"
// exampleServiceName = "lb.example.grpc.io"
exampleScheme, exampleServiceName), 1
// "pick_first" is the default option. 2
grpc.WithBalancerName("pick_first"),
grpc.WithInsecure(),)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer pickfirstConn.Close()
log.Println("==== Calling helloworld.Greeter/SayHello " +
"with pick_first ====")
makeRPCs(pickfirstConn, 10)
// Make another ClientConn with round_robin policy.
roundrobinConn, err := grpc.Dial(
fmt.Sprintf("%s:///%s", exampleScheme, exampleServiceName),
// "example:///lb.example.grpc.io"
grpc.WithBalancerName("round_robin"), 3
grpc.WithInsecure(),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer roundrobinConn.Close()
log.Println("==== Calling helloworld.Greeter/SayHello " +
"with round_robin ====")
makeRPCs(roundrobinConn, 10)
Compression
compression은 network 트래픽을 줄이기 위해 지원하는 기능으로 다음과 같인 remote 함수를 호출할 때, 추가해주면 됩니다.
res, err := c.UnaryEcho(ctx, &pb.EchoRequest{Message: msg}, grpc.UseCompressor(gzip.Name))
'개발일지 > go' 카테고리의 다른 글
GORM (2) - CRUD (0) | 2020.04.24 |
---|---|
GORM (1) - Getting Started (0) | 2020.04.23 |
gRPC - Context and Error Handling (0) | 2020.04.11 |
gRPC - Interceptor (0) | 2020.03.31 |
GRPC로 이미지 파일 보내기 (0) | 2020.03.30 |