콘텐츠로 이동

gRPC 게이트웨이

go-zero Gateway는 HTTP 요청을 gRPC 호출로 변환해 RESTful 인터페이스처럼 노출할 수 있게 합니다. 기존 gRPC 서비스를 외부 HTTP client에 공개하거나, API gateway 계층에서 여러 gRPC 서비스를 통합할 때 사용할 수 있습니다.

  1. proto 파일에서 gRPC 서비스 정의를 읽습니다.
  2. 설정 파일에서 HTTP-to-gRPC mapping rule을 읽습니다.
  3. 각 gRPC 메서드에 대응하는 HTTP handler를 생성합니다.
  4. HTTP 서버를 시작하고 들어오는 요청을 받습니다.
  5. 각 HTTP 요청을 gRPC 요청으로 변환합니다.
  6. gRPC 응답을 HTTP 응답으로 변환합니다.
  7. HTTP 응답을 호출자에게 반환합니다.

전체 gateway source는 go-zero/gateway를 참고하세요.

type (
GatewayConf struct {
rest.RestConf
Upstreams []Upstream
Timeout time.Duration `json:",default=5s"`
}
RouteMapping struct {
Method string
Path string
RpcPath string
}
Upstream struct {
Name string `json:",optional"`
Grpc zrpc.RpcClientConf
ProtoSets []string `json:",optional"`
Mappings []RouteMapping `json:",optional"`
}
)
이름설명타입필수 여부예시
RestConfREST 서비스 설정RestConf기본 서비스 설정 참고
UpstreamsgRPC 서비스 설정[]Upstream
Timeouttimeout 시간duration아니요5s
이름설명타입필수 여부예시
Name서비스 이름string아니요demo1-gateway
GrpcgRPC 서비스 설정RpcClientConfRPC 설정 참고
ProtoSetsproto descriptor 파일 목록[]string아니요["hello.pb"]
Mappings라우트 mapping입니다. 비워 두면 기본 gRPC 경로를 사용합니다.[]RouteMapping아니요
이름설명타입필수 여부예시
MethodHTTP 메서드stringget
PathHTTP 경로string/ping
RpcPathgRPC 경로stringhello.Hello/Ping

go-zero에서 gRPC gateway를 사용하는 방법은 두 가지입니다. 하나는 proto descriptor를 사용하는 방식이고, 다른 하나는 gRPC reflection을 사용하는 방식입니다.

proto descriptor 방식은 protoc으로 생성한 descriptor 파일(.pb)을 gateway 설정에 지정합니다.

  1. demo1 프로젝트를 만들고 demo1 디렉터리에 hello.proto 파일을 작성합니다.
syntax = "proto3";
package hello;
option go_package = "./hello";
message Request {
}
message Response {
string msg = 1;
}
service Hello {
rpc Ping(Request) returns(Response);
}
  1. demo1 디렉터리 아래에 gateway 디렉터리를 만들고, demo1 디렉터리에서 다음 명령으로 proto descriptor를 생성합니다.
Terminal window
$ protoc --descriptor_set_out=gateway/hello.pb hello.proto
  1. demo1 디렉터리에서 다음 명령으로 gRPC 서비스 코드를 생성합니다.
Terminal window
$ goctl rpc protoc hello.proto --go_out=server --go-grpc_out=server --zrpc_out=server

demo1/server/internal/logic/pinglogic.goPing 메서드 로직을 채웁니다.

func (l *PingLogic) Ping(in *hello.Request) (*hello.Response, error) {
return &hello.Response{
Msg: "pong",
}, nil
}
  1. demo1/server/etc/hello.yaml 설정 파일을 다음처럼 수정합니다.
Name: hello.rpc
ListenOn: 0.0.0.0:8080
  1. demo1/gateway 디렉터리로 이동해 etc 디렉터리를 만들고 gateway.yaml 설정 파일을 추가합니다.
Name: demo1-gateway
Host: localhost
Port: 8888
Upstreams:
- Grpc:
Target: localhost:8080
# proto descriptor 파일입니다
ProtoSets:
- hello.pb
# HTTP 경로와 gRPC 메서드 매핑입니다
Mappings:
- Method: get
Path: /ping
RpcPath: hello.Hello/Ping
  1. demo1/gateway 디렉터리에 gateway.go 파일을 만들고 다음 내용을 작성합니다.
package main
import (
"flag"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/gateway"
)
var configFile = flag.String("f", "etc/gateway.yaml", "config file")
func main() {
flag.Parse()
var c gateway.GatewayConf
conf.MustLoad(*configFile, &c)
gw := gateway.MustNewServer(c)
defer gw.Stop()
gw.Start()
}

RPC 서버를 실행합니다.

Terminal window
$ go run hello.go
Starting rpc server at 0.0.0.0:8080...

Gateway를 실행합니다.

Terminal window
$ go run gateway.go

HTTP 요청으로 확인합니다.

Terminal window
$ curl http://localhost:8888/ping
{"msg":"pong"}%
  1. demo2 프로젝트를 만들고 demo2 디렉터리에 hello.proto 파일을 작성합니다.
syntax = "proto3";
package hello;
option go_package = "./hello";
message Request {
}
message Response {
string msg = 1;
}
service Hello {
rpc Ping(Request) returns(Response);
}
  1. demo2 디렉터리 아래에 gateway 디렉터리를 만듭니다.

  2. demo2 디렉터리에서 다음 명령으로 gRPC 서비스 코드를 생성합니다.

Terminal window
$ goctl rpc protoc hello.proto --go_out=server --go-grpc_out=server --zrpc_out=server

demo2/server/internal/logic/pinglogic.goPing 메서드 로직을 채웁니다.

func (l *PingLogic) Ping(in *hello.Request) (*hello.Response, error) {
return &hello.Response{
Msg: "pong",
}, nil
}

demo2/server/etc/hello.yaml 설정 파일을 다음처럼 수정합니다.

Name: hello.rpc
ListenOn: 0.0.0.0:8080
Mode: dev
  1. demo2/gateway 디렉터리로 이동해 etc 디렉터리를 만들고 gateway.yaml 설정 파일을 추가합니다.
Name: demo1-gateway
Host: localhost
Port: 8888
Upstreams:
- Grpc:
Target: localhost:8080
# HTTP 경로와 gRPC 메서드 매핑입니다
Mappings:
- Method: get
Path: /ping
RpcPath: hello.Hello/Ping
  1. demo2/gateway 디렉터리에 gateway.go 파일을 만들고 다음 내용을 작성합니다.
package main
import (
"flag"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/gateway"
)
var configFile = flag.String("f", "etc/gateway.yaml", "config file")
func main() {
flag.Parse()
var c gateway.GatewayConf
conf.MustLoad(*configFile, &c)
gw := gateway.MustNewServer(c)
defer gw.Stop()
gw.Start()
}

RPC 서버를 실행합니다.

Terminal window
$ go run hello.go
Starting rpc server at 0.0.0.0:8080...

Gateway를 실행합니다.

Terminal window
$ go run gateway.go

HTTP 요청으로 확인합니다.

Terminal window
$ curl http://localhost:8888/ping
{"msg":"pong"}%

WithDialer로 사용자 정의 gRPC 클라이언트 사용하기(v1.10.0부터)

섹션 제목: “WithDialer로 사용자 정의 gRPC 클라이언트 사용하기(v1.10.0부터)”
package main
import (
"flag"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/gateway"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
)
var configFile = flag.String("f", "etc/gateway.yaml", "config file")
func main() {
flag.Parse()
var c gateway.GatewayConf
conf.MustLoad(*configFile, &c)
gw := gateway.MustNewServer(c,
gateway.WithDialer(func(conf zrpc.RpcClientConf) zrpc.Client {
return zrpc.MustNewClient(conf,
zrpc.WithDialOption(grpc.MaxCallRecvMsgSize(50*1024*1024)),
zrpc.WithDialOption(grpc.MaxCallSendMsgSize(50*1024*1024)),
)
}),
)
defer gw.Stop()
gw.Start()
}
사용 사례Dial 옵션
수신 메시지 크기 늘리기grpc.MaxCallRecvMsgSize(n)
송신 메시지 크기 늘리기grpc.MaxCallSendMsgSize(n)
사용자 정의 TLSgrpc.WithTransportCredentials(creds)
Keep-alivegrpc.WithKeepaliveParams(params)