Go gRPC进阶-go-grpc-middleware使用(八)
前言
上篇介绍了gRPC中TLS认证和自定义方法认证,最后还简单介绍了gRPC拦截器的使用。gRPC自身只能设置一个拦截器,所有逻辑都写一起会比较乱。本篇简单介绍go-grpc-middleware的使用,包括grpc_zap、grpc_auth和grpc_recovery。
go-grpc-middleware简介
go-grpc-middleware封装了认证(auth), 日志( logging), 消息(message), 验证(validation), 重试(retries) 和监控(retries)等拦截器。
- 安装
go get github.com/grpc-ecosystem/go-grpc-middleware - 使用
import "github.com/grpc-ecosystem/go-grpc-middleware"
myServer := grpc.NewServer(
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpc_ctxtags.StreamServerInterceptor(),
grpc_opentracing.StreamServerInterceptor(),
grpc_prometheus.StreamServerInterceptor,
grpc_zap.StreamServerInterceptor(zapLogger),
grpc_auth.StreamServerInterceptor(myAuthFunction),
grpc_recovery.StreamServerInterceptor(),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_ctxtags.UnaryServerInterceptor(),
grpc_opentracing.UnaryServerInterceptor(),
grpc_prometheus.UnaryServerInterceptor,
grpc_zap.UnaryServerInterceptor(zapLogger),
grpc_auth.UnaryServerInterceptor(myAuthFunction),
grpc_recovery.UnaryServerInterceptor(),
)),
)
grpc.StreamInterceptor中添加流式RPC的拦截器。
grpc.UnaryInterceptor中添加简单RPC的拦截器。
grpc_zap日志记录
1.创建zap.Logger实例
func ZapInterceptor() *zap.Logger {
logger, err := zap.NewDevelopment()
if err != nil {
log.Fatalf("failed to initialize zap logger: %v", err)
}
grpc_zap.ReplaceGrpcLogger(logger)
return logger
}
2.把zap拦截器添加到服务端
grpcServer := grpc.NewServer(
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpc_zap.StreamServerInterceptor(zap.ZapInterceptor()),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_zap.UnaryServerInterceptor(zap.ZapInterceptor()),
)),
)
3.日志分析

各个字段代表的意思如下:
{
"level": "info", // string zap log levels
"msg": "finished unary call", // string log message
"grpc.code": "OK", // string grpc status code
"grpc.method": "Ping", / string method name
"grpc.service": "mwitkow.testproto.TestService", // string full name of the called service
"grpc.start_time": "2006-01-02T15:04:05Z07:00", // string RFC3339 representation of the start time
"grpc.request.deadline": "2006-01-02T15:04:05Z07:00", // string RFC3339 deadline of the current request if supplied
"grpc.request.value": "something", // string value on the request
"grpc.time_ms": 1.345, // float32 run time of the call in ms
"peer.address": {
"IP": "127.0.0.1", // string IP address of calling party
"Port": 60216, // int port call is coming in on
"Zone": "" // string peer zone for caller
},
"span.kind": "server", // string client | server
"system": "grpc", // string
"custom_field": "custom_value", // string user defined field
"custom_tags.int": 1337, // int user defined tag on the ctx
"custom_tags.string": "something" // string user defined tag on the ctx
}
4.把日志写到文件中
上面日志是在控制台输出的,现在我们把日志写到文件中,修改ZapInterceptor方法。
import (
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
// ZapInterceptor 返回zap.logger实例(把日志写到文件中)
func ZapInterceptor() *zap.Logger {
w := zapcore.AddSync(&lumberjack.Logger{
Filename: "log/debug.log",
MaxSize: 1024, //MB
LocalTime: true,
})
config := zap.NewProductionEncoderConfig()
config.EncodeTime = zapcore.ISO8601TimeEncoder
core := zapcore.NewCore(
zapcore.NewJSONEncoder(config),
w,
zap.NewAtomicLevel(),
)
logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
grpc_zap.ReplaceGrpcLogger(logger)
return logger
}
grpc_auth认证
go-grpc-middleware中的grpc_auth默认使用authorization认证方式,以authorization为头部,包括basic, bearer形式等。下面介绍bearer token认证。bearer允许使用access key(如JSON Web Token (JWT))进行访问。
1.新建grpc_auth服务端拦截器
// TokenInfo 用户信息
type TokenInfo struct {
ID string
Roles []string
}
// AuthInterceptor 认证拦截器,对以authorization为头部,形式为`bearer token`的Token进行验证
func AuthInterceptor(ctx context.Context) (context.Context, error) {
token, err := grpc_auth.AuthFromMD(ctx, "bearer")
if err != nil {
return nil, err
}
tokenInfo, err := parseToken(token)
if err != nil {
return nil, grpc.Errorf(codes.Unauthenticated, " %v", err)
}
//使用context.WithValue添加了值后,可以用Value(key)方法获取值
newCtx := context.WithValue(ctx, tokenInfo.ID, tokenInfo)
//log.Println(newCtx.Value(tokenInfo.ID))
return newCtx, nil
}
//解析token,并进行验证
func parseToken(token string) (TokenInfo, error) {
var tokenInfo TokenInfo
if token == "grpc.auth.token" {
tokenInfo.ID = "1"
tokenInfo.Roles = []string{"admin"}
return tokenInfo, nil
}
return tokenInfo, errors.New("Token无效: bearer " + token)
}
//从token中获取用户唯一标识
func userClaimFromToken(tokenInfo TokenInfo) string {
return tokenInfo.ID
}
代码中的对token进行简单验证并返回模拟数据。
2.客户端请求添加bearer token
实现和上篇的自定义认证方法大同小异。gRPC 中默认定义了 PerRPCCredentials,是提供用于自定义认证的接口,它的作用是将所需的安全认证信息添加到每个RPC方法的上下文中。其包含 2 个方法:
GetRequestMetadata:获取当前请求认证所需的元数据RequireTransportSecurity:是否需要基于 TLS 认证进行安全传输
接下来我们实现这两个方法
// Token token认证
type Token struct {
Value string
}
const headerAuthorize string = "authorization"
// GetRequestMetadata 获取当前请求认证所需的元数据
func (t *Token) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{headerAuthorize: t.Value}, nil
}
// RequireTransportSecurity 是否需要基于 TLS 认证进行安全传输
func (t *Token) RequireTransportSecurity() bool {
return true
}
注意:这里要以
authorization为头部,和服务端对应。
发送请求时添加token
//从输入的证书文件中为客户端构造TLS凭证
creds, err := credentials.NewClientTLSFromFile("../tls/server.pem", "go-grpc-example")
if err != nil {
log.Fatalf("Failed to create TLS credentials %v", err)
}
//构建Token
token := auth.Token{
Value: "bearer grpc.auth.token",
}
// 连接服务器
conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(&token))
注意:Token中的Value的形式要以
bearer token值形式。因为我们服务端使用了bearer token验证方式。
3.把grpc_auth拦截器添加到服务端
grpcServer := grpc.NewServer(cred.TLSInterceptor(),
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpc_auth.StreamServerInterceptor(auth.AuthInterceptor),
grpc_zap.StreamServerInterceptor(zap.ZapInterceptor()),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_auth.UnaryServerInterceptor(auth.AuthInterceptor),
grpc_zap.UnaryServerInterceptor(zap.ZapInterceptor()),
)),
)
写到这里,服务端都会拦截请求并进行bearer token验证,使用bearer token是规范了与HTTP请求的对接,毕竟gRPC也可以同时支持HTTP请求。
grpc_recovery恢复
把gRPC中的panic转成error,从而恢复程序。
1.直接把grpc_recovery拦截器添加到服务端
最简单使用方式
grpcServer := grpc.NewServer(cred.TLSInterceptor(),
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpc_auth.StreamServerInterceptor(auth.AuthInterceptor),
grpc_zap.StreamServerInterceptor(zap.ZapInterceptor()),
grpc_recovery.StreamServerInterceptor,
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_auth.UnaryServerInterceptor(auth.AuthInterceptor),
grpc_zap.UnaryServerInterceptor(zap.ZapInterceptor()),
grpc_recovery.UnaryServerInterceptor(),
)),
)
2.自定义错误返回
当panic时候,自定义错误码并返回。
// RecoveryInterceptor panic时返回Unknown错误吗
func RecoveryInterceptor() grpc_recovery.Option {
return grpc_recovery.WithRecoveryHandler(func(p interface{}) (err error) {
return grpc.Errorf(codes.Unknown, "panic triggered: %v", p)
})
}
添加grpc_recovery拦截器到服务端
grpcServer := grpc.NewServer(cred.TLSInterceptor(),
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpc_auth.StreamServerInterceptor(auth.AuthInterceptor),
grpc_zap.StreamServerInterceptor(zap.ZapInterceptor()),
grpc_recovery.StreamServerInterceptor(recovery.RecoveryInterceptor()),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_auth.UnaryServerInterceptor(auth.AuthInterceptor),
grpc_zap.UnaryServerInterceptor(zap.ZapInterceptor()),
grpc_recovery.UnaryServerInterceptor(recovery.RecoveryInterceptor()),
)),
)
总结
本篇介绍了go-grpc-middleware中的grpc_zap、grpc_auth和grpc_recovery拦截器的使用。go-grpc-middleware中其他拦截器可参考GitHub学习使用。
教程源码地址:https://github.com/Bingjian-Zhu/go-grpc-example
Go gRPC进阶-go-grpc-middleware使用(八)的更多相关文章
- Go gRPC进阶-TLS认证+自定义方法认证(七)
前言 前面篇章的gRPC都是明文传输的,容易被篡改数据.本章将介绍如何为gRPC添加安全机制,包括TLS证书认证和Token认证. TLS证书认证 什么是TLS TLS(Transport Layer ...
- protobuffer、gRPC、restful gRPC的相互转化
转自:https://studygolang.com/articles/12510 文档 grpc中文文档 grpc-gateway,restful和grpc转换库 protobuf 官网 proto ...
- [gRPC via C#] gRPC本质的探究与实践
鉴于内容过多,先上太长不看版: grpc 就是请求流&响应流特殊一点的 Http 请求,性能和 WebAPI 比起来只快在 Protobuf 上: 附上完整试验代码:GrpcWithOutSD ...
- 我的Go gRPC之旅、01 初识gRPC,感受gRPC的强大魅力
微服务架构 微服务是一种开发软件的架构和组织方法,其中软件由通过明确定义的API 进行通信的小型独立服务组成. 这些服务由各个小型独立团队负责. 微服务架构使应用程序更易于扩展和更快地开发,从而加速创 ...
- Go gRPC进阶-超时设置(六)
前言 gRPC默认的请求的超时时间是很长的,当你没有设置请求超时时间时,所有在运行的请求都占用大量资源且可能运行很长的时间,导致服务资源损耗过高,使得后来的请求响应过慢,甚至会引起整个进程崩溃. 为了 ...
- Go gRPC进阶-proto数据验证(九)
前言 上篇介绍了go-grpc-middleware的grpc_zap.grpc_auth和grpc_recovery使用,本篇将介绍grpc_validator,它可以对gRPC数据的输入和输出进行 ...
- Go gRPC进阶-gRPC转换HTTP(十)
前言 我们通常把RPC用作内部通信,而使用Restful Api进行外部通信.为了避免写两套应用,我们使用grpc-gateway把gRPC转成HTTP.服务接收到HTTP请求后,grpc-gatew ...
- Golang gRPC学习(03): grpc官方示例程序route_guide简析
代码主要来源于grpc的官方examples代码: route_guide https://github.com/grpc/grpc-go/tree/master/examples/route_gui ...
- Python 爬虫从入门到进阶之路(十八)
在之前的文章我们通过 scrapy 框架 及 scrapy.Spider 类做了一个<糗事百科>的糗百爬虫,本章我们再来看一下相较于 scrapy.Spider 类更为强大的 CrawlS ...
随机推荐
- [概率] HDU 2019 Multi-University Training Contest 10 - Valentine's Day
Valentine's Day Time Limit: 2000/2000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others ...
- 【前端词典】这些功能其实不需要 JS,CSS 就能搞定
前言 今天我们大家介绍一些你可能乍一眼以为一定需要 JavaScript 才能完成的功能,其实 CSS 就能完成,甚至更加简单. 内容已经发布在 gitHub 了,欢迎围观 Star,更多文章都在 g ...
- 浅尝Go语言GC
大家好,我是小栈君,因为个人和工作的缘故,所以拖更了一点时间,但是关于拖更的内容小栈君会在后续的时间中补回来,还希望大家继续支持和关注小栈君.当然,在国内疫情稍微减缓的情况下,小栈君在这里也多说两句, ...
- 《深入理解 Java 虚拟机》读书笔记:Java 内存模型与线程
正文 由于计算机的处理器运算速度与它的存储和通信子系统速度的差距太大了,大量的时间都花费在磁盘 I/O.网络通信或者数据库访问上,导致处理器在大部分时间里都处于等待其他资源的状态.因此,为了充分利用计 ...
- 开源APP
仿微信 https://github.com/zhengwenming/WeChat 电台韵律 https://github.com/DaMingShen 运动App https://github.c ...
- Spring中应用的那些设计模式
设计模式作为工作学习中的枕边书,却时常处于勤说不用的尴尬境地,也不是我们时常忘记,只是一直没有记忆. 今天,我们就设计模式的内在价值做一番探讨,并以spring为例进行讲解,只有领略了其设计的思想理念 ...
- SSH免密配置
1. 在三个节点生成秘钥cd ~/.sshssh-keygen -t rsa 2.合并三个节点的密码cat id_rsa.pub >> authorized_keysssh postgre ...
- 一文彻底读懂MySQL事务的四大隔离级别
前言 之前分析一个死锁问题,发现自己对数据库隔离级别理解还不够清楚,所以趁着这几天假期,整理一下MySQL事务的四大隔离级别相关知识,希望对大家有帮助~ 事务 什么是事务? 事务,由一个有限的数据库操 ...
- prometheus+grafana实现监控过程的整体流程
prometheus安装较为简单,下面会省略安装步骤: 一.服务器启动 Prometheus启动 ./prometheus --config.file=prometheus.yml Grafana启动 ...
- 通过pycharm使用git和github的步骤(图文详解)
一.在Pycharm工具中配置集成Git和GitHub.1.集成Git. 打开Pycharm, 点击File-->Settins-->Version Control-->Git 然 ...