Go微服务实战|第12章:gRPC-gateway generate RESTful API
Synopsis: 只需要实现 gRPC 服务端方法,结合 gRPC-gateway 就可以同时帮你生成 RESTful API,它们可以监听在不同的 TCP 端口上,也可以监听同一端口,最后如果你想公开你的 REST API,还可以自动生成 Swagger 接口文档
代码已上传到 https://github.com/wangy8961/grpc-go-tutorial/tree/v0.12 ,欢迎 star
1. 安装插件
请确保你已经按照 https://madmalls.com/blog/post/what-is-grpc/#42-generate-grpc-go-interface 所示,正确地安装了 protoc
编译器(必须有 /usr/local/include/google/protobuf
目录),并安装了 protoc-gen-go
插件
然后,再安装 protoc-gen-grpc-gateway
和 protoc-gen-swagger
两个插件:
2. 服务定义文件中添加 REST 注释
创建 grpc-go-tutorial/restful-api/userpb/service.proto
文件:
syntax = "proto3"; option go_package="userpb"; package user; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; // User define a user message User { string username = 1; string password = 2; } // CreateRequest is the request for creating a user. message CreateRequest { User user = 1; } // GetRequest is the request for getting a user. message GetRequest { string username = 1; } // GetRequest is the response for getting a user. message GetResponse { User user = 1; } // UserService is the user service. service UserService { // Create a new user rpc Create(CreateRequest) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/api/v1/users" body: "*" }; } // Get a specified user rpc Get(GetRequest) returns (GetResponse) { option (google.api.http) = { get: "/api/v1/users/{username}" }; } }
我们在 UserService 服务的 Create 方法下,添加了 REST 注释,标注它支持 HTTP POST 操作,同样的标注 Get 方法支持 HTTP GET 操作
执行如下命令生成 gRPC Golang 代码文件 service.pb.go
:
[root@CentOS userpb]# pwd /root/grpc-go-tutorial/restful-api/userpb [root@CentOS userpb]# protoc -I/usr/local/include -I. \ -I$GOPATH/src \ -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ --go_out=plugins=grpc:. \ service.proto
执行如下命令生成 gRPC Gateway 代码文件 service.pb.gw.go
:
[root@CentOS userpb]# protoc -I/usr/local/include -I. \ -I$GOPATH/src \ -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ --grpc-gateway_out=logtostderr=true:. \ service.proto
3. gRPC 与 REST 监听不同的端口
3.1 实现 gRPC 服务端
创建 grpc-go-tutorial/restful-api/server/main.go
文件:
// Package main implements a server for User service. package main import ( "context" "flag" "fmt" "log" "net" "google.golang.org/grpc/credentials" "github.com/golang/protobuf/ptypes/empty" "google.golang.org/grpc/codes" pb "github.com/wangy8961/grpc-go-tutorial/restful-api/userpb" "google.golang.org/grpc" ) // server is used to implement pb.UserServiceServer. type server struct { users map[string]pb.User } // NewServer creates User service func NewServer() pb.UserServiceServer { return &server{ users: make(map[string]pb.User), } } // Create a new user func (s *server) Create(ctx context.Context, req *pb.CreateRequest) (*empty.Empty, error) { log.Println("--- Creating new user... ---") log.Printf("request received: %v\n", req) user := req.GetUser() if user.Username == "" { return nil, grpc.Errorf(codes.InvalidArgument, "username cannot be empty") } if user.Password == "" { return nil, grpc.Errorf(codes.InvalidArgument, "password cannot be empty") } s.users[user.Username] = *user log.Println("--- User created! ---") return &empty.Empty{}, nil } // Get a specified user func (s *server) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { log.Println("--- Getting user... ---") if req.Username == "" { return nil, grpc.Errorf(codes.InvalidArgument, "username cannot be empty") } u, exists := s.users[req.Username] if !exists { return nil, grpc.Errorf(codes.NotFound, "user not found") } log.Println("--- User found! ---") return &pb.GetResponse{User: &u}, nil } func main() { port := flag.Int("port", 50051, "the port to serve on") certFile := flag.String("certfile", "server.crt", "Server certificate") keyFile := flag.String("keyfile", "server.key", "Server private key") flag.Parse() lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) // Specify the port we want to use to listen for client requests if err != nil { log.Fatalf("failed to listen: %v", err) } fmt.Printf("server listening at %v\n", lis.Addr()) creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) if err != nil { log.Fatalf("failed to load certificates: %v", err) } s := grpc.NewServer(grpc.Creds(creds)) // Create an instance of the gRPC server pb.RegisterUserServiceServer(s, NewServer()) // Register our service implementation with the gRPC server if err := s.Serve(lis); err != nil { // Call Serve() on the server with our port details to do a blocking wait until the process is killed or Stop() is called. log.Fatalf("failed to serve: %v", err) } }
3.2 (可选) 实现客户端
创建 grpc-go-tutorial/restful-api/client/main.go
文件:
// Package main implements a client for User service. package main import ( "time" "context" "flag" "log" "google.golang.org/grpc/credentials" pb "github.com/wangy8961/grpc-go-tutorial/restful-api/userpb" "google.golang.org/grpc" ) func createUserCall(client pb.UserServiceClient, username, password string) { log.Println("--- gRPC Create RPC Call ---") // 设置 10 秒超时时长,可参考 https://madmalls.com/blog/post/grpc-deadline/#21-contextwithtimeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // 调用 Create RPC req := &pb.CreateRequest{ User: &pb.User{ Username: username, Password: password, }, } resp, err := client.Create(ctx, req) if err != nil { log.Fatalf("failed to call Create RPC: %v", err) } log.Println("response:") log.Printf(" - %q\n", resp) } func getUserCall(client pb.UserServiceClient, username string) { log.Println("--- gRPC Get RPC Call ---") ctx, cancel := context.WithTimeout(context.Background(), 10
0 条评论
评论者的用户名
评论时间暂时还没有评论.