Go gRPC服务进阶middleware使用教程
来源:脚本之家
时间:2022-12-29 15:58:24 212浏览 收藏
亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《Go gRPC服务进阶middleware使用教程》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下教程、gRPC、middleware,希望所有认真读完的童鞋们,都有实质性的提高。
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服务进阶middleware使用教程》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布Golang相关知识,快来关注吧!
-
206 收藏
-
336 收藏
-
282 收藏
-
213 收藏
-
135 收藏
-
165 收藏
-
473 收藏
-
377 收藏
-
384 收藏
-
246 收藏
-
110 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习