app开发定制公司gRPC教程 — grpc-gateway

教程 — grpc-gateway

前言

app开发定制公司本文使用的认证方式为 双向认证,app开发定制公司也可改为其他认证方式,可参考 进行改造,app开发定制公司本文不再赘述。

代码

一、Grpc网关介绍

1.1 原因

etcd3 APIapp开发定制公司全面升级为gRPC后,app开发定制公司同时还要提供REST API服务,app开发定制公司维护两个版本的服务就app开发定制公司显得不太合理,所以 诞生了。通过使用protobuf的自定义optionapp开发定制公司实现了一个网关,服务端可以同时开启Grpc服务和HTTP服务,HTTP服务负责接收客户端请求,然后将请求信息转换protobuf格式作为 Grpc 请求数据,再发送至Grpc服务,HTTP服务等从Grpc服务获取响应后再转为JSON数据返回给客户端。

1.2 补充

Grpc-Gateway是Protocol Buffers编译器协议的一个插件。它读取Protobuf服务定义并生成一个反向代理服务器,该服务器将RESTful HTTP API转换为gRPC。这种服务是根据google.api.http annotations注解生成的,所以在项目中会使用到该注解。

1.3 流程

请求流程,当客户端发送http请求时候,grpc-gateway接受请求,生成grpc的client去请求grpc的server端;grpc-gateway实际上做了反向代理的作用。因此会产生两个服务,一个是grpc-gateway产生的http服务,负责接受客户端的http请求,一个grpc的server服务,负责处理client端的请求。

1.4 流程图

二、环境配置

2.1 需要的依赖

2.1.1 proto转go

go get -u google.golang.org/protobuf/cmd/protoc-gen-go 
  • 1

2.1.2 grpc

go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc
  • 1

2.1.3 grpc-gateway

go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
  • 1

2.1.4 对客户端提供服务的API依赖

go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
  • 1

2.2 下载结果

三、代码

3.1 加入google/api/annotations.proto

在1.2中我们讲到需要 google/api/annotations.proto,所以还需要将这些文件到项目中,在前面环境配置完成后,去项目的 External Libraries 下 将grpc-gateway包中google包下的内容全部拷贝到项目的一个单独的目录下,注意是用 v1.16.0版本下的,v2.10.2下没有 。

图一

图二

3.2 在代码中的位置及整个项目结构

google 文件夹放到了 pbfiles 下:

3.3 创建protobuf文件

新建的 ProdGrpcGateWay.proto 在 pbfiles 目录下,与google是同级。有的 创建的是 Prod.proto ,其实都一样,但是在本项目中会出现一个小问题,等后面运行时可能会报:

{"code":12, "message":"method GetProdStock not implemented", "details":[]}
  • 1

出现这个问题的原因是在本项目中集成了 的代码,多个示例中可能会出现 protobuf 文件内容相同的情况,在实现 GetProdStock 方法时有可能实现的是别的示例接口中的,所以报这个错误。

解决办法
1、修改Prod.proto 里的方法名;(本文使用的是方法)
2、单独一个项目;

ProdGrpcGateWay.proto 内容:

syntax = "proto3"; //proto3的语法,不写会默认为proto2package services; //包名,通过protoc生成go文件时使用option go_package = "../service"; //添加生成go文件的路径//必须要导入import "google/api/annotations.proto";message GrpcGateWayRequest {    int32 goods_id = 1; //传入的商品id}message GrpcGateWayResponse {    int32 goods_stock =1; //商品库存}service GrpcGateWayService {    rpc GetGrpcGateWayStock (GrpcGateWayRequest) returns (GrpcGateWayResponse){        option (google.api.http) = {            get: "/v1/prod/{goods_id}" //注意这里的路径参数要和上面 GrpcGateWayRequest 中定义的保持一致        };    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3.4 将 ProdGrpcGateWay.proto 转成相关go文件

在 pbfiles目录下执行以下命令,注意不要写错!

3.4.1 生成 ProdGrpcGateWay.pb.goProdGrpcGateWay_grpc.pb.go 的命令:

protoc --go_out=. --go-grpc_out=. *.proto
  • 1

3.4.2 生成 grpc-gateway使用的 ProdGrpcGateWay.pb.gw.go 命令:

protoc --grpc-gateway_out=logtostderr=true:../service ProdGrpcGateWay.proto
  • 1

3.5 添加认证证书

将在 双向认证 示例中的相关证书复制一份过来,本文是复制到了 keys2 下,关于生成相关证书的讲解可看前言中的相关链接。

3.6 服务端代码

server.go

package mainimport (	"context"	"crypto/tls"	"crypto/x509"	"go-grpc/grpc-gateway/service"	"google.golang.org/grpc"	"google.golang.org/grpc/credentials"	"google.golang.org/grpc/grpclog"	"io/ioutil"	"log"	"net")const (	// Address gRPC服务地址	ServerAddress = "127.0.0.1:8888")//1、声明一个server,里面是未实现的字段type server struct {	service.UnimplementedGrpcGateWayServiceServer}//2、必须要实现在 ProdGrpcGateWay.proto 里声明的远程调用接口,否则客户端会报://rpc error: code = Unimplemented desc = method GetGrpcGateWayStock not implementefunc (s *server) GetGrpcGateWayStock(ctx context.Context, in *service.GrpcGateWayRequest) (*service.GrpcGateWayResponse, error) {	return &service.GrpcGateWayResponse{GoodsStock: in.GetGoodsId()}, nil}func main() {	//1、创建带ca证书验证的服务端	rpcServer := grpc.NewServer(grpc.Creds(GetServeCreds()))	//2、注册服务	service.RegisterGrpcGateWayServiceServer(rpcServer, &server{})	//3、设置传输协议和监听地址	listen, err := net.Listen("tcp", ServerAddress)	if err != nil {		log.Fatal("服务监听端口失败", err)	}	log.Println("Server listen on " + ServerAddress + " with TLS")	//4、启动服务	rpcServer.Serve(listen)}//加入服务端认证证书func GetServeCreds() credentials.TransportCredentials {	// TLS认证	//从证书相关文件中读取和解析信息,得到证书公钥、密钥对	cert, err := tls.LoadX509KeyPair("grpc-gateway/keys2/server.pem", "grpc-gateway/keys2/server.key")	if err != nil {		grpclog.Fatalf("Failed to find server credentials %v", err)	}	certPool := x509.NewCertPool() //初始化一个CertPool	ca, err := ioutil.ReadFile("grpc-gateway/keys2/ca.pem")	if err != nil {		grpclog.Fatalf("Failed to find root credentials %v", err)	}	certPool.AppendCertsFromPEM(ca) //解析传入的证书,解析成功会将其加到池子中	creds := credentials.NewTLS(&tls.Config{ //构建基于TLS的TransportCredentials选项		Certificates: []tls.Certificate{cert},        //服务端证书链,可以有多个		ClientAuth:   tls.RequireAndVerifyClientCert, //要求必须验证客户端证书		ClientCAs:    certPool,                       //设置根证书的集合,校验方式使用 ClientAuth 中设定的模式	})	return creds}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

3.7 客户端服务代码

clientServer.go

package mainimport (	"context"	"crypto/tls"	"crypto/x509"	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"	"go-grpc/grpc-gateway/service"	"google.golang.org/grpc"	"google.golang.org/grpc/credentials"	"google.golang.org/grpc/grpclog"	"io/ioutil"	"log"	"net/http")const (	// ServerAddress gRPC服务地址	ServerAddress = "127.0.0.1:8888"	//ClientAddress 是浏览器等发送http请求时的地址	ClientAddress = "127.0.0.1:8080")func main() {	ctx := context.Background()	ctx, cancelFunc := context.WithCancel(ctx)	defer cancelFunc()	//1、创建路由	mux := runtime.NewServeMux()	//2、加入认证证书	opt := []grpc.DialOption{grpc.WithTransportCredentials(GetClientCreds())}	//3、注册请求服务端的方法	err := service.RegisterGrpcGateWayServiceHandlerFromEndpoint(ctx, mux, ServerAddress, opt)	if err != nil {		log.Fatalf("cannot start grpc gateway: %v", err)	}	//4、启动并监听http请求	err = http.ListenAndServe(ClientAddress, mux)	if err != nil {		log.Fatalf("cannot listen and server: %v", err)	}	log.Println("ClientServer listen on " + ServerAddress + " with TLS")}//加入客户端认证证书func GetClientCreds() credentials.TransportCredentials {	// TLS连接	//从证书相关文件中读取和解析信息,得到证书公钥、密钥对	cert, err := tls.LoadX509KeyPair("grpc-gateway/keys2/client.pem", "grpc-gateway/keys2/client.key")	if err != nil {		grpclog.Fatalf("Failed to find client credentials %v", err)	}	certPool := x509.NewCertPool()	ca, err := ioutil.ReadFile("grpc-gateway/keys2/ca.pem")	if err != nil {		grpclog.Fatalf("Failed to find root credentials %v", err)	}	certPool.AppendCertsFromPEM(ca)	creds := credentials.NewTLS(&tls.Config{		Certificates: []tls.Certificate{cert}, //客户端证书		ServerName:   "ximu.info",           //注意这里的参数为配置文件中所允许的ServerName,也就是其中配置的DNS...		RootCAs:      certPool,	})	return creds}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

附: client.go(测试与服务端通信是否正常的客户端)

package mainimport (	"context"	"crypto/tls"	"crypto/x509"	"fmt"	"go-grpc/grpc-gateway/service"	"google.golang.org/grpc"	"google.golang.org/grpc/credentials"	"io/ioutil"	"log")const (	// Address gRPC服务地址	Address = "127.0.0.1:8888")func main() {	// 证书认证-双向认证	// 从证书相关文件中读取和解析信息,得到证书公钥、密钥对	cert, _ := tls.LoadX509KeyPair("grpc-gateway/keys2/client.pem", "grpc-gateway/keys2/client.key")	// 创建一个新的、空的 CertPool	certPool := x509.NewCertPool()	ca, _ := ioutil.ReadFile("grpc-gateway/keys2/ca.pem")	//注意这里只能解析pem类型的根证书,所以需要的是ca.pem	// 尝试解析所传入的 PEM 编码的证书。如果解析成功会将其加到 CertPool 中,便于后面的使用	certPool.AppendCertsFromPEM(ca)	// 构建基于 TLS 的 TransportCredentials 选项	creds := credentials.NewTLS(&tls.Config{		// 设置证书链,允许包含一个或多个		Certificates: []tls.Certificate{cert},		ServerName:   "ximu.info", //注意这里的参数为配置文件中所允许的ServerName,也就是其中配置的DNS...		RootCAs:      certPool,	})	//1、 建立连接	conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds))	if err != nil {		log.Fatalf("did not connect: %v", err)	}	defer conn.Close()	request := &service.GrpcGateWayRequest{		GoodsId:   123,	}	// 2. 调用 ProdGrpcGateWay_grpc.pb.go 中的 NewGrpcGateWayServiceClient 方法建立客户端	query := service.NewGrpcGateWayServiceClient(conn)	//3、调用rpc方法	res, err := query.GetGrpcGateWayStock(context.Background(), request)	if err != nil {		log.Fatal("调用gRPC方法错误: ", err)	}	fmt.Println("grpc-gateway:调用gRPC方法成功,GoodsStock = ", res)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

四、测试

4.1 启动服务

先启动 server.go,再启动 clientServer.go

4.2 访问clientServer服务的接口

4.3 测试结果

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发