1. 安装插件
protoc/usr/local/include/google/protobufprotoc-gen-go
protoc-gen-grpc-gatewayprotoc-gen-swagger
[root@CentOS ~]# go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway2. 服务定义文件中添加 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 操作
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
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.proto3. 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