01介绍
我们在上一篇文章「Go 语言基于 Go kit 开发 Web 项目」中,介绍了怎么使用 Go kit 开发 Web 项目,在这篇文章中,我们传输层使用的是 HTTP,本文我们介绍 Go kit 怎么集成 gRPC,也就是说我们在传输层使用 rpc。
在阅读完前面几篇文章后,我们已经了解 Go kit 分为三层,分别是 Transport、Endpoint 和 Service,其中 Transport 负责网络传输,Endpoint 负责接收请求和返回响应,Service 层负责定义业务接口,并实现接口方法。
Go kit 集成 gRPC,主要在 Transport 层实现。
02实现原理
Go kit 集成 gRPC,即将 gRPC 集成到 Go kit 的 Transport 层。Transport 层将接收到的网络请求转换为 Endpoint 层可以处理的对象,主要需要实现两个功能,解码和编码。
其中,解码负责把网络请求转换为 Endpoint 可以处理的请求对象;编码负责将 Endpoint 处理结果转换为响应对象,返回给客户端。
03Go kit 集成 gRPC 的示例项目
在了解完实现原理之后,我们通过示例项目介绍 Go kit 怎么集成 gRPC,关于定义 proto 文件,和使用 protoc 生成 pb 文件,我们在之前的文章中已经介绍过,限于篇幅,本文不再赘述。
使用 Go kit 集成 gRPC,实际上就是在 Transport 层使用 gRPC 传输,除此之外,它和我们上一节讲的使用 Go kit 开发 Web 项目的流程是一样的,共分为五个步骤实现该示例项目,分别是定义 proto 并生成 pb 文件、创建 service 层、创建 endpoint 层、创建 transport 层和定义主函数。
定义 proto
option go_package = "./user";
service UserService {
rpc Register(RegisterReq) returns (RegisterRes) {}
}
生成 pb 文件
protoc -I proto \
> --go_out ./pb/user --go_opt paths=source_relative \
> --go-grpc_out=require_unimplemented_servers=false:./pb/user --go-grpc_opt paths=source_relative \
> proto/user.proto
阅读上面这段代码,我们定义一个 proto 文件,并使用 protoc 工具生成 pb 文件,需要注意的是我们将 require_unimplemented_servers 设置为 false。
原因如下:
- By default, to register services using the methods generated by this tool, the service implementations must embed the corresponding UnimplementedServer for future compatibility. This is a behavior change from the grpc code generator previously included with protoc-gen-go. To restore this behavior, set the option require_unimplemented_servers=false.
Service - 定义接口
type IUser interface {
Register(ctx context.Context, username, email, password string) error
}
type User struct{}
func (u User) Register(ctx context.Context, username, email, password string) error {
if username != "" && email != "" && password != "" {
return nil
}
return errors.New("register param is invalid")
}
阅读上面这段代码,我们在 Service 层创建 IUser 接口,接口包含一个方法 Register,需要注意的是,Register 方法会通过调用 grpc.Handler 的 ServeGRPC 方法,将请求参数传递给 Go kit 处理。
Endpoint - 接收请求和返回响应
func MakeUserEndpoint(user IUser) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(RegisterReq)
err = user.Register(ctx, req.Username, req.Email, req.Password)
if err != nil {
log.Printf("err:%s", err)
}
return RegisterRes{
Username: req.Username,
Email: req.Email,
}, nil
}
}
阅读上面这段代码,在 Endpoint 层,我们给业务接口 IUser 构建 endpoint.Endpoint,用于调用 Service 层的接口的方法处理请求。
Transport - 传输层
type grpcHandler struct {
register grpc.Handler
}
func (g *grpcHandler) Register(ctx context.Context, req *pb.RegisterReq) (*pb.RegisterRes, error) {
_, res, err := g.register.ServeGRPC(ctx, req)
if err != nil {
return nil, err
}
return res.(*pb.RegisterRes), nil
}
func NewUserServer(ctx context.Context, endpoints Endpoints) pb.UserServiceServer {
return &grpcHandler{
register: grpc.NewServer(
endpoints.UserEndpoint,
DecodeRegister,
EncodeRegister,
),
}
}
阅读上面这段代码,我们在 Transport 层实现 pb 文件中的 UserServiceServer 方法,需要注意的是,我们在 NewUserService 函数中,传入 Endpoint。
完整代码,请参阅 Github。
04总结
本文我们通过示例项目介绍 Go kit 怎么集成 gRPC,通过集成 gRPC,Transport 层实现通过 rpc 进行网络传输。