gRPC學習之五:gRPC-Gateway實戰


歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內容:所有原創文章分類匯總及配套源碼,涉及Java、Docker、Kubernetes、DevOPS等;

gRPC學習系列文章鏈接

  1. 在CentOS7部署和設置GO
  2. GO的gRPC開發環境准備
  3. 初試GO版gRPC開發
  4. 實戰四類服務方法
  5. gRPC-Gateway實戰
  6. gRPC-Gateway集成swagger

本篇概覽

  • 本文《gRPC學習》系列的第五篇,gRPC常用於服務端之間的相互調用,如果想把服務暴露給前端,雖然動手修改服務端也能實現,但似乎增加了不少工作量,此時還可以選擇gRPC-Gateway方式來快速將gRPC服務以http的方式暴露出來;
  • gRPC-Gateway原理如下圖,借助grpc-gateway插件,可以基於proto文件生成反向代理(Reverse Proxy)的代碼,這個反向代理運行起來后,對外提供RESTful服務,收到RESTful請求后通過gRPC調用原來的gRPC服務:

在這里插入圖片描述

  • 本文展示了gRPC-Gateway環境搭建、開發、驗證的整個過程,由以下步驟組成:
  1. 極速搭建gRPC-Gateway環境;
  2. 編寫proto文件;
  3. 根據proto文件生成gRPC、gRPC-Gateway源碼;
  4. 添加業務代碼;
  5. 編譯、運行、驗證;

提前說明文件和目錄

  • 本次實戰在$GOPATH/src目錄下新增文件夾helloworld,里面總共有以下內容:
[golang@centos7 src]$ tree helloworld/
helloworld/
├── gateway
│   └── helloworld.gw.go
├── helloworld.pb.go
├── helloworld.pb.gw.go
├── helloworld.proto
├── helloworld.swagger.json
└── server
    └── server.go
  • 准備工作完成,接下來正式開始開發;

前提條件

  • 本文的所有操作都沒有用到root賬號,而是前文創建的golang賬號;
  • 請參照以下兩篇文章將GO環境和gRPC環境搭建好:
  1. 在CentOS7部署和設置GO
  2. GO的gRPC開發環境准備

極速搭建gRPC-Gateway環境

  • 所謂的搭建gRPC-Gateway環境,其實是完成以下三件事:

在這里插入圖片描述

  1. 在搭建環境時參考了一些網上的文章,結果遇到了各種問題一直沒有成功(我當然不會認為文章有問題,必須認識到是自己能力不足的原因所致);
  2. 經過反復折騰后終於成功后,我把所有操作做成一個shell腳本,執行以下命令即可完成上圖中的所有操作:
curl -o install-grpc-gateway.sh \
https://raw.githubusercontent.com/zq2599/blog_demos/master/files/install-grpc-gateway.sh \
&& chmod a+x ./install-grpc-gateway.sh \
&& ./install-grpc-gateway.sh
  1. 進入$GOPATH/bin目錄,可見新增兩個文件protoc-gen-grpc-gatewayprotoc-gen-swagger
[golang@centos7 ~]$ cd $GOPATH/bin
[golang@centos7 bin]$ ls -al
總用量 26708
drwxrwxr-x. 2 golang golang      98 12月 19 08:59 .
drwxrwxr-x. 5 golang golang      39 12月 19 08:21 ..
-rwxr-x---. 1 golang golang 5253272 12月 19 08:20 protoc
-rwxrwxr-x. 1 golang golang 8461147 12月 19 08:21 protoc-gen-go
-rwxrwxr-x. 1 golang golang 6717463 12月 19 08:59 protoc-gen-grpc-gateway
-rwxrwxr-x. 1 golang golang 6908535 12月 19 08:59 protoc-gen-swagger
  • 現在環境准備好了,開始開發;

編寫proto文件

  • $GOPATH/src目錄下,新建文件夾helloworld,里面新建文件helloworld.proto,內容如下,有幾處要注意的地方稍后會說:
// 協議類型
syntax = "proto3";

// 包名
package helloworld;

import "google/api/annotations.proto";

// 定義的服務名
service Greeter {
  // 具體的遠程服務方法
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      post: "/helloworld"
      body: "*"
    };
  }
}

// SayHello方法的入參,只有一個字符串字段
message HelloRequest {
  string name = 1;
}

// SayHello方法的返回值,只有一個字符串字段
message HelloReply {
  string message = 1;
}
  • 上述proto文件有以下幾處要注意的地方:
  1. 整個文件其實就是以 《初試GO版gRPC開發》一文中的helloworld.proto為基礎,增加了兩處內容;
  2. 增加的第一處,是用import關鍵詞導入google/api/annotations.proto
  3. 增加的第二處,是SayHello方法的聲明處,增加了option配置,作用是配置SayHello方法對外暴露的RESTful接口的信息;
  4. 在使用protoc-gen-grpc-gateway的時候,上述兩處配置會被識別到並生成對應的代碼;

根據proto文件生成gRPC、gRPC-Gateway源碼

  1. proto文件編寫完成,接下來是生成gRPC、gRPC-Gateway的源碼;
  2. 生成gRPC源碼的命令咱們前面的文章中已經用過,如下:
protoc -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=plugins=grpc:. \
helloworld.proto
  1. 執行完成后會在當前目錄生成helloworld.pb.go文件;
  2. 執行生成gRPC-Gateway源碼的命令:
protoc -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. \
helloworld.proto
  1. 執行完成后會在當前目錄生成helloworld.pb.gw.go文件;
  2. 執行生成swagger文件的命令:
protoc -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--swagger_out=logtostderr=true:. \
helloworld.proto
  1. 執行完成后會在當前目錄生成helloworld.swagger.json文件;
  2. 至此,helloworld目錄下一共有這些內容:
[golang@centos7 src]$ tree helloworld/
helloworld/
├── helloworld.pb.go
├── helloworld.pb.gw.go
├── helloworld.proto
└── helloworld.swagger.json

0 directories, 4 files
  1. 接下來開始編碼,把運行整個服務所需的代碼補全;
  2. 由於篇幅限制,本文暫不提及swagger相關的開發和驗證,因此生成的helloworld.swagger.json文件本篇用不上,留待下一篇文章使用;

編寫服務端代碼server.go並啟動

  1. 接下來編寫服務端代碼server.go,這個和《初試GO版gRPC開發》中的server.go內容一樣;
  2. $GOPATH/src/helloworld目錄下新建文件夾server,在此文件夾下新建server.go,內容如下,已經添加詳細注釋:
package main

import (
	"context"
	"log"
	"net"

	"google.golang.org/grpc"
	pb "helloworld"
)

const (
	port = ":50051"
)

// 定義結構體,在調用注冊api的時候作為入參,
// 該結構體會帶上SayHello方法,里面是業務代碼
// 這樣遠程調用時就執行了業務代碼了
type server struct {
	// pb.go中自動生成的,是個空結構體
	pb.UnimplementedGreeterServer
}

// 業務代碼在此寫,客戶端遠程調用SayHello時,
// 會執行這里的代碼
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	// 打印請求參數
	log.Printf("Received: %v", in.GetName())
	// 實例化結構體HelloReply,作為返回值
	return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
	// 要監聽的協議和端口
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	// 實例化gRPC server結構體
	s := grpc.NewServer()

	// 服務注冊
	pb.RegisterGreeterServer(s, &server{})

	log.Println("開始監聽,等待遠程調用...")

	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
  1. 在server.go所在目錄執行go run server.go,控制台提示如下:
[golang@centos7 server]$ go run server.go 
2020/12/13 08:20:32 開始監聽,等待遠程調用...
  1. 此時gRPC的服務端已啟動,可以響應遠程調用,接下來開發反向代理(Reverse Proxy);

編寫反向代理(Reverse Proxy)代碼helloworld.gw.go並啟動

  • 接下來編反向代理(Reverse Proxy)代碼helloworld.gw.go
  • $GOPATH/src/helloworld目錄下新建文件夾gateway,在此文件夾下新建helloworld.gw.go,內容如下,有幾處要注意的地方稍后會說明:
package main

import (
	"flag"
	"fmt"
	"net/http"
	gw "helloworld"

	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

var (
	echoEndpoint = flag.String("echo_endpoint", "localhost:50051", "endpoint of YourService")
)

func run() error {

	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	mux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithInsecure()}
	err := gw.RegisterGreeterHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)

	if err != nil {
		return err
	}

	return http.ListenAndServe(":9090", mux)
}

func main() {
	if err := run(); err != nil {
		fmt.Print(err.Error())
	}
}
  1. 第一處要注意的地方,是調用http.ListenAndServe監聽9090端口,這是對外提供RESTful服務的端口;
  2. 第二處要注意的地方,是echoEndpoint配置了將外部RESTful請求轉發到server.go提供gRPC服務的入口處;
  3. 第三處要注意的地方,是調用了自動生成代碼中的RegisterGreeterHandlerFromEndpoint方法完成上下游調用的綁定;
  • hellowworld.gw.go所在目錄執行go run hellowworld.gw.go,開始監聽9090端口的web請求;

驗證

  1. 在本機上驗證,用curl發送請求:
curl \
-X POST \
-d '{"name": "will"}' \
192.168.133.203:9090/helloworld
  1. 收到響應如下,這是來自server.go的內容,可見http請求通過Reserve Proxy到達了真實的gRPC服務提供者,並順利返回給調用方:
{"message":"Hello will"}
  1. 去看server.go的日志如下:
[golang@centos7 server]$ go run server.go 
2020/12/19 14:16:47 開始監聽,等待遠程調用...
2020/12/19 14:24:35 Received: will
  1. 還可以在其他機器上通過postman驗證,記得關閉服務所在機器的防火牆,請求和響應如下,注意按數字順序設置和觀察:

在這里插入圖片描述

  • 至此,將gRPC服務快速暴露為RESTful服務的實戰就完成了,如果您正在做這方面的嘗試,希望本文能給您一些參考,接下來的文章咱們一起把swagger補全,讓開發和聯調更加高效;

你不孤單,欣宸原創一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 數據庫+中間件系列
  6. DevOps系列

歡迎關注公眾號:程序員欣宸

微信搜索「程序員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM