Program Life
  • Introduction
  • Catalog
  • Part I - Language
    • 目录
    • Golang
      • go 知识点合辑
      • go mod 简介
      • recover & const 简述
      • 关于 nil 的一些事情
      • slice 底层结构
    • JS
      • js 零基础起步
    • Python
      • python 基础
  • Part II - Network
    • 目录
    • TCP与UDP 对比
    • http2
      • http/2.0 and http/2.0 in Go
    • Grpc
      • gRPC 客户端连接语义与API
      • gRPC over http/2
      • gRPC 的 go 拦截器
  • Part III - Database
    • 目录
    • 常见 DB 基础细节
    • High Performance Mysql, 3th Edition - 笔记
    • mysql 中的索引类型
    • 批量写入造成mysql访问慢问题追踪
  • Part Ⅳ - Devops
    • 目录
    • Docker
      • Docker 基础使用指南
    • Kubernetes
      • K8S网络之网络框架
      • K8S网络之service间通信
      • K8S网络之集群外访问service的方式
    • IPVS 在 k8s 中连接保持引发的问题
    • Linux 常用指令
    • Linux 内存缓慢增长问题
    • Linux 性能领域大师布伦丹·格雷格的工具图谱
  • Part Ⅴ - Bigdata
    • 目录
    • Machine Learn
      • PCA原理推导
  • Part Ⅵ - Algorithm
    • 目录
    • 常用算法列表
    • 分布式一致性协议简介
    • ARC 算法简述
  • Part Ⅶ - Design
    • 目录
  • Part Ⅷ - Skill
    • 目录
    • 关于沟通、交流
    • Google 技能评分卡
    • 架构之重构的12条军规
    • 驾考指南
    • 杂项
    • RNote
      • 代码重构培训(笔记)
      • 登高四书(笔记)
      • 番茄工作法图解(笔记)
Powered by GitBook
On this page
  • Interceptor
  • grpc.stats
  • Appendix A

Was this helpful?

  1. Part II - Network
  2. Grpc

gRPC 的 go 拦截器

grpc interceptor with go

PreviousgRPC over http/2Next目录

Last updated 5 years ago

Was this helpful?

Interceptor

通过 ServerOption 给 grpc.Server 增加 interceptor 时有限制:Only one unary interceptor can be installed.。为了安装多个拦截器,我们需要一种可以将多个 interceptor 重新组合的方式。

目前常用的方式是:

        package : github.com/grpc-ecosystem/go-grpc-middleware:
            func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor
            func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor

使用示例见:

下边我们来看看它的实现:

        // Execution is done in left-to-right order, including passing of context.
        func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor {
            n := len(interceptors)

            if n > 1 {
                lastI := n - 1
                return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
                    var (
                        chainHandler grpc.UnaryHandler
                        curI         int
                    )

                    chainHandler = func(currentCtx context.Context, currentReq interface{}) (interface{}, error) {
                        if curI == lastI {
                            return handler(currentCtx, currentReq)
                        }
                        curI++
                        resp, err := interceptors[curI](currentCtx, currentReq, info, chainHandler)
                        curI--
                        return resp, err
                    }

                    return interceptors[0](ctx, req, info, chainHandler)
                }
            }

            if n == 1 {
                return interceptors[0]
            }

            // n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil.
            return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
                return handler(ctx, req)
            }
        }

其实,它的实现的本质就是:利用闭包来引用栈中数据达到延迟使用的效果

通过这种方式,我们就可以尽情的实现自己的各种 interceptor:UnaryServerInterceptorAccessLog、grpc_recovery.UnaryServerInterceptor(package: github.com/grpc-ecosystem/go-grpc-middleware/recovery) 等等。

grpc.stats

grpc.Server 可以通过安装 func WithStatsHandler(h stats.Handler) DialOption 生成的 ServerOption 实现 This package is for monitoring purpose only. All fields are read-only.

首先,我们看看 stats.Handler 的定义:

    type Handler interface {
        // TagRPC can attach some information to the given context.
        // The context used for the rest lifetime of the RPC will be derived from
        // the returned context.
        TagRPC(context.Context, *RPCTagInfo) context.Context
        // HandleRPC processes the RPC stats.
        HandleRPC(context.Context, RPCStats)

        // TagConn can attach some information to the given context.
        // The returned context will be used for stats handling.
        // For conn stats handling, the context used in HandleConn for this
        // connection will be derived from the context returned.
        // For RPC stats handling,
        //  - On server side, the context used in HandleRPC for all RPCs on this
        // connection will be derived from the context returned.
        //  - On client side, the context is not derived from the context returned.
        TagConn(context.Context, *ConnTagInfo) context.Context
        // HandleConn processes the Conn stats.
        HandleConn(context.Context, ConnStats)
    }

在 grpc 的方法调用过程中,RPCStats 在 stat.Begin、stat.ConnBegin、stat.ConnEnd、stat.End 几个状态之间转换。

其中 Handler.HandleConn 发生在 grpc 的方法调用之前,而 grpc方法调用后,会调用 Handler.HandleRPC (此时 RPCStats 为 stat.End)。由此,我们可以通过这种方式,达到对 grpc 方法 装饰的效果。如:github.com/openzipkin/zipkin-go 的实现,使用示例:

Appendix A

enceladus.wgrpc.server
gRPC server 的 go 实现片段