- 前言
- 一、grpc-gateway原理
- 二、 环境准备
- 三、服务端改造
- 四、总结
在上一篇文章自签CA、服务端和客户端双向认证中,我们了解了双向认证并进行了实践,本篇文章将基于双向认证,使用gRPC-Gateway提供http请求处理的api,这样便于提供gRPC和RESTful风格的API。官方地址:https://github.com/grpc-ecosystem/grpc-gateway,本篇文章中的代码上传在gitee上:tonghua / grpc-gateway
一、grpc-gateway原理gRPC-Gateway是Protocol Buffers编译器协议的一个插件。它读取Protobuf服务定义并生成一个反向代理服务器,该服务器将RESTful HTTP API转换为gRPC。这种服务是根据google.api.http annotations
注解生成的。具体而言,当客户端发送http请求时候,grpc-gateway接受请求,生成grpc的client去请求grpc的server端;grpc-gateway实际上做了反向代理的作用。因此会产生两个服务,一个是grpc-gateway产生的http服务,负责接受客户端的http请求,一个grpc的server服务,负责处理client端的请求。
文档地址:https://grpc-ecosystem.github.io/grpc-gateway/,可以简单理解grpc-gateway就是在grpc的基础之上加一层代理并转发,转变成protobuf格式来访问grpc服务。
新版本的中对环境的一些依赖做了调整,因此我们需要安装下面的依赖:
go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
go get -u google.golang.org/protobuf/cmd/protoc-gen-go
go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc
安装完成之后在自己的GOPATH下能找到下面几个可执行文件:
接下来为了使用google/api/annotations.proto
注解中的内容,我们在External Libraries将grpc-gateway包中google包下的内容全部拷贝到项目的目录下,同时最好打开几个Proto文件看看包导入的问题,发现爆红直接删除掉import,ProtoBuf插件能进行自动的包导入:
为了便于后面的描述,我们可以看下目录结构:
然后我们对Prod.proto文件进行改写,使其中的ProdService能够提供http的服务。
syntax = "proto3"; //proto3的语法,不写会默认为proto2
package services; //包名,通过protoc生成go文件时使用
option go_package = "../services"; //添加生成go文件的路径
import "google/api/annotations.proto";
message ProdRequest {
int32 prod_id = 1; //传入的商品id
}
message ProdResponse {
int32 prod_stock =1; //商品库存
}
service ProdService {
rpc GetProdStock (ProdRequest) returns (ProdResponse){
option (google.api.http) = {
get: "/v1/prod/{prod_id}" //注意这里的路径参数要和上面ProdRequest中定义的保持一致
};
}
}
再利用下面的两个命令生成相关的go文件,执行下面命令时需要在Prod.proto的目录下执行。生成的go文件都在services目录下:
protoc --go_out=plugins=grpc:../services Prod.proto //生成Prod.pb.go
protoc --grpc-gateway_out=logtostderr=true:../services Prod.proto //生成Prod.pb.gw.go
然后将客户端的证书和密钥拷贝到服务端的keys目录下
改写服务端的server.go代码,抽取其中的证书认证代码到helper/helper.go中,同时将客户端的证书认证代码也放到这儿:
helper.go:
package helper
import (
"crypto/tls"
"crypto/x509"
"google.golang.org/grpc/credentials"
"io/ioutil"
)
func GetServeCreds() credentials.TransportCredentials {
// TLS认证
//从证书相关文件中读取和解析信息,得到证书公钥、密钥对
cert, _ := tls.LoadX509KeyPair("keys/server.pem", "keys/server.key")
certPool := x509.NewCertPool() //初始化一个CertPool
ca, _ := ioutil.ReadFile("keys/ca.pem")
certPool.AppendCertsFromPEM(ca) //解析传入的证书,解析成功会将其加到池子中
creds := credentials.NewTLS(&tls.Config{ //构建基于TLS的TransportCredentials选项
Certificates: []tls.Certificate{cert}, //服务端证书链,可以有多个
ClientAuth: tls.RequireAndVerifyClientCert, //要求必须验证客户端证书
ClientCAs: certPool, //设置根证书的集合,校验方式使用 ClientAuth 中设定的模式
})
return creds
}
func GetClientCreds() credentials.TransportCredentials{
// TLS连接
//从证书相关文件中读取和解析信息,得到证书公钥、密钥对
cert, _ := tls.LoadX509KeyPair("keys/client.pem", "keys/client.key")
certPool := x509.NewCertPool()
ca, _ := ioutil.ReadFile("keys/ca.pem")
certPool.AppendCertsFromPEM(ca)
creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{cert}, //客户端证书
ServerName: "www.p-pp.cn", //注意这里的参数为配置文件中所允许的ServerName,也就是其中配置的DNS...
RootCAs: certPool,
})
return creds
}
servier.go
package main
import (
"google.golang.org/grpc"
"grpcpro/helper"
"grpcpro/services"
"log"
"net"
)
const (
// Address gRPC服务地址
Address = "127.0.0.1:8888"
)
func main() {
rpcServer := grpc.NewServer(grpc.Creds(helper.GetServeCreds())) //实例化grpc Server
//创建带ca证书验证的服务端
services.RegisterProdServiceServer(rpcServer,new(services.ProdService))
listen, _ := net.Listen("tcp",Address) //设置传输协议和监听地址
log.Println("Listen on " + Address + " with TLS")
rpcServer.Serve(listen)
}
接下来我们编写httpServer.go
用于接收客户端的http请求和转发代理该请求:
package main
import (
"context"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"grpcpro/helper"
"grpcpro/services"
"log"
"net/http"
)
func main() {
gwmux := runtime.NewServeMux() //创建路由
opt := []grpc.DialOption{grpc.WithTransportCredentials(helper.GetClientCreds())}
err := services.RegisterProdServiceHandlerFromEndpoint(context.Background(),gwmux,"127.0.0.1:8888",opt) //第二个参数为gRPC服务的地址
if err != nil {
log.Fatal(err)
}
httpServer := &http.Server{
Addr: "127.0.0.1:8080", //客户端请求的http地址
Handler: gwmux,
}
httpServer.ListenAndServe()
}
最后,先启动server.go,再启动httpServer.go,访问http://localhost:8080/v1/prod/21```即可得到gRPC提供服务的结果:
本篇文章是在上一篇文章的双向认证的基础之上完成的,通过protobuf的自定义option实现了一个网关,在服务端同时开启gRPC和HTTP服务,HTTP服务接收客户端请求后转换为grpc请求数据,获取响应后转为json数据返回给客户端。
提示:现在go相关的版本迭代更新比较快,视频教程或一些博文都是老版本的,给我们这种初学者带来负担,但也要不停尝试,依赖这种东西尽量以官方的为准,多看看官方文档,不停试坑。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)