1. 前言

本文的主要目的是让读者能够跟着一步一步成功使用grpc-gateway,只有很少的理论知识。

2. 工作机制

在这里插入图片描述
这是官方给出的一张图片,从上面我们可以得到以下信息:

  • 我们需要从 profile-service.proto 文件生成两部分代码
    1. grpc service代码,stubs代码;
    2. grpc-gateway 反向代理的代码,即插件stubs代码;
  • 接口调用方面,API客户端使用RESTful API直接访问反向代理即可,它会帮你访问gRPC服务;

3. 新建用户

[test@oyk-test ~]$ useradd test	# 新建 test 用户
[test@oyk-test ~]$ passwd test		# 修改密码
[test@oyk-test ~]$ sudo vim /etc/sudoers	# 为 test 用户增加  sudo 权限,使用wq!保存
# 省略前面部分 ...
## Allow root to run any commands anywhere 
root    ALL=(ALL)       ALL
test	ALL=(ALL)		ALL
# 省略后面部分 ...
[test@oyk-test ~]$ su - test		# 切换至 test 用户

4. 安装工具

  • 安装golang
[test@oyk-test ~]$ sudo yum install golang -y
[test@oyk-test ~]$ go version
go version go1.13.3 linux/amd64
  • 安装protoc
# 可以换成新版本,如: v3.10.1 -> v3.20.1
[test@oyk-test ~]$ if [ `arch` == "aarch64" ];then wget https://github.com/protocolbuffers/protobuf/releases/download/v3.10.1/protoc-3.10.1-linux-aarch_64.zip ;elif [ `arch` == 'x86_64' ];then wget https://github.com/protocolbuffers/protobuf/releases/download/v3.10.1/protoc-3.10.1-linux-x86_64.zip ;else echo "暂时没有找别的架构" ;fi
[test@oyk-test ~]$ unzip protoc-3.10.1-linux-aarch_64.zip # x86_64 使用 unzip protoc-3.10.1-linux-x86_64.zip
[test@oyk-test ~]$ sudo ln -s ~/bin/protoc /bin/protoc
[test@oyk-test ~]$ protoc --version
libprotoc 3.10.1
  • 安装protoc-gen-go
[test@oyk-test ~]$ echo "export GOPROXY=https://goproxy.cn" >> ~/.bashrc && source ~/.bashrc
[test@oyk-test ~]$ echo "export GOPATH=~/go" >> ~/.bashrc && source ~/.bashrc
[test@oyk-test ~]$ mkdir helloworld && cd helloworld
[test@oyk-test helloworld]$ go mod init github.com/test/myrepo	# 初始化模块
[test@oyk-test helloworld]$ go install google.golang.org/protobuf/cmd/protoc-gen-go
  • 安装protoc-gen-go-grpc
[test@oyk-test helloworld]$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
  • 安装protoc-gen-grpc-gateway
[test@oyk-test helloworld]$ go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
  • 安装grpc-gateway
[test@oyk-test helloworld]$ mkdir -p $GOPATH/src/github.com/grpc-ecosystem
[test@oyk-test helloworld]$ pushd !$
# 需要注意v2.3.0(包含)之后的版本没有third_party,
# 也就是没有annotations.proto和http.proto;
# v2.3.0之后使用的是buf命令生成stubs源码;
[test@oyk-test grpc-ecosystem]$ wget https://github.com/grpc-ecosystem/grpc-gateway/archive/v2.0.0.zip -O grpc-gateway.zip
[test@oyk-test grpc-ecosystem]$ unzip grpc-gateway.zip && rm -f grpc-gateway.zip
[test@oyk-test grpc-ecosystem]$ mv grpc-gateway-2.0.0 grpc-gateway
[test@oyk-test grpc-ecosystem]$ popd
  • 添加到PATH
[test@oyk-test helloworld]$ echo "PATH=\$PATH:$(go env GOPATH)/bin" >> ~/.bashrc && source ~/.bashrc

5. 开始创建helloworld

  • 生成stubs文件
[test@oyk-test helloworld]$ mkdir -p proto/helloworld/	# 新建 proto 文件夹
# 然后创建helloworld原型文件
[test@oyk-test helloworld]$ cat > proto/helloworld/helloworld.proto <<EOF
syntax = "proto3";
option go_package = "./;__"; 
package helloworld;

// The greeting service definition
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
EOF
[test@oyk-test helloworld]$ protoc -I ./proto \
   --go_out ./proto --go_opt paths=source_relative \
   --go-grpc_out ./proto --go-grpc_opt paths=source_relative \
   ./proto/helloworld/helloworld.proto	# 生成stubs文件
[test@oyk-test helloworld]$ ls proto/helloworld/		# 执行完上述命令后,会多出两个go文件
helloworld_grpc.pb.go  helloworld.pb.go  helloworld.proto
  • 修改 proto/helloworld/helloworld.proto 如下,并生成反向代理stubs
[test@oyk-test helloworld]$ cat proto/helloworld/helloworld.proto 
syntax = "proto3";
option go_package = "./;__";
package helloworld;

import "google/api/annotations.proto";		// +

// Here is the overall greeting service definition where we define all our endpoints
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {			// +
      post: "/v1/example/echo"				// +
      body: "*"								// +
    };										// +
  }
}

// The request message containing the user's name
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
// 增加了5行
[test@oyk-test helloworld]$ go mod tidy && go mod download # 更新依赖和安装依赖
[test@oyk-test helloworld]$ mkdir -p  proto/google/api/
[test@oyk-test helloworld]$ sudo cp $(go env GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api/{annotations.proto,http.proto} proto/google/api/
[test@oyk-test helloworld]$ protoc -I ./proto \
  --go_out ./proto --go_opt paths=source_relative \
  --go-grpc_out ./proto --go-grpc_opt paths=source_relative \
  --grpc-gateway_out ./proto --grpc-gateway_opt paths=source_relative \
  ./proto/helloworld/helloworld.proto # 生成grpc-gateway stubs
[test@oyk-test helloworld]$ ls proto/helloworld/
helloworld_grpc.pb.go  helloworld.pb.go  helloworld.pb.gw.go  helloworld.proto # 生成了 helloworld.pb.gw.go
  • 写主程序main.go文件
  1. 编写普通grpc程序
[test@oyk-test helloworld]$ cat main.go
package main

import (
	"context"
	"log"
	"net"

	"google.golang.org/grpc"

	helloworldpb "github.com/test/myrepo/proto/helloworld"
)

type server struct{
	helloworldpb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, in *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) {
	return &helloworldpb.HelloReply{Message: in.Name + " world"}, nil
}

func main() {
	// Create a listener on TCP port
	lis, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalln("Failed to listen:", err)
	}

	// Create a gRPC server object
	s := grpc.NewServer()
	// Attach the Greeter service to the server
	helloworldpb.RegisterGreeterServer(s, &server{})
	// Serve gRPC Server
	log.Println("Serving gRPC on 0.0.0.0:8080")
	log.Fatal(s.Serve(lis))
}
  1. 改写普通grpc程序,增加代理代码,使用8081端口代理8080
    [test@oyk-test helloworld]$ cat main.go
package main

import (
        "context"
        "log"
        "net"
        "net/http"

        "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
        "google.golang.org/grpc"

        helloworldpb "github.com/test/myrepo/proto/helloworld"
)

type server struct{
        helloworldpb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, in *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) {
        return &helloworldpb.HelloReply{Message: in.Name + " world"}, nil
}

func main() {
        // Create a listener on TCP port
        lis, err := net.Listen("tcp", ":8080")
        if err != nil {
                log.Fatalln("Failed to listen:", err)
        }

        // Create a gRPC server object
        s := grpc.NewServer()
        // Attach the Greeter service to the server
        helloworldpb.RegisterGreeterServer(s, &server{})
        // Serve gRPC server
        log.Println("Serving gRPC on 0.0.0.0:8080")
        go func() {
			log.Fatalln(s.Serve(lis))
		}()

        // Create a client connection to the gRPC server we just started
        // This is where the gRPC-Gateway proxies the requests
        conn, err := grpc.DialContext(
                context.Background(),
                "0.0.0.0:8080",
                grpc.WithBlock(),
                grpc.WithInsecure(),
        )
        if err != nil {
                log.Fatalln("Failed to dial server:", err)
        }

        gwmux := runtime.NewServeMux()
        // Register Greeter
        err = helloworldpb.RegisterGreeterHandler(context.Background(), gwmux, conn)
        if err != nil {
                log.Fatalln("Failed to register gateway:", err)
        }

        gwServer := &http.Server{
                Addr:    ":8081",
                Handler: gwmux,
        }

        log.Println("Serving gRPC-Gateway on http://0.0.0.0:8081")
        log.Fatalln(gwServer.ListenAndServe())
}

6. 测试

在本机上执行:

[root@localhost proto]# curl -X POST -k http://localhost:8081/v1/example/echo -d '{"name": " hello"}'
{"message":" hello world"}

7. 总结

以上就是本文的全部内容,有任何问题请指出,包括错别字。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐