脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Go gRPC服务进阶middleware使用教程

Go gRPC服务进阶middleware使用教程

2022-10-21 11:54烟花易冷人憔悴 Golang

这篇文章主要为大家介绍了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)等拦截器。

安装

?
1
go get github.com/grpc-ecosystem/go-grpc-middleware

使用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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实例

?
1
2
3
4
5
6
7
8
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拦截器添加到服务端

?
1
2
3
4
5
6
7
8
grpcServer := grpc.NewServer(
    grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
            grpc_zap.StreamServerInterceptor(zap.ZapInterceptor()),
        )),
        grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
            grpc_zap.UnaryServerInterceptor(zap.ZapInterceptor()),
        )),
    )

3.日志分析

Go gRPC服务进阶middleware使用教程

各个字段代表的意思如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
      "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方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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服务端拦截器

?
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
// 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 认证进行安全传输

接下来我们实现这两个方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
// 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

?
1
2
3
4
5
6
7
8
9
10
11
//从输入的证书文件中为客户端构造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拦截器添加到服务端

?
1
2
3
4
5
6
7
8
9
10
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拦截器添加到服务端

最简单使用方式

?
1
2
3
4
5
6
7
8
9
10
11
12
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时候,自定义错误码并返回。

?
1
2
3
4
5
6
// 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拦截器到服务端

?
1
2
3
4
5
6
7
8
9
10
11
12
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使用教程的详细内容,更多关于Go gRPC服务middleware的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/FireworksEasyCool/p/12750339.html

延伸 · 阅读

精彩推荐
  • Golanggolang interface判断为空nil的实现代码

    golang interface判断为空nil的实现代码

    这篇文章主要介绍了golang interface判断为空nil的实现代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    翔云1234565452021-05-27
  • GolangGo 使用 xorm 操作 MySQL

    Go 使用 xorm 操作 MySQL

    本文介绍了 golang orm 库 xorm 的使用和项目结构。...

    马哥Linux运维11132021-10-28
  • Golang适合PHP同学的GoFrame框架使用体验及学习建议

    适合PHP同学的GoFrame框架使用体验及学习建议

    这篇文章主要为大家介绍了非常适合PHP同学使用的GoFrame框架设计思想使用体验及学习建议介绍,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家...

    王中阳Go3292022-10-18
  • Golanggolang NewRequest/gorequest实现http请求的示例代码

    golang NewRequest/gorequest实现http请求的示例代码

    本文主要介绍了golang NewRequest/gorequest实现http请求的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的...

    我的猫叫土豆6412022-08-08
  • Golanggolang select 机制和超时问题

    golang select 机制和超时问题

    golang 中的协程使用非常方便,但是协程什么时候结束是一个控制问题,可以用 select 配合使用,这篇文章主要介绍了golang select 机制和超时问题,需要的朋友...

    拾荒志6372022-06-26
  • Golang在Linux系统中安装Go语言的详细教程

    在Linux系统中安装Go语言的详细教程

    这篇文章主要介绍了在Linux系统中安装Go语言的详细教程,由于国内很多人对谷歌的盲目追捧,导致Go语言在国内的人气远超国外...需要的朋友可以参考下 ...

    Golang教程网3352020-04-26
  • Golanggo语言数据结构之前缀树Trie

    go语言数据结构之前缀树Trie

    这篇文章主要介绍了go语言数据结构之前缀树Trie,文章围绕主题展开详细内容介绍,具有一定得参考价值,需要的小伙伴可以参考一下...

    呆呆灿4772022-10-09
  • Golanggolang解析yaml文件操作

    golang解析yaml文件操作

    这篇文章主要介绍了golang解析yaml文件操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    思维的深度16372021-03-20