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. 编译、运行、验证;

提前说明文件和目录

  • 本次实战在<font color="blue">$GOPATH/src</font>目录下新增文件夹<font color="red">helloworld</font>,里面总共有以下内容:
[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
  • 准备工作完成,接下来正式开始开发;

前提条件

  • 本文的所有操作都没有用到<font color="blue">root</font>账号,而是前文创建的<font color="red">golang</font>账号;
  • 请参照以下两篇文章将GO环境和gRPC环境搭建好:
  1. 在CentOS7部署和设置GO
  2. GO的gRPC开发环境准备

极速搭建gRPC-Gateway环境

  • 所谓的<font color="blue">搭建gRPC-Gateway环境</font>,其实是完成以下三件事:
在这里插入图片描述
  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. 进入<font color="blue">$GOPATH/bin</font>目录,可见新增两个文件<font color="red">protoc-gen-grpc-gateway</font>和<font color="blue">protoc-gen-swagger</font>:
[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文件

  • 在<font color="blue">$GOPATH/src</font>目录下,新建文件夹<font color="blue">helloworld</font>,里面新建文件<font color="red">helloworld.proto</font>,内容如下,有几处要注意的地方稍后会说:
// 协议类型
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. 增加的第一处,是用<font color="blue">import</font>关键词导入<font color="red">google/api/annotations.proto</font>;
  3. 增加的第二处,是<font color="blue">SayHello</font>方法的声明处,增加了<font color="red">option</font>配置,作用是配置<font color="blue">SayHello</font>方法对外暴露的RESTful接口的信息;
  4. 在使用<font color="blue">protoc-gen-grpc-gateway</font>的时候,上述两处配置会被识别到并生成对应的代码;

根据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. 执行完成后会在当前目录生成<font color="blue">helloworld.pb.go</font>文件;
  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. 执行完成后会在当前目录生成<font color="blue">helloworld.pb.gw.go</font>文件;
  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. 执行完成后会在当前目录生成<font color="blue">helloworld.swagger.json</font>文件;
  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相关的开发和验证,因此生成的<font color="blue">helloworld.swagger.json</font>文件本篇用不上,留待下一篇文章使用;

编写服务端代码server.go并启动

  1. 接下来编写服务端代码server.go,这个和《初试GO版gRPC开发》中的server.go内容一样;
  2. 在<font color="blue">$GOPATH/src/helloworld</font>目录下新建文件夹<font color="red">server</font>,在此文件夹下新建<font color="red">server.go</font>,内容如下,已经添加详细注释:
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所在目录执行<font color="blue">go run server.go</font>,控制台提示如下:
[golang@centos7 server]$ go run server.go 
2020/12/13 08:20:32 开始监听,等待远程调用...
  1. 此时gRPC的服务端已启动,可以响应远程调用,接下来开发反向代理(Reverse Proxy);

编写反向代理(Reverse Proxy)代码helloworld.gw.go并启动

  • 接下来编反向代理(Reverse Proxy)代码<font color="blue">helloworld.gw.go</font>;
  • 在<font color="blue">$GOPATH/src/helloworld</font>目录下新建文件夹<font color="red">gateway</font>,在此文件夹下新建<font color="red">helloworld.gw.go</font>,内容如下,有几处要注意的地方稍后会说明:
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. 第一处要注意的地方,是调用<font color="blue">http.ListenAndServe</font>监听9090<font color="blue">端口</font>,这是对外提供RESTful服务的端口;
  2. 第二处要注意的地方,是<font color="blue">echoEndpoint</font>配置了将外部RESTful请求转发到server.go提供gRPC服务的入口处;
  3. 第三处要注意的地方,是调用了自动生成代码中的RegisterGreeterHandlerFromEndpoint方法完成上下游调用的绑定;
  • 在<font color="blue">hellowworld.gw.go</font>所在目录执行<font color="blue">go run hellowworld.gw.go</font>,开始监听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. 还可以在其他机器上通过<font color="blue">postman</font>验证,记得关闭服务所在机器的防火墙,请求和响应如下,注意按数字顺序设置和观察:
在这里插入图片描述
  • 至此,将gRPC服务快速暴露为RESTful服务的实战就完成了,如果您正在做这方面的尝试,希望本文能给您一些参考,接下来的文章咱们一起把swagger补全,让开发和联调更加高效;

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

欢迎关注公众号:程序员欣宸

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容