├── .gitignore ├── LICENSE ├── README.md ├── client ├── client.go └── options.go ├── helm └── README.md ├── internal └── common │ ├── etcd.go │ └── opentracing.go ├── plugins ├── middleware │ ├── chain.go │ ├── errorformat │ │ └── README.md │ ├── hystrixlimitter │ │ ├── README.md │ │ ├── hystrix.go │ │ ├── hystrixlimitter.go │ │ ├── limitter.go │ │ └── options.go │ ├── logger │ │ ├── grpclog.go │ │ ├── logger.go │ │ └── options.go │ ├── middleware.go │ ├── opentracing │ │ ├── opentracing.go │ │ └── options.go │ ├── prometheus │ │ ├── README.md │ │ ├── options.go │ │ └── prometheus.go │ ├── tpl │ │ ├── options.go │ │ └── tpl.go │ └── wrappers.go └── register │ ├── etcdv3 │ └── etcdv3.go │ ├── options.go │ └── plugins.go └── server ├── options.go └── server.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 micro-kit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于grpc技术,开箱即用的微服务框架 2 | 3 | 基于grpc技术,开箱即用的微服务框架。目标做成一个符合云原生的跑在grpc上的微服务快速创建框架。 4 | 5 | ## 简介 6 | 7 | 在使用过go-kit和go-micro这两个go语言里毕竟出众的框架后,两个框架都有使用不爽的地方。 8 | 9 | ### go-kit 10 | go-kit目标是成为一个微服务的工具链,但本身不提供具体实现方式,它支持的通讯方式很多,但是多带来一个问题就是通用性问题,通用意味着兼容和丢失对某种通讯协议的友好度。 11 | 比方说在用go-kit开发grpc微服务时,你需要编写3层代码,分别是transport、endpoint和service,目的是很好的,就是我们的transport层用http或者grpc或者其它任何接入方式都不影响service层代码编写,但是他却忽略了很重要一个现实情况,就是一个公司或一个团队要是有一种通讯协议时,它不会说短时间内切换为另一种协议,不希望因为要兼容可能永远都使用不到的协议而在代码编写上浪费更多时间,本人在开发中就遇到了频繁的编写类似代码,最后编写了工具代替手工编写。总结就是是一个很好的工具库,单需要在使用时做很多东西,且代码3层耗费开发周期。 12 | 13 | ### go-micro 14 | 使用go-micro是在入职一家新公司时开始的,这个框架可以说是go语言里最简单的微服务框架,使用简单可以快速编写代码,基本不存在go-kit存在的问题,但是它存在的问题是它做的太多了,编写了一个grpc的proto解析工具,相当于改变了grpc本身标准接口,改成了它自己的接口形式,最主要原因也就是便于插入中间件,我本人不希望改变原生grpc使用方式。第二个不友好的地方还是它做的太多了,在我编写go标准单元测试时失败了,因为它本身代码中使用了和go test冲突的命令行参数,导致我无法使用单元测试!总结就是做的太多版本发布也很块,它代码中可能存在bug你需要升级,这个时候你可能发现不是把依赖升级就可以完成的,需要改代码,当服务一多这个过程是很痛苦的。 15 | 16 | ### microkit 17 | 也就是本项目,为了解决以上问题让微服务开发更加清爽,造一个简单的轮子,让开发更快。 18 | 1. 只使用grpc作为服务transport,所以不需要像go-kit实现3层代码,以原生grpc的方式开发代码 19 | 2. 提供统一项目目录和模板项目,并提供一个cli工具用于创建项目。 20 | 3. 实现常用grpc中间件,使使用本框架的项目可以从最开始就支持日志中间件、链路追踪、限流熔断、性能监控等中间件。 21 | 4. 微服务应尽量少的读取本地配置,所以本项目配置从环境变量读取一个服务必须的参数,其它全从配置中心获取。 22 | 5. 提供统一的客户端调用库的编写方式,加载中间件 23 | 6. 命令行工具要可以生成helm项目配置,已方便项目符合与原生可以方便部署到k8s等云平台 24 | 25 | 26 | ## 参考 27 | 1. grpc中间件 [https://github.com/grpc-ecosystem/go-grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware) 28 | 2. grpc普罗米修斯中间件 [github.com/grpc-ecosystem/go-grpc-prometheus](github.com/grpc-ecosystem/go-grpc-prometheus) 29 | 3. grpc中间件示例 https://github.com/g4zhuj/grpc-wrapper 30 | 4. 其它文章 https://segmentfault.com/a/1190000014546372 https://segmentfault.com/a/1190000012439580 https://segmentfault.com/a/1190000015347065 31 | 32 | 33 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | 7 | "github.com/micro-kit/micro-common/logger" 8 | "github.com/micro-kit/microkit/internal/common" 9 | "github.com/micro-kit/microkit/plugins/middleware" 10 | "github.com/micro-kit/microkit/plugins/middleware/hystrixlimitter" 11 | zap "github.com/micro-kit/microkit/plugins/middleware/logger" 12 | "github.com/micro-kit/microkit/plugins/middleware/opentracing" 13 | "github.com/micro-kit/microkit/plugins/middleware/prometheus" 14 | "google.golang.org/grpc" 15 | "google.golang.org/grpc/resolver" 16 | ) 17 | 18 | /* 19 | 客户端 20 | 用于创建客户端对象,传入基本配置即可创建一个客户端 21 | */ 22 | 23 | // Client 客户端对象 24 | type Client struct { 25 | opts *Options 26 | conn *grpc.ClientConn // grpc 连接 27 | tracerCloser io.Closer // 链路追踪关闭 28 | } 29 | 30 | // NewDefaultClient 创建默认客户端对象 31 | func NewDefaultClient(opts ...Option) (*Client, error) { 32 | c := &Client{ 33 | opts: new(Options), 34 | } 35 | // 配置 36 | configure(c, opts...) 37 | // 服务名检查 38 | if c.opts.serviceName == "" { 39 | return nil, errors.New("Service name must be set") 40 | } 41 | 42 | // 存储默认选项 43 | middlewares := make([]middleware.Middleware, 0) 44 | // 日志中间件 45 | middlewares = append(middlewares, zap.NewZapLogger(nil, zap.Logger(logger.Logger))) 46 | // 链路追踪中间件 47 | tracer, closer, err := common.NewJaegerTracer(c.opts.serviceName) 48 | if err != nil { 49 | return nil, err 50 | } 51 | c.tracerCloser = closer // 保存链路追踪关闭对象 52 | middlewares = append(middlewares, opentracing.NewOpentracing( 53 | opentracing.Logger(logger.Logger), 54 | opentracing.Tracer(tracer), 55 | )) 56 | // 监控中间件 57 | middlewares = append(middlewares, prometheus.NewPrometheus(prometheus.Enable(true))) 58 | // 熔断限流中间件 59 | middlewares = append(middlewares, hystrixlimitter.NewHystrixLimitter( 60 | hystrixlimitter.Type(hystrixlimitter.HystrixLimitterTypeClient), 61 | hystrixlimitter.Logger(logger.Logger), 62 | hystrixlimitter.ServiceName(c.opts.serviceName), 63 | )) 64 | 65 | // 再次配置 66 | opts = append(opts, Middleware(middlewares...)) 67 | configure(c, opts...) 68 | 69 | return c, nil 70 | } 71 | 72 | // NewClient 创建客户端对象 73 | func NewClient(opts ...Option) (*Client, error) { 74 | c := &Client{ 75 | opts: new(Options), 76 | } 77 | // 配置 78 | configure(c, opts...) 79 | // 服务名检查 80 | if c.opts.serviceName == "" { 81 | return nil, errors.New("Service name must be set") 82 | } 83 | 84 | return c, nil 85 | } 86 | 87 | // 配置设置项 88 | func configure(c *Client, ops ...Option) { 89 | // 处理设置参数 90 | for _, o := range ops { 91 | o(c.opts) 92 | } 93 | // 默认值 94 | if c.opts.connTimeout <= 0 { 95 | c.opts.connTimeout = defaultConnTimeout 96 | } 97 | if c.opts.reg == nil { 98 | c.opts.reg = common.NewClientEtcdRegister(c.opts.serviceName) 99 | } 100 | } 101 | 102 | // NewGrpcClient 用户生成客户端对象 103 | type NewGrpcClient func(*grpc.ClientConn) 104 | 105 | // Dial 连接grpc服务端,并构造多客户端 106 | func (c *Client) Dial(clients ...NewGrpcClient) error { 107 | // 注册中间件获取客户端发现对象 108 | r := c.opts.reg.GetBuilder() 109 | resolver.Register(r) 110 | // grpc连接配置 111 | opts := make([]grpc.DialOption, 0) 112 | opts = append(opts, 113 | grpc.WithBalancerName("round_robin"), // 轮训调用 114 | grpc.WithInsecure(), // 禁用连接安全检查 115 | grpc.WithTimeout(c.opts.connTimeout), // 连接超时 116 | ) 117 | // 连接服务 118 | conn, err := grpc.Dial(r.Scheme()+"://author/"+c.opts.serviceName, opts...) 119 | if err != nil { 120 | return err 121 | } 122 | c.conn = conn 123 | // 初始化grpc客户端 124 | for _, cc := range clients { 125 | cc(conn) 126 | } 127 | return nil 128 | } 129 | 130 | // Close 关闭连接 - 服务结束时 131 | func (c *Client) Close() { 132 | // grpc 连接 133 | if c.conn != nil { 134 | err := c.conn.Close() 135 | if err != nil { 136 | logger.Logger.Errorw("关闭grpc客户端连接错误", "err", err, "service_name", c.opts.serviceName) 137 | } 138 | } 139 | // 链路追踪 140 | if c.tracerCloser != nil { 141 | err := c.tracerCloser.Close() 142 | if err != nil { 143 | logger.Logger.Errorw("关闭链路追踪错误", "err", err, "service_name", c.opts.serviceName) 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /client/options.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/micro-kit/microkit/plugins/middleware" 7 | "github.com/micro-kit/microkit/plugins/register" 8 | ) 9 | 10 | /* 客户端配置 */ 11 | 12 | const ( 13 | defaultConnTimeout = 5 * time.Second 14 | ) 15 | 16 | // Option 实例值设置 17 | type Option func(*Options) 18 | 19 | // Options 注册相关参数 20 | type Options struct { 21 | serviceName string // 服务名 22 | middlewares []middleware.Middleware // 中间件 23 | reg register.Register // 注册中间件 24 | connTimeout time.Duration // 连接超时 25 | } 26 | 27 | // ServiceName 服务名 28 | func ServiceName(serviceName string) Option { 29 | return func(o *Options) { 30 | o.serviceName = serviceName 31 | } 32 | } 33 | 34 | // Middleware 服务中间件 35 | func Middleware(middlewares ...middleware.Middleware) Option { 36 | return func(o *Options) { 37 | o.middlewares = middlewares 38 | } 39 | } 40 | 41 | // Register 服务注册中间件 42 | func Register(reg register.Register) Option { 43 | return func(o *Options) { 44 | o.reg = reg 45 | } 46 | } 47 | 48 | // ConnTimeout 设置连接超时 49 | func ConnTimeout(connTimeout time.Duration) Option { 50 | return func(o *Options) { 51 | o.connTimeout = connTimeout 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /helm/README.md: -------------------------------------------------------------------------------- 1 | # 备注 2 | 用于生成helm应用部署文件,可以根据配置生成模版用于部署 3 | -------------------------------------------------------------------------------- /internal/common/etcd.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/micro-kit/micro-common/config" 7 | "github.com/micro-kit/micro-common/logger" 8 | "github.com/micro-kit/microkit/plugins/register" 9 | "github.com/micro-kit/microkit/plugins/register/etcdv3" 10 | ) 11 | 12 | // NewServerEtcdRegister 获取一个etcd注册中间件 - 配置从环境变量获取 13 | func NewServerEtcdRegister() register.Register { 14 | etcdAddrs := strings.Split(config.GetETCDAddr(), ";") 15 | reg := etcdv3.NewRegistry( 16 | register.Addrs(etcdAddrs...), 17 | register.TTL(config.GetRegisterTTL()), 18 | register.Name(config.GetSvcName()), 19 | register.Schema(config.GetSchema()), 20 | register.Logger(logger.Logger), 21 | ) 22 | return reg 23 | } 24 | 25 | // NewClientEtcdRegister 创建客户端注册对象 26 | func NewClientEtcdRegister(svcName string) register.Register { 27 | etcdAddrs := strings.Split(config.GetETCDAddr(), ";") 28 | reg := etcdv3.NewRegistry( 29 | register.Addrs(etcdAddrs...), 30 | register.TTL(config.GetRegisterTTL()), 31 | register.Name(svcName), 32 | register.Schema(config.GetSchema()), 33 | register.Logger(logger.Logger), 34 | ) 35 | return reg 36 | } 37 | -------------------------------------------------------------------------------- /internal/common/opentracing.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "io" 5 | "time" 6 | 7 | "github.com/micro-kit/micro-common/config" 8 | opentracingGo "github.com/opentracing/opentracing-go" 9 | jaegerCfg "github.com/uber/jaeger-client-go/config" 10 | ) 11 | 12 | /* 生成一个链路追踪器 */ 13 | 14 | // NewJaegerTracer 生成一个默认链路追踪器 15 | func NewJaegerTracer(serviceName string) (tracer opentracingGo.Tracer, closer io.Closer, err error) { 16 | cfg := jaegerCfg.Configuration{ 17 | ServiceName: serviceName, 18 | RPCMetrics: true, 19 | Sampler: &jaegerCfg.SamplerConfig{ 20 | Type: "const", 21 | Param: 1, 22 | }, 23 | Reporter: &jaegerCfg.ReporterConfig{ 24 | LogSpans: true, 25 | BufferFlushInterval: 3 * time.Second, 26 | LocalAgentHostPort: config.GetJaegerAgentHostPort(), 27 | }, 28 | } 29 | tracer, closer, err = cfg.NewTracer() 30 | if err != nil { 31 | return 32 | } 33 | opentracingGo.SetGlobalTracer(tracer) 34 | return 35 | } 36 | -------------------------------------------------------------------------------- /plugins/middleware/chain.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Michal Witkowski. All Rights Reserved. 2 | // See LICENSE for licensing terms. 3 | 4 | // gRPC Server Interceptor chaining middleware. 5 | 6 | // 来源 github.com/grpc-ecosystem/go-grpc-middleware 7 | 8 | package middleware 9 | 10 | import ( 11 | "context" 12 | 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | // ChainUnaryServer creates a single interceptor out of a chain of many interceptors. 17 | // 18 | // Execution is done in left-to-right order, including passing of context. 19 | // For example ChainUnaryServer(one, two, three) will execute one before two before three, and three 20 | // will see context changes of one and two. 21 | func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor { 22 | n := len(interceptors) 23 | 24 | return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 25 | chainer := func(currentInter grpc.UnaryServerInterceptor, currentHandler grpc.UnaryHandler) grpc.UnaryHandler { 26 | return func(currentCtx context.Context, currentReq interface{}) (interface{}, error) { 27 | return currentInter(currentCtx, currentReq, info, currentHandler) 28 | } 29 | } 30 | 31 | chainedHandler := handler 32 | for i := n - 1; i >= 0; i-- { 33 | chainedHandler = chainer(interceptors[i], chainedHandler) 34 | } 35 | 36 | return chainedHandler(ctx, req) 37 | } 38 | } 39 | 40 | // ChainStreamServer creates a single interceptor out of a chain of many interceptors. 41 | // 42 | // Execution is done in left-to-right order, including passing of context. 43 | // For example ChainUnaryServer(one, two, three) will execute one before two before three. 44 | // If you want to pass context between interceptors, use WrapServerStream. 45 | func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor { 46 | n := len(interceptors) 47 | 48 | return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 49 | chainer := func(currentInter grpc.StreamServerInterceptor, currentHandler grpc.StreamHandler) grpc.StreamHandler { 50 | return func(currentSrv interface{}, currentStream grpc.ServerStream) error { 51 | return currentInter(currentSrv, currentStream, info, currentHandler) 52 | } 53 | } 54 | 55 | chainedHandler := handler 56 | for i := n - 1; i >= 0; i-- { 57 | chainedHandler = chainer(interceptors[i], chainedHandler) 58 | } 59 | 60 | return chainedHandler(srv, ss) 61 | } 62 | } 63 | 64 | // ChainUnaryClient creates a single interceptor out of a chain of many interceptors. 65 | // 66 | // Execution is done in left-to-right order, including passing of context. 67 | // For example ChainUnaryClient(one, two, three) will execute one before two before three. 68 | func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor { 69 | n := len(interceptors) 70 | 71 | return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { 72 | chainer := func(currentInter grpc.UnaryClientInterceptor, currentInvoker grpc.UnaryInvoker) grpc.UnaryInvoker { 73 | return func(currentCtx context.Context, currentMethod string, currentReq, currentRepl interface{}, currentConn *grpc.ClientConn, currentOpts ...grpc.CallOption) error { 74 | return currentInter(currentCtx, currentMethod, currentReq, currentRepl, currentConn, currentInvoker, currentOpts...) 75 | } 76 | } 77 | 78 | chainedInvoker := invoker 79 | for i := n - 1; i >= 0; i-- { 80 | chainedInvoker = chainer(interceptors[i], chainedInvoker) 81 | } 82 | 83 | return chainedInvoker(ctx, method, req, reply, cc, opts...) 84 | } 85 | } 86 | 87 | // ChainStreamClient creates a single interceptor out of a chain of many interceptors. 88 | // 89 | // Execution is done in left-to-right order, including passing of context. 90 | // For example ChainStreamClient(one, two, three) will execute one before two before three. 91 | func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor { 92 | n := len(interceptors) 93 | 94 | return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { 95 | chainer := func(currentInter grpc.StreamClientInterceptor, currentStreamer grpc.Streamer) grpc.Streamer { 96 | return func(currentCtx context.Context, currentDesc *grpc.StreamDesc, currentConn *grpc.ClientConn, currentMethod string, currentOpts ...grpc.CallOption) (grpc.ClientStream, error) { 97 | return currentInter(currentCtx, currentDesc, currentConn, currentMethod, currentStreamer, currentOpts...) 98 | } 99 | } 100 | 101 | chainedStreamer := streamer 102 | for i := n - 1; i >= 0; i-- { 103 | chainedStreamer = chainer(interceptors[i], chainedStreamer) 104 | } 105 | 106 | return chainedStreamer(ctx, desc, cc, method, opts...) 107 | } 108 | } 109 | 110 | // Chain creates a single interceptor out of a chain of many interceptors. 111 | // 112 | // WithUnaryServerChain is a grpc.Server config option that accepts multiple unary interceptors. 113 | // Basically syntactic sugar. 114 | func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption { 115 | return grpc.UnaryInterceptor(ChainUnaryServer(interceptors...)) 116 | } 117 | 118 | // WithStreamServerChain is a grpc.Server config option that accepts multiple stream interceptors. 119 | // Basically syntactic sugar. 120 | func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption { 121 | return grpc.StreamInterceptor(ChainStreamServer(interceptors...)) 122 | } 123 | -------------------------------------------------------------------------------- /plugins/middleware/errorformat/README.md: -------------------------------------------------------------------------------- 1 | # 备注 2 | 处理服务端返回错误,如果不是自定义错误类型,则设置为默认错误 -------------------------------------------------------------------------------- /plugins/middleware/hystrixlimitter/README.md: -------------------------------------------------------------------------------- 1 | # 熔断&限流 2 | 3 | 1. 服务端实现限流 4 | 2. 客户端熔断 5 | -------------------------------------------------------------------------------- /plugins/middleware/hystrixlimitter/hystrix.go: -------------------------------------------------------------------------------- 1 | package hystrixlimitter 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/afex/hystrix-go/hystrix" 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | /* 11 | 客户端熔断 - 如果可以,可以在调用层并发使用 hystrix.Go 处理 12 | https://segmentfault.com/a/1190000012439580 13 | https://segmentfault.com/a/1190000015347065 14 | */ 15 | 16 | // UnaryClient 非流式客户端中间件 grpc.UnaryClientInterceptor 17 | func (hl *HystrixLimitter) UnaryClient(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) { 18 | if hl.Options.FilterOutFunc != nil && !hl.Options.FilterOutFunc(ctx, method) { 19 | err = invoker(ctx, method, req, reply, cc, opts...) 20 | return 21 | } 22 | 23 | // 熔断 24 | err = hystrix.Do(hl.Options.ServiceName, func() error { 25 | // 执行下一步 26 | return invoker(ctx, method, req, reply, cc, opts...) 27 | }, func(err error) error { 28 | // 失败处理逻辑,访问其他资源失败时,或者处于熔断开启状态时,会调用这段逻辑 29 | // 可以简单构造一个response返回,也可以有一定的策略,比如访问备份资源 30 | // 也可以直接返回 err,这样不用和远端失败的资源通信,防止雪崩 31 | return err 32 | }) 33 | if err != nil { 34 | return 35 | } 36 | return 37 | } 38 | 39 | // StreamClient 流式服客户中间件 grpc.StreamClientInterceptor 40 | func (hl *HystrixLimitter) StreamClient(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (cs grpc.ClientStream, err error) { 41 | if hl.Options.FilterOutFunc != nil && !hl.Options.FilterOutFunc(ctx, method) { 42 | return streamer(ctx, desc, cc, method, opts...) 43 | } 44 | 45 | // TODO 流调用,暂时不处理 46 | 47 | cs, err = streamer(ctx, desc, cc, method, opts...) 48 | return 49 | } 50 | -------------------------------------------------------------------------------- /plugins/middleware/hystrixlimitter/hystrixlimitter.go: -------------------------------------------------------------------------------- 1 | package hystrixlimitter 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/afex/hystrix-go/hystrix" 7 | "github.com/micro-kit/microkit/plugins/middleware" 8 | "golang.org/x/time/rate" 9 | ) 10 | 11 | // HystrixLimitter 熔断限流中间件 12 | type HystrixLimitter struct { 13 | Options *Options 14 | Limiter *rate.Limiter // 限流器 15 | StreamLimiter *rate.Limiter // 流调用 限流器 16 | 17 | } 18 | 19 | // NewHystrixLimitter 创建熔断限流中间件 20 | func NewHystrixLimitter(opts ...Option) middleware.Middleware { 21 | hl := &HystrixLimitter{ 22 | Options: new(Options), 23 | } 24 | // 配置 25 | configure(hl, opts...) 26 | // 未设置日志对象退出 27 | if hl.Options.Logger == nil { 28 | log.Fatalln("限流熔断中间件未设置日志对象") 29 | } 30 | // 如果未设置类型,直接退出 31 | if hl.Options.Type == "" { 32 | hl.Options.Logger.Fatalw("未设置限流熔断中间件类型 server|client", "options", hl.Options) 33 | } 34 | // 如果是服务端-初始化限流器 35 | if hl.Options.Type == HystrixLimitterTypeServer { 36 | // 普通rpc调用 37 | hl.Limiter = rate.NewLimiter(rate.Every(hl.Options.Limiter), hl.Options.LimiterBurst) 38 | // 流式调用 39 | hl.StreamLimiter = rate.NewLimiter(rate.Every(hl.Options.StreamLimiter), hl.Options.StreamLimiterBurst) 40 | } 41 | if hl.Options.Type == HystrixLimitterTypeClient { 42 | if hl.Options.ServiceName == "" { 43 | hl.Options.Logger.Fatalw("服务名不能为空", "options", hl.Options) 44 | } 45 | // 熔断器 46 | hystrix.ConfigureCommand( 47 | hl.Options.ServiceName, // 熔断器名字,可以用服务名称命名,一个名字对应一个熔断器,对应一份熔断策略 48 | hystrix.CommandConfig{ 49 | Timeout: hl.Options.Timeout, // 超时时间 毫秒 50 | MaxConcurrentRequests: hl.Options.MaxConcurrentRequests, // 最大并发数,超过并发返回错误 51 | RequestVolumeThreshold: hl.Options.RequestVolumeThreshold, // 请求数量的阀值,用这些数量的请求来计算阀值 52 | ErrorPercentThreshold: hl.Options.ErrorPercentThreshold, // 错误率阀值,达到阀值,启动熔断 百分比 53 | SleepWindow: hl.Options.SleepWindow, // 熔断尝试恢复时间 毫秒 54 | }, 55 | ) 56 | } 57 | return hl 58 | } 59 | 60 | // 配置设置项 61 | func configure(hl *HystrixLimitter, ops ...Option) { 62 | // 处理设置参数 63 | for _, o := range ops { 64 | o(hl.Options) 65 | } 66 | /* 默认值 */ 67 | // 限流 68 | if hl.Options.Limiter <= 0 { 69 | hl.Options.Limiter = DefaultLimiter 70 | } 71 | if hl.Options.LimiterBurst <= 0 { 72 | hl.Options.LimiterBurst = DefaultLimiterBurst 73 | } 74 | if hl.Options.StreamLimiter <= 0 { 75 | hl.Options.StreamLimiter = DefaultStreamLimiter 76 | } 77 | if hl.Options.StreamLimiterBurst <= 0 { 78 | hl.Options.StreamLimiterBurst = DefaultStreamLimiterBurst 79 | } 80 | // 熔断 81 | if hl.Options.Timeout <= 0 { 82 | hl.Options.Timeout = hystrix.DefaultTimeout 83 | } 84 | if hl.Options.MaxConcurrentRequests <= 0 { 85 | hl.Options.MaxConcurrentRequests = hystrix.DefaultMaxConcurrent 86 | } 87 | if hl.Options.RequestVolumeThreshold <= 0 { 88 | hl.Options.RequestVolumeThreshold = hystrix.DefaultVolumeThreshold 89 | } 90 | if hl.Options.ErrorPercentThreshold <= 0 { 91 | hl.Options.ErrorPercentThreshold = hystrix.DefaultErrorPercentThreshold 92 | } 93 | if hl.Options.SleepWindow <= 0 { 94 | hl.Options.SleepWindow = hystrix.DefaultSleepWindow 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /plugins/middleware/hystrixlimitter/limitter.go: -------------------------------------------------------------------------------- 1 | package hystrixlimitter 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | /* 11 | 服务端限流 12 | https://segmentfault.com/a/1190000015347065 13 | */ 14 | 15 | var ( 16 | // ErrLimitExceed 超出限流器限制 17 | ErrLimitExceed = errors.New("Rate limit exceed!") 18 | ) 19 | 20 | // UnaryHandler 非流式中间件 21 | func (hl *HystrixLimitter) UnaryHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { 22 | if hl.Options.FilterOutFunc != nil && !hl.Options.FilterOutFunc(ctx, info.FullMethod) { 23 | resp, err = handler(ctx, req) 24 | return 25 | } 26 | 27 | // 限流 28 | if hl.Limiter.Allow() == false { 29 | return nil, ErrLimitExceed 30 | } 31 | 32 | // 执行下一步 33 | resp, err = handler(ctx, req) 34 | return 35 | } 36 | 37 | // StreamHandler 流式中间件 38 | func (hl *HystrixLimitter) StreamHandler(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) { 39 | if hl.Options.FilterOutFunc != nil && !hl.Options.FilterOutFunc(stream.Context(), info.FullMethod) { 40 | err = handler(srv, stream) 41 | return 42 | } 43 | 44 | // 限流 45 | if hl.StreamLimiter.Allow() == false { 46 | return ErrLimitExceed 47 | } 48 | 49 | err = handler(srv, stream) 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /plugins/middleware/hystrixlimitter/options.go: -------------------------------------------------------------------------------- 1 | package hystrixlimitter 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/micro-kit/microkit/plugins/middleware" 7 | "go.uber.org/zap" 8 | ) 9 | 10 | // 默认值 11 | const ( 12 | // DefaultLimiter 默认3毫秒 13 | DefaultLimiter = 3 * time.Millisecond 14 | // DefaultLimiterBurst 默认缓存token数 15 | DefaultLimiterBurst = 100 16 | // DefaultStreamLimiter 流调用 默认3毫米 17 | DefaultStreamLimiter = 3 * time.Millisecond 18 | // DefaultStreamLimiterBurst 流调用 默认缓存token数 19 | DefaultStreamLimiterBurst = 3 20 | // HystrixLimitterTypeServer 服务端 21 | HystrixLimitterTypeServer = "server" 22 | // HystrixLimitterTypeClient 客户端 23 | HystrixLimitterTypeClient = "client" 24 | ) 25 | 26 | // HystrixLimitterType 客户端还是服务端 27 | type HystrixLimitterType string 28 | 29 | // Option 实例值设置 30 | type Option func(*Options) 31 | 32 | // Options 注册相关参数 33 | type Options struct { 34 | Type HystrixLimitterType 35 | FilterOutFunc middleware.FilterFunc 36 | Logger *zap.SugaredLogger 37 | /* 限流 */ 38 | Limiter time.Duration // 限流器,多久生成一个token 39 | StreamLimiter time.Duration // 流调用 限流器,多久生成一个token 40 | LimiterBurst int // 缓存token数量 41 | StreamLimiterBurst int // 流调用 缓存token数量 42 | /* 熔断 */ 43 | ServiceName string // 服务名 44 | Timeout int // 单位毫秒 45 | MaxConcurrentRequests int // 最大并发数,超过并发返回错误 46 | RequestVolumeThreshold int // 请求数量的阀值,用这些数量的请求来计算阀值 47 | ErrorPercentThreshold int // 错误数量阀值,达到阀值,启动熔断 48 | SleepWindow int // 熔断尝试恢复时间 49 | } 50 | 51 | // Type 设置是客户端还是服务端 52 | func Type(typ HystrixLimitterType) Option { 53 | return func(o *Options) { 54 | o.Type = typ 55 | } 56 | } 57 | 58 | // Logger 设置日志对象 59 | func Logger(logger *zap.SugaredLogger) Option { 60 | return func(o *Options) { 61 | o.Logger = logger 62 | } 63 | } 64 | 65 | // FilterOutFunc 设置中间件忽略函数列表 66 | func FilterOutFunc(filterOutFunc middleware.FilterFunc) Option { 67 | return func(o *Options) { 68 | o.FilterOutFunc = filterOutFunc 69 | } 70 | } 71 | 72 | /* 限流 */ 73 | 74 | // Limiter 多久生成一个token 75 | func Limiter(limiter time.Duration) Option { 76 | if limiter <= 0 { 77 | limiter = DefaultLimiter 78 | } 79 | return func(o *Options) { 80 | o.Limiter = limiter 81 | } 82 | } 83 | 84 | // LimiterBurst 限流缓存token数量 85 | func LimiterBurst(limiterBurst int) Option { 86 | if limiterBurst <= 0 { 87 | limiterBurst = DefaultLimiterBurst 88 | } 89 | return func(o *Options) { 90 | o.LimiterBurst = limiterBurst 91 | } 92 | } 93 | 94 | // StreamLimiter 多久生成一个token 95 | func StreamLimiter(limiter time.Duration) Option { 96 | if limiter <= 0 { 97 | limiter = DefaultStreamLimiter 98 | } 99 | return func(o *Options) { 100 | o.StreamLimiter = limiter 101 | } 102 | } 103 | 104 | // StreamLimiterBurst 限流缓存token数量 105 | func StreamLimiterBurst(limiterBurst int) Option { 106 | if limiterBurst <= 0 { 107 | limiterBurst = DefaultStreamLimiterBurst 108 | } 109 | return func(o *Options) { 110 | o.StreamLimiterBurst = limiterBurst 111 | } 112 | } 113 | 114 | /* end 限流 */ 115 | 116 | /* 熔断 */ 117 | 118 | // ServiceName 服务名 119 | func ServiceName(serviceName string) Option { 120 | return func(o *Options) { 121 | o.ServiceName = serviceName 122 | } 123 | } 124 | 125 | // Timeout 单位毫秒 126 | func Timeout(t int) Option { 127 | return func(o *Options) { 128 | o.Timeout = t 129 | } 130 | } 131 | 132 | // MaxConcurrentRequests 最大并发数,超过并发返回错误 133 | func MaxConcurrentRequests(max int) Option { 134 | return func(o *Options) { 135 | o.MaxConcurrentRequests = max 136 | } 137 | } 138 | 139 | // RequestVolumeThreshold 请求数量的阀值,用这些数量的请求来计算阀值 140 | func RequestVolumeThreshold(threshold int) Option { 141 | return func(o *Options) { 142 | o.RequestVolumeThreshold = threshold 143 | } 144 | } 145 | 146 | // ErrorPercentThreshold 错误数量阀值,达到阀值,启动熔断 147 | func ErrorPercentThreshold(threshold int) Option { 148 | return func(o *Options) { 149 | o.ErrorPercentThreshold = threshold 150 | } 151 | } 152 | 153 | // SleepWindow 熔断尝试恢复时间 154 | func SleepWindow(sleep int) Option { 155 | return func(o *Options) { 156 | o.SleepWindow = sleep 157 | } 158 | } 159 | 160 | /* end 熔断 */ 161 | -------------------------------------------------------------------------------- /plugins/middleware/logger/grpclog.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | ) 6 | 7 | /* 设置grpc内置日志对象 */ 8 | 9 | // GrpcLog 符合grpclog.Logger接口 10 | type GrpcLog struct { 11 | SugaredLogger *zap.SugaredLogger 12 | } 13 | 14 | // Info logs to INFO log. Arguments are handled in the manner of fmt.Print. 15 | func (l *GrpcLog) Info(args ...interface{}) { 16 | // l.SugaredLogger.Info(args...) 17 | } 18 | 19 | // Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println. 20 | func (l *GrpcLog) Infoln(args ...interface{}) { 21 | // args = append(args, "\n") 22 | // l.SugaredLogger.Info(args...) 23 | } 24 | 25 | // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf. 26 | func (l *GrpcLog) Infof(format string, args ...interface{}) { 27 | // l.SugaredLogger.Infof(format, args...) 28 | } 29 | 30 | // Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print. 31 | func (l *GrpcLog) Warning(args ...interface{}) { 32 | l.SugaredLogger.Warn(args...) 33 | } 34 | 35 | // Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println. 36 | func (l *GrpcLog) Warningln(args ...interface{}) { 37 | args = append(args, "\n") 38 | l.SugaredLogger.Warn(args...) 39 | } 40 | 41 | // Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf. 42 | func (l *GrpcLog) Warningf(format string, args ...interface{}) { 43 | l.SugaredLogger.Warnf(format, args...) 44 | } 45 | 46 | // Error logs to ERROR log. Arguments are handled in the manner of fmt.Print. 47 | func (l *GrpcLog) Error(args ...interface{}) { 48 | l.SugaredLogger.Error(args...) 49 | } 50 | 51 | // Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println. 52 | func (l *GrpcLog) Errorln(args ...interface{}) { 53 | args = append(args, "\n") 54 | l.SugaredLogger.Error(args...) 55 | } 56 | 57 | // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. 58 | func (l *GrpcLog) Errorf(format string, args ...interface{}) { 59 | l.SugaredLogger.Errorf(format, args...) 60 | } 61 | 62 | // Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print. 63 | // gRPC ensures that all Fatal logs will exit with os.Exit(1). 64 | // Implementations may also call os.Exit() with a non-zero exit code. 65 | func (l *GrpcLog) Fatal(args ...interface{}) { 66 | l.SugaredLogger.Fatal(args...) 67 | } 68 | 69 | // Fatalln logs to ERROR log. Arguments are handled in the manner of fmt.Println. 70 | // gRPC ensures that all Fatal logs will exit with os.Exit(1). 71 | // Implementations may also call os.Exit() with a non-zero exit code. 72 | func (l *GrpcLog) Fatalln(args ...interface{}) { 73 | args = append(args, "\n") 74 | l.SugaredLogger.Fatal(args...) 75 | } 76 | 77 | // Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. 78 | // gRPC ensures that all Fatal logs will exit with os.Exit(1). 79 | // Implementations may also call os.Exit() with a non-zero exit code. 80 | func (l *GrpcLog) Fatalf(format string, args ...interface{}) { 81 | l.SugaredLogger.Fatalf(format, args...) 82 | } 83 | 84 | // V reports whether verbosity level l is at least the requested verbose level. 85 | func (*GrpcLog) V(l int) bool { 86 | return true 87 | } 88 | -------------------------------------------------------------------------------- /plugins/middleware/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/micro-kit/microkit/plugins/middleware" 7 | "go.uber.org/zap" 8 | "go.uber.org/zap/zapcore" 9 | "google.golang.org/grpc" 10 | "google.golang.org/grpc/grpclog" 11 | "gopkg.in/natefinch/lumberjack.v2" 12 | ) 13 | 14 | /* 日志中间件 */ 15 | 16 | // ZapLogger zap 日志中间件 17 | type ZapLogger struct { 18 | Options *Options 19 | SugaredLogger *zap.SugaredLogger 20 | } 21 | 22 | // NewZapLogger 创建一个zap日志中间件 23 | func NewZapLogger(filterOutFunc middleware.FilterFunc, opts ...Option) middleware.Middleware { 24 | zapLogger := &ZapLogger{ 25 | Options: new(Options), 26 | } 27 | // 配置 28 | configure(zapLogger, opts...) 29 | // 未设置日志对象,则创建一个 30 | if zapLogger.Options.Logger == nil { 31 | // 创建zap日志对象 32 | syncWriter := zapcore.AddSync(&lumberjack.Logger{ 33 | Filename: zapLogger.Options.Filename, 34 | MaxSize: int(zapLogger.Options.MaxSize), 35 | LocalTime: zapLogger.Options.LocalTime, 36 | Compress: zapLogger.Options.Compress, 37 | }) 38 | encoder := zap.NewProductionEncoderConfig() 39 | encoder.EncodeTime = zapcore.EpochMillisTimeEncoder // 时间格式 40 | core := zapcore.NewCore(zapcore.NewJSONEncoder(encoder), syncWriter, zap.NewAtomicLevelAt(zapcore.Level(zapLogger.Options.Level))) 41 | logger := zap.New(core, zap.AddCaller()) 42 | zapLogger.SugaredLogger = logger.Sugar() 43 | } else { 44 | zapLogger.SugaredLogger = zapLogger.Options.Logger 45 | } 46 | 47 | // 设置grpc日志对象 48 | grpclog.SetLoggerV2(&GrpcLog{ 49 | SugaredLogger: zapLogger.SugaredLogger, 50 | }) 51 | 52 | return zapLogger 53 | } 54 | 55 | // 配置设置项 56 | func configure(zap *ZapLogger, ops ...Option) { 57 | // 默认值 58 | zap.Options.LocalTime = true 59 | zap.Options.Compress = true 60 | // 处理设置参数 61 | for _, o := range ops { 62 | o(zap.Options) 63 | } 64 | // 参数为空时默认值 65 | if zap.Options.Filename == "" { 66 | zap.Options.Filename = DefaultFilename 67 | } 68 | if zap.Options.MaxSize <= 0 { 69 | zap.Options.MaxSize = DefaultMaxSize 70 | } 71 | if zap.Options.Level < -1 || zap.Options.Level > 5 { 72 | zap.Options.Level = DefaultLevel 73 | } 74 | } 75 | 76 | // UnaryHandler 非流式中间件 77 | func (zap *ZapLogger) UnaryHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { 78 | if zap.Options.FilterOutFunc != nil && !zap.Options.FilterOutFunc(ctx, info.FullMethod) { 79 | resp, err = handler(ctx, req) 80 | return 81 | } 82 | // 记录日志 83 | defer func() { 84 | if err != nil { 85 | zap.SugaredLogger.Errorw("请求出现错误", "req", req, "resp", resp, "method", info.FullMethod, "err", err) 86 | } else if zap.Options.Level == zapcore.DebugLevel { 87 | zap.SugaredLogger.Debugw("请求日志", "req", req, "resp", resp, "method", info.FullMethod) 88 | } 89 | }() 90 | // 执行下一步 91 | resp, err = handler(ctx, req) 92 | return 93 | } 94 | 95 | // StreamHandler 流式中间件 96 | func (zap *ZapLogger) StreamHandler(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) { 97 | if zap.Options.FilterOutFunc != nil && !zap.Options.FilterOutFunc(stream.Context(), info.FullMethod) { 98 | err = handler(srv, stream) 99 | return 100 | } 101 | defer func() { 102 | if err != nil { 103 | zap.SugaredLogger.Errorw("请求流函数", "method", info.FullMethod, "err", err) 104 | } else if zap.Options.Level == zapcore.DebugLevel { 105 | zap.SugaredLogger.Debugw("请求流函数", "method", info.FullMethod) 106 | } 107 | }() 108 | err = handler(srv, stream) 109 | return 110 | } 111 | 112 | // UnaryClient 非流式客户端中间件 grpc.UnaryClientInterceptor 113 | func (zap *ZapLogger) UnaryClient(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) { 114 | if zap.Options.FilterOutFunc != nil && !zap.Options.FilterOutFunc(ctx, method) { 115 | err = invoker(ctx, method, req, reply, cc, opts...) 116 | return 117 | } 118 | // 记录日志 119 | defer func() { 120 | if err != nil { 121 | zap.SugaredLogger.Errorw("请求出现错误", "req", req, "reply", reply, "method", method, "err", err) 122 | } else if zap.Options.Level == zapcore.DebugLevel { 123 | zap.SugaredLogger.Debugw("请求流函数", "method", method, "req", req, "reply", reply) 124 | } 125 | }() 126 | // 执行下一步 127 | err = invoker(ctx, method, req, reply, cc, opts...) 128 | return 129 | } 130 | 131 | // StreamClient 流式服客户中间件 grpc.StreamClientInterceptor 132 | func (zap *ZapLogger) StreamClient(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (cs grpc.ClientStream, err error) { 133 | if zap.Options.FilterOutFunc != nil && !zap.Options.FilterOutFunc(ctx, method) { 134 | return streamer(ctx, desc, cc, method, opts...) 135 | } 136 | defer func() { 137 | if err != nil { 138 | zap.SugaredLogger.Errorw("请求流函数", "method", method, "err", err) 139 | } else if zap.Options.Level == zapcore.DebugLevel { 140 | zap.SugaredLogger.Debugw("请求流函数", "method", method, "err", err) 141 | } 142 | }() 143 | cs, err = streamer(ctx, desc, cc, method, opts...) 144 | return 145 | } 146 | -------------------------------------------------------------------------------- /plugins/middleware/logger/options.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "github.com/micro-kit/microkit/plugins/middleware" 5 | "go.uber.org/zap" 6 | "go.uber.org/zap/zapcore" 7 | ) 8 | 9 | /* 日志中间件参数 */ 10 | 11 | // Option 实例值设置 12 | type Option func(*Options) 13 | 14 | // 默认值 15 | const ( 16 | // DefaultLevel 默认日志级别 17 | DefaultLevel = zapcore.DebugLevel 18 | // DefaultFileName 默认日志输出路径 19 | DefaultFilename = "./access.log" 20 | // DefaultMaxSize 默认单日志文件大小 21 | DefaultMaxSize = 100 22 | ) 23 | 24 | // 级别映射 25 | var zapLevel = map[string]zapcore.Level{ 26 | "debug": zapcore.DebugLevel, 27 | "info": zapcore.InfoLevel, 28 | "warn": zapcore.WarnLevel, 29 | "error": zapcore.ErrorLevel, 30 | } 31 | 32 | // Options 注册相关参数 33 | type Options struct { 34 | Logger *zap.SugaredLogger 35 | Filename string 36 | MaxSize int32 37 | LocalTime bool 38 | Compress bool 39 | Level zapcore.Level 40 | FilterOutFunc middleware.FilterFunc 41 | } 42 | 43 | // Logger 设置日志对象 44 | func Logger(logger *zap.SugaredLogger) Option { 45 | return func(o *Options) { 46 | o.Logger = logger 47 | } 48 | } 49 | 50 | // Filename 日志文件名 51 | func Filename(filename string) Option { 52 | return func(o *Options) { 53 | o.Filename = filename 54 | } 55 | } 56 | 57 | // MaxSize 单日志文件大小上限 58 | func MaxSize(maxSize int32) Option { 59 | return func(o *Options) { 60 | o.MaxSize = maxSize 61 | } 62 | } 63 | 64 | // LocalTime 是否使用本地事件 65 | func LocalTime(localTime bool) Option { 66 | return func(o *Options) { 67 | o.LocalTime = localTime 68 | } 69 | } 70 | 71 | // Compress 是否压缩日志文件 72 | func Compress(compress bool) Option { 73 | return func(o *Options) { 74 | o.Compress = compress 75 | } 76 | } 77 | 78 | // Level 日志级别 79 | func Level(l string) Option { 80 | return func(o *Options) { 81 | if level, ok := zapLevel[l]; ok { 82 | o.Level = level 83 | } else { 84 | o.Level = DefaultLevel 85 | } 86 | } 87 | } 88 | 89 | // FilterOutFunc 设置中间件忽略函数列表 90 | func FilterOutFunc(filterOutFunc middleware.FilterFunc) Option { 91 | return func(o *Options) { 92 | o.FilterOutFunc = filterOutFunc 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /plugins/middleware/middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc" 7 | ) 8 | 9 | /* grpc中间件 */ 10 | 11 | // Middleware 通用拦截器中间件 12 | type Middleware interface { 13 | // 非流式服务端中间件 grpc.UnaryServerInterceptor 14 | UnaryHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) 15 | // 流式服务端中间件 grpc.StreamServerInterceptor 16 | StreamHandler(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error 17 | // 非流式客户端中间件 grpc.UnaryClientInterceptor 18 | UnaryClient(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error 19 | // 流式服客户中间件 grpc.StreamClientInterceptor 20 | StreamClient(parentCtx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) 21 | } 22 | 23 | // FilterFunc 过滤函数,如果返回false则中间件不处理 24 | type FilterFunc func(ctx context.Context, fullMethodName string) bool 25 | -------------------------------------------------------------------------------- /plugins/middleware/opentracing/opentracing.go: -------------------------------------------------------------------------------- 1 | package opentracing 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "io" 7 | slog "log" 8 | "strings" 9 | 10 | "github.com/micro-kit/microkit/plugins/middleware" 11 | "github.com/opentracing/opentracing-go" 12 | "github.com/opentracing/opentracing-go/ext" 13 | "github.com/opentracing/opentracing-go/log" 14 | "google.golang.org/grpc" 15 | "google.golang.org/grpc/metadata" 16 | ) 17 | 18 | /* 链路追踪中间件 */ 19 | 20 | var ( 21 | grpcTag = opentracing.Tag{Key: string(ext.Component), Value: "gRPC"} 22 | ) 23 | 24 | // Opentracing 链路追踪中间件 25 | type Opentracing struct { 26 | Options *Options 27 | } 28 | 29 | // NewOpentracing 创建链路追踪器 30 | func NewOpentracing(opts ...Option) middleware.Middleware { 31 | opentracing := &Opentracing{ 32 | Options: new(Options), 33 | } 34 | // 配置 35 | configure(opentracing, opts...) 36 | // 未设置日志对象退出 37 | if opentracing.Options.Logger == nil { 38 | slog.Fatalln("链路追踪中间件未设置日志对象") 39 | } 40 | 41 | return opentracing 42 | } 43 | 44 | // 配置设置项 45 | func configure(zap *Opentracing, ops ...Option) { 46 | // 处理设置参数 47 | for _, o := range ops { 48 | o(zap.Options) 49 | } 50 | } 51 | 52 | /* 服务端拦截器 */ 53 | 54 | // UnaryHandler 非流式中间件 55 | func (trace *Opentracing) UnaryHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { 56 | if trace.Options.FilterOutFunc != nil && !trace.Options.FilterOutFunc(ctx, info.FullMethod) { 57 | resp, err = handler(ctx, req) 58 | return 59 | } 60 | // 处理链路追踪数据 61 | newCtx, serverSpan := trace.newServerSpanFromInbound(ctx, trace.Options.Tracer, info.FullMethod) 62 | defer func() { 63 | if err != nil { 64 | ext.Error.Set(serverSpan, true) 65 | serverSpan.LogFields(log.String("error", err.Error())) 66 | reqJs, _ := json.Marshal(req) 67 | serverSpan.LogFields(log.String("req", string(reqJs))) 68 | replyJs, _ := json.Marshal(resp) 69 | serverSpan.LogFields(log.String("resp", string(replyJs))) 70 | } 71 | serverSpan.Finish() 72 | }() 73 | 74 | // 执行下一步 75 | resp, err = handler(newCtx, req) 76 | return 77 | } 78 | 79 | // StreamHandler 流式中间件 80 | func (trace *Opentracing) StreamHandler(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) { 81 | if trace.Options.FilterOutFunc != nil && !trace.Options.FilterOutFunc(stream.Context(), info.FullMethod) { 82 | err = handler(srv, stream) 83 | return 84 | } 85 | 86 | // 处理链路追踪数据 87 | _, serverSpan := trace.newServerSpanFromInbound(stream.Context(), trace.Options.Tracer, info.FullMethod) 88 | defer func() { 89 | if err != nil { 90 | ext.Error.Set(serverSpan, true) 91 | serverSpan.LogFields(log.String("error", err.Error())) 92 | } 93 | serverSpan.Finish() 94 | }() 95 | 96 | err = handler(srv, stream) 97 | return 98 | } 99 | 100 | // MDReaderWriter metadata Reader and Writer 101 | type MDReaderWriter struct { 102 | metadata.MD 103 | } 104 | 105 | // ForeachKey range all keys to call handler 106 | func (c MDReaderWriter) ForeachKey(handler func(key, val string) error) error { 107 | for k, vs := range c.MD { 108 | for _, v := range vs { 109 | if err := handler(k, v); err != nil { 110 | return err 111 | } 112 | } 113 | } 114 | return nil 115 | } 116 | 117 | // Set implements Set() of opentracing.TextMapWriter 118 | func (c MDReaderWriter) Set(key, val string) { 119 | key = strings.ToLower(key) 120 | c.MD[key] = append(c.MD[key], val) 121 | } 122 | 123 | // 服务端链路对象 https://segmentfault.com/a/1190000014546372 124 | func (trace *Opentracing) newServerSpanFromInbound(ctx context.Context, tracer opentracing.Tracer, fullMethodName string) (context.Context, opentracing.Span) { 125 | //从context中取出metadata 126 | md, ok := metadata.FromIncomingContext(ctx) 127 | if !ok { 128 | md = metadata.New(nil) 129 | } 130 | //从metadata中取出最终数据,并创建出span对象 131 | spanContext, err := tracer.Extract(opentracing.TextMap, MDReaderWriter{md}) 132 | if err != nil && err != opentracing.ErrSpanContextNotFound { 133 | trace.Options.Logger.Errorw("failed parsing trace information", "err", err) 134 | } 135 | serverSpan := tracer.StartSpan( 136 | fullMethodName, 137 | ext.RPCServerOption(spanContext), 138 | grpcTag, 139 | ext.SpanKindRPCServer, 140 | ) 141 | // 这里在上下文记录了span对象 - 可通过opentracing.SpanFromContext(ctx)获取,可能为nil 142 | ctx = opentracing.ContextWithSpan(ctx, serverSpan) 143 | return ctx, serverSpan 144 | } 145 | 146 | /* 以下客户端拦截器 */ 147 | 148 | // UnaryClient 非流式客户端中间件 grpc.UnaryClientInterceptor 149 | func (trace *Opentracing) UnaryClient(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) { 150 | if trace.Options.FilterOutFunc != nil && !trace.Options.FilterOutFunc(ctx, method) { 151 | err = invoker(ctx, method, req, reply, cc, opts...) 152 | return 153 | } 154 | 155 | // 处理链路追踪数据 156 | newCtx, serverSpan := trace.newClientSpanFromContext(ctx, trace.Options.Tracer, method) 157 | defer func() { 158 | // 记录错误和请求响应参数 159 | if err != nil && err != io.EOF { 160 | ext.Error.Set(serverSpan, true) 161 | serverSpan.LogFields(log.String("error", err.Error())) 162 | reqJs, _ := json.Marshal(req) 163 | serverSpan.LogFields(log.String("req", string(reqJs))) 164 | replyJs, _ := json.Marshal(reply) 165 | serverSpan.LogFields(log.String("resp", string(replyJs))) 166 | } 167 | serverSpan.Finish() 168 | }() 169 | 170 | // 执行下一步 171 | err = invoker(newCtx, method, req, reply, cc, opts...) 172 | return 173 | } 174 | 175 | // StreamClient 流式服客户中间件 grpc.StreamClientInterceptor 176 | func (trace *Opentracing) StreamClient(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (cs grpc.ClientStream, err error) { 177 | if trace.Options.FilterOutFunc != nil && !trace.Options.FilterOutFunc(ctx, method) { 178 | return streamer(ctx, desc, cc, method, opts...) 179 | } 180 | 181 | // 处理链路追踪数据 182 | newCtx, clientSpan := trace.newClientSpanFromContext(ctx, trace.Options.Tracer, method) 183 | defer func() { 184 | if err != nil && err != io.EOF { 185 | ext.Error.Set(clientSpan, true) 186 | clientSpan.LogFields(log.String("error", err.Error())) 187 | } 188 | clientSpan.Finish() 189 | }() 190 | 191 | cs, err = streamer(newCtx, desc, cc, method, opts...) 192 | return 193 | } 194 | 195 | // 客户端链路对象 https://segmentfault.com/a/1190000014546372 196 | func (trace *Opentracing) newClientSpanFromContext(ctx context.Context, tracer opentracing.Tracer, fullMethodName string) (context.Context, opentracing.Span) { 197 | // 从context中获取spanContext,如果上层没有开启追踪,则这里新建一个 198 | // 追踪,如果上层已经有了,测创建子span. 199 | var parentCtx opentracing.SpanContext 200 | if parent := opentracing.SpanFromContext(ctx); parent != nil { 201 | parentCtx = parent.Context() 202 | } 203 | cliSpan := tracer.StartSpan( 204 | fullMethodName, 205 | opentracing.ChildOf(parentCtx), 206 | grpcTag, 207 | ext.SpanKindRPCClient, 208 | ) 209 | 210 | // 将之前放入context中的metadata数据取出,如果没有则新建一个metadata 211 | md, ok := metadata.FromOutgoingContext(ctx) 212 | if !ok { 213 | md = metadata.New(nil) 214 | } else { 215 | md = md.Copy() 216 | } 217 | mdWriter := MDReaderWriter{md} 218 | 219 | // 将追踪数据注入到metadata中 220 | err := tracer.Inject(cliSpan.Context(), opentracing.TextMap, mdWriter) 221 | if err != nil { 222 | trace.Options.Logger.Errorw("将追踪数据注入到metadata中错误", "err", err, "md", md, "fullMethodName", fullMethodName) 223 | } 224 | // 将metadata数据装入context中 225 | ctx = metadata.NewOutgoingContext(ctx, md) 226 | return ctx, cliSpan 227 | } 228 | -------------------------------------------------------------------------------- /plugins/middleware/opentracing/options.go: -------------------------------------------------------------------------------- 1 | package opentracing 2 | 3 | import ( 4 | "github.com/micro-kit/microkit/plugins/middleware" 5 | "github.com/opentracing/opentracing-go" 6 | "go.uber.org/zap" 7 | ) 8 | 9 | // Option 实例值设置 10 | type Option func(*Options) 11 | 12 | // Options 注册相关参数 13 | type Options struct { 14 | FilterOutFunc middleware.FilterFunc 15 | Logger *zap.SugaredLogger 16 | Tracer opentracing.Tracer 17 | } 18 | 19 | // Logger 设置日志对象 20 | func Logger(logger *zap.SugaredLogger) Option { 21 | return func(o *Options) { 22 | o.Logger = logger 23 | } 24 | } 25 | 26 | // Tracer 链路追踪客户端 27 | func Tracer(tracer opentracing.Tracer) Option { 28 | return func(o *Options) { 29 | o.Tracer = tracer 30 | } 31 | } 32 | 33 | // FilterOutFunc 设置中间件忽略函数列表 34 | func FilterOutFunc(filterOutFunc middleware.FilterFunc) Option { 35 | return func(o *Options) { 36 | o.FilterOutFunc = filterOutFunc 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /plugins/middleware/prometheus/README.md: -------------------------------------------------------------------------------- 1 | # 备注 2 | 3 | 直接使用 github.com/grpc-ecosystem/go-grpc-prometheus 实现 4 | 5 | 6 | ## docker-compose.yml 7 | ``` 8 | version: '2' 9 | 10 | services: 11 | prometheus: 12 | image: prom/prometheus 13 | ports: 14 | - 9090:9090 15 | volumes: 16 | - ./prometheus.yml:/etc/prometheus/prometheus.yml 17 | grafana: 18 | image: grafana/grafana 19 | ports: 20 | - 3000:3000 21 | environment: 22 | - GF_SECURITY_ADMIN_PASSWORD=password 23 | volumes: 24 | - $PWD/extra/grafana_db:/var/lib/grafana grafana/grafana 25 | 26 | ``` -------------------------------------------------------------------------------- /plugins/middleware/prometheus/options.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "github.com/micro-kit/microkit/plugins/middleware" 5 | ) 6 | 7 | /* 服务注册参数 */ 8 | 9 | // Option 实例值设置 10 | type Option func(*Options) 11 | 12 | // Options 注册相关参数 13 | type Options struct { 14 | Enable bool // 是否启用监控 15 | FilterOutFunc middleware.FilterFunc 16 | } 17 | 18 | // Enable 是否启用普罗米修斯指标采集中间件 19 | func Enable(enable bool) Option { 20 | return func(o *Options) { 21 | o.Enable = enable 22 | } 23 | } 24 | 25 | // FilterOutFunc 设置中间件忽略函数列表 26 | func FilterOutFunc(filterOutFunc middleware.FilterFunc) Option { 27 | return func(o *Options) { 28 | o.FilterOutFunc = filterOutFunc 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /plugins/middleware/prometheus/prometheus.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "context" 5 | 6 | grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" 7 | "github.com/micro-kit/microkit/plugins/middleware" 8 | "google.golang.org/grpc" 9 | ) 10 | 11 | /* 普罗米修斯指标监控中间件 */ 12 | 13 | // Prometheus 普罗米修斯指标监控中间件 14 | type Prometheus struct { 15 | Options *Options 16 | } 17 | 18 | func NewPrometheus(opts ...Option) middleware.Middleware { 19 | p := &Prometheus{ 20 | Options: new(Options), 21 | } 22 | // 配置 23 | configure(p, opts...) 24 | 25 | return p 26 | } 27 | 28 | // 配置设置项 29 | func configure(p *Prometheus, ops ...Option) { 30 | // 处理设置参数 31 | for _, o := range ops { 32 | o(p.Options) 33 | } 34 | } 35 | 36 | /* 服务端拦截器 */ 37 | 38 | // UnaryHandler 非流式中间件 39 | func (p *Prometheus) UnaryHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { 40 | if p.Options.FilterOutFunc != nil && !p.Options.FilterOutFunc(ctx, info.FullMethod) { 41 | resp, err = handler(ctx, req) 42 | return 43 | } 44 | 45 | // github.com/grpc-ecosystem/go-grpc-prometheus 46 | resp, err = grpc_prometheus.UnaryServerInterceptor(ctx, req, info, handler) 47 | return 48 | } 49 | 50 | // StreamHandler 流式中间件 51 | func (p *Prometheus) StreamHandler(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) { 52 | if p.Options.FilterOutFunc != nil && !p.Options.FilterOutFunc(stream.Context(), info.FullMethod) { 53 | err = handler(srv, stream) 54 | return 55 | } 56 | // github.com/grpc-ecosystem/go-grpc-prometheus 57 | err = grpc_prometheus.StreamServerInterceptor(srv, stream, info, handler) 58 | return 59 | } 60 | 61 | /* 以下客户端拦截器 */ 62 | 63 | // UnaryClient 非流式客户端中间件 grpc.UnaryClientInterceptor 64 | func (p *Prometheus) UnaryClient(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) { 65 | if p.Options.FilterOutFunc != nil && !p.Options.FilterOutFunc(ctx, method) { 66 | err = invoker(ctx, method, req, reply, cc, opts...) 67 | return 68 | } 69 | 70 | // github.com/grpc-ecosystem/go-grpc-prometheus 71 | err = grpc_prometheus.UnaryClientInterceptor(ctx, method, req, reply, cc, invoker, opts...) 72 | return 73 | } 74 | 75 | // StreamClient 流式服客户中间件 grpc.StreamClientInterceptor 76 | func (p *Prometheus) StreamClient(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (cs grpc.ClientStream, err error) { 77 | if p.Options.FilterOutFunc != nil && !p.Options.FilterOutFunc(ctx, method) { 78 | return streamer(ctx, desc, cc, method, opts...) 79 | } 80 | // github.com/grpc-ecosystem/go-grpc-prometheus 81 | cs, err = grpc_prometheus.StreamClientInterceptor(ctx, desc, cc, method, streamer, opts...) 82 | return 83 | } 84 | -------------------------------------------------------------------------------- /plugins/middleware/tpl/options.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | import ( 4 | "github.com/micro-kit/microkit/plugins/middleware" 5 | "go.uber.org/zap" 6 | ) 7 | 8 | // Option 实例值设置 9 | type Option func(*Options) 10 | 11 | // Options 注册相关参数 12 | type Options struct { 13 | FilterOutFunc middleware.FilterFunc 14 | Logger *zap.SugaredLogger 15 | } 16 | 17 | // Logger 设置日志对象 18 | func Logger(logger *zap.SugaredLogger) Option { 19 | return func(o *Options) { 20 | o.Logger = logger 21 | } 22 | } 23 | 24 | // FilterOutFunc 设置中间件忽略函数列表 25 | func FilterOutFunc(filterOutFunc middleware.FilterFunc) Option { 26 | return func(o *Options) { 27 | o.FilterOutFunc = filterOutFunc 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /plugins/middleware/tpl/tpl.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/micro-kit/microkit/plugins/middleware" 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | /* 中间件 */ 11 | 12 | // Tpl 中间件 13 | type Tpl struct { 14 | Options *Options 15 | } 16 | 17 | // NewTpl 创建链路追踪器 18 | func NewTpl(opts ...Option) middleware.Middleware { 19 | tpl := &Tpl{ 20 | Options: new(Options), 21 | } 22 | // 配置 23 | configure(tpl, opts...) 24 | return tpl 25 | } 26 | 27 | // 配置设置项 28 | func configure(tpl *Tpl, ops ...Option) { 29 | // 处理设置参数 30 | for _, o := range ops { 31 | o(tpl.Options) 32 | } 33 | } 34 | 35 | // UnaryHandler 非流式中间件 36 | func (tpl *Tpl) UnaryHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { 37 | if tpl.Options.FilterOutFunc != nil && !tpl.Options.FilterOutFunc(ctx, info.FullMethod) { 38 | resp, err = handler(ctx, req) 39 | return 40 | } 41 | 42 | // 执行下一步 43 | resp, err = handler(ctx, req) 44 | return 45 | } 46 | 47 | // StreamHandler 流式中间件 48 | func (tpl *Tpl) StreamHandler(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) { 49 | if tpl.Options.FilterOutFunc != nil && !tpl.Options.FilterOutFunc(stream.Context(), info.FullMethod) { 50 | err = handler(srv, stream) 51 | return 52 | } 53 | 54 | err = handler(srv, stream) 55 | return 56 | } 57 | 58 | // UnaryClient 非流式客户端中间件 grpc.UnaryClientInterceptor 59 | func (tpl *Tpl) UnaryClient(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) { 60 | if tpl.Options.FilterOutFunc != nil && !tpl.Options.FilterOutFunc(ctx, method) { 61 | err = invoker(ctx, method, req, reply, cc, opts...) 62 | return 63 | } 64 | 65 | // 执行下一步 66 | err = invoker(ctx, method, req, reply, cc, opts...) 67 | return 68 | } 69 | 70 | // StreamClient 流式服客户中间件 grpc.StreamClientInterceptor 71 | func (tpl *Tpl) StreamClient(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (cs grpc.ClientStream, err error) { 72 | if tpl.Options.FilterOutFunc != nil && !tpl.Options.FilterOutFunc(ctx, method) { 73 | return streamer(ctx, desc, cc, method, opts...) 74 | } 75 | 76 | cs, err = streamer(ctx, desc, cc, method, opts...) 77 | return 78 | } 79 | -------------------------------------------------------------------------------- /plugins/middleware/wrappers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Michal Witkowski. All Rights Reserved. 2 | // See LICENSE for licensing terms. 3 | 4 | // 来源 github.com/grpc-ecosystem/go-grpc-middleware 5 | 6 | package middleware 7 | 8 | import ( 9 | "context" 10 | 11 | "google.golang.org/grpc" 12 | ) 13 | 14 | // WrappedServerStream is a thin wrapper around grpc.ServerStream that allows modifying context. 15 | type WrappedServerStream struct { 16 | grpc.ServerStream 17 | // WrappedContext is the wrapper's own Context. You can assign it. 18 | WrappedContext context.Context 19 | } 20 | 21 | // Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context() 22 | func (w *WrappedServerStream) Context() context.Context { 23 | return w.WrappedContext 24 | } 25 | 26 | // WrapServerStream returns a ServerStream that has the ability to overwrite context. 27 | func WrapServerStream(stream grpc.ServerStream) *WrappedServerStream { 28 | if existing, ok := stream.(*WrappedServerStream); ok { 29 | return existing 30 | } 31 | return &WrappedServerStream{ServerStream: stream, WrappedContext: stream.Context()} 32 | } 33 | -------------------------------------------------------------------------------- /plugins/register/etcdv3/etcdv3.go: -------------------------------------------------------------------------------- 1 | package etcdv3 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "time" 10 | 11 | "github.com/micro-kit/microkit/plugins/register" 12 | "go.etcd.io/etcd/clientv3" 13 | "go.etcd.io/etcd/mvcc/mvccpb" 14 | "google.golang.org/grpc/resolver" 15 | ) 16 | 17 | /* 注册服务到etcd v3 */ 18 | 19 | // EtcdV3 注册服务到etcd v3 api的服务注册中间件 20 | type EtcdV3 struct { 21 | Options *register.Options // 注册服务配置 22 | cli *clientv3.Client // etcdv3 客户端 23 | srvKey string // 服务注册key 24 | node *register.Node // 注册节点信息 25 | cc resolver.ClientConn // grpc 客户端 26 | } 27 | 28 | // NewRegistry 创建一个etcdv3服务注册中间件 29 | func NewRegistry(ops ...register.Option) register.Register { 30 | etcdV3 := &EtcdV3{ 31 | Options: new(register.Options), 32 | } 33 | // 配置 34 | configure(etcdV3, ops...) 35 | // 创建etcd客户端对象 36 | err := etcdV3.initEtcdCli() 37 | if err != nil { 38 | etcdV3.Options.Logger.Fatalw("连接etcd服务错误", "err", err, "addrs", etcdV3.Options.Addrs) 39 | } 40 | return etcdV3 41 | } 42 | 43 | // 配置设置项 44 | func configure(etcdV3 *EtcdV3, ops ...register.Option) { 45 | for _, o := range ops { 46 | o(etcdV3.Options) 47 | } 48 | // 默认值 49 | if len(etcdV3.Options.Addrs) == 0 { 50 | etcdV3.Options.Addrs = append(etcdV3.Options.Addrs, "127.0.0.1:2379") 51 | } 52 | if etcdV3.Options.TTL <= 0 { 53 | etcdV3.Options.TTL = 10 54 | } 55 | } 56 | 57 | // 初始化etcd服务连接 58 | func (p *EtcdV3) initEtcdCli() (err error) { 59 | p.cli, err = clientv3.New(clientv3.Config{ 60 | Endpoints: p.Options.Addrs, 61 | DialTimeout: 10 * time.Second, 62 | }) 63 | if err != nil { 64 | p.Options.Logger.Errorw("连接etcd服务错误", "err", err, "addrs", p.Options.Addrs) 65 | log.Fatalln("连接etcd服务错误", err) 66 | return err 67 | } 68 | return nil 69 | } 70 | 71 | // Register 注册一个服务 72 | func (p *EtcdV3) Register(n *register.Node) error { 73 | if n == nil { 74 | return errors.New("服务注册信息不能为nil") 75 | } 76 | p.node = n 77 | // 定时器每隔多少秒续期一次 78 | ticker := time.NewTicker(time.Second * time.Duration(p.Options.TTL)) 79 | // 服务存储地址 - 服务名/服务id 80 | p.srvKey = p.keyPrefix(n) 81 | // 协程内定时续期 82 | go func() { 83 | for { 84 | getResp, err := p.cli.Get(context.Background(), p.srvKey) 85 | if err != nil { 86 | p.Options.Logger.Warnw("续期服务注册信息,查询错误", "err", err, "srvKey", p.srvKey) 87 | } else if getResp.Count == 0 { 88 | err = p.withAlive() 89 | if err != nil { 90 | p.Options.Logger.Warnw("续期服务注册信息,续期错误", "err", err, "srvKey", p.srvKey) 91 | } 92 | } else { 93 | // 存在则什么也做 94 | } 95 | <-ticker.C 96 | } 97 | }() 98 | return nil 99 | } 100 | 101 | // 获取服务存储key前缀 102 | func (p *EtcdV3) keyPrefix(n *register.Node) string { 103 | if n == nil { 104 | return fmt.Sprintf("microkit/services/%s/", p.Options.Name) 105 | } 106 | return fmt.Sprintf("microkit/services/%s/%s", p.Options.Name, n.Id) 107 | } 108 | 109 | // 续期 110 | func (p *EtcdV3) withAlive() error { 111 | leaseResp, err := p.cli.Grant(context.Background(), p.Options.TTL) 112 | if err != nil { 113 | return err 114 | } 115 | // 服务注册信息 - 包含node全部信息 116 | addr, err := json.Marshal(p.node) 117 | if err != nil { 118 | return err 119 | } 120 | 121 | // 写服务信息 122 | _, err = p.cli.Put(context.Background(), p.srvKey, string(addr), clientv3.WithLease(leaseResp.ID)) 123 | if err != nil { 124 | return err 125 | } 126 | _, err = p.cli.KeepAlive(context.Background(), leaseResp.ID) 127 | if err != nil { 128 | return err 129 | } 130 | return nil 131 | } 132 | 133 | // UnRegister 取消注册一个服务 134 | func (p *EtcdV3) UnRegister(n *register.Node) error { 135 | _, err := p.cli.Delete(context.Background(), p.srvKey) 136 | if err != nil { 137 | p.Options.Logger.Warnw("删除服务注册信息错误", "err", err, "srvKey", p.srvKey) 138 | } 139 | return err 140 | } 141 | 142 | // GetResolver 获取客户端发现 grpc Resolver对象 143 | func (p *EtcdV3) GetResolver() resolver.Resolver { 144 | return p 145 | } 146 | 147 | // GetBuilder 获取grpc服务注册Builder对象 148 | func (p *EtcdV3) GetBuilder() resolver.Builder { 149 | return p 150 | } 151 | 152 | // Build grpc resolver接口需要 - 创建一个服务注册中心中间件 153 | func (p *EtcdV3) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { 154 | var err error 155 | if p.cli == nil { 156 | // 创建etcd客户端对象 157 | err = p.initEtcdCli() 158 | if err != nil { 159 | p.Options.Logger.Fatalw("连接etcd服务错误", "err", err, "addrs", p.Options.Addrs) 160 | return nil, err 161 | } 162 | } 163 | p.cc = cc 164 | 165 | go p.watch(p.keyPrefix(nil)) 166 | return p, nil 167 | } 168 | 169 | // Scheme 相当于服务分类名 - 服务注册根路径 170 | func (p *EtcdV3) Scheme() string { 171 | schema := p.Options.Schema 172 | if schema == "" { 173 | schema = "default" 174 | } 175 | return schema 176 | } 177 | 178 | // ResolveNow 什么也不做 179 | func (p *EtcdV3) ResolveNow(rn resolver.ResolveNowOption) { 180 | // log.Println("ResolveNow") 181 | } 182 | 183 | // Close 关闭注册解析器 184 | func (p *EtcdV3) Close() { 185 | p.Options.Logger.Infow("关闭etcd服务解析器") 186 | } 187 | 188 | // watch 监听服务变化 189 | func (p *EtcdV3) watch(keyPrefix string) { 190 | var addrList []resolver.Address 191 | getResp, err := p.cli.Get(context.Background(), keyPrefix, clientv3.WithPrefix()) 192 | if err != nil { 193 | p.Options.Logger.Errorw("监听服务变化错误", "err", err, "keyPrefix", keyPrefix) 194 | } else { 195 | for _, kv := range getResp.Kvs { 196 | sn := new(register.Node) 197 | err = json.Unmarshal(kv.Value, sn) 198 | addrList = append(addrList, resolver.Address{Addr: sn.Advertise}) 199 | } 200 | } 201 | p.cc.NewAddress(addrList) 202 | rch := p.cli.Watch(context.Background(), keyPrefix, clientv3.WithPrefix()) 203 | for n := range rch { 204 | for _, ev := range n.Events { 205 | // 解析服务注册的地址信息 206 | sn := new(register.Node) 207 | err = json.Unmarshal(ev.Kv.Value, sn) 208 | if err != nil { 209 | p.Options.Logger.Errorw("服务注册信息解析错误", "err", err, "key", string(ev.Kv.Key), "value", string(ev.Kv.Value)) 210 | continue 211 | } 212 | // 客户端发现地址 213 | addr := sn.Advertise 214 | 215 | switch ev.Type { 216 | case mvccpb.PUT: 217 | if !exist(addrList, addr) { 218 | addrList = append(addrList, resolver.Address{Addr: addr}) 219 | p.cc.NewAddress(addrList) 220 | } 221 | case mvccpb.DELETE: 222 | if s, ok := remove(addrList, addr); ok { 223 | addrList = s 224 | p.cc.NewAddress(addrList) 225 | } 226 | } 227 | p.Options.Logger.Infow("监听到etcd服务变化", "type", ev.Type, "key", string(ev.Kv.Key), "value", string(ev.Kv.Value)) 228 | } 229 | } 230 | } 231 | 232 | // 解析器是否已存在 233 | func exist(l []resolver.Address, addr string) bool { 234 | for i := range l { 235 | if l[i].Addr == addr { 236 | return true 237 | } 238 | } 239 | return false 240 | } 241 | 242 | // 删除解析器 243 | func remove(s []resolver.Address, addr string) ([]resolver.Address, bool) { 244 | for i := range s { 245 | if s[i].Addr == addr { 246 | s[i] = s[len(s)-1] 247 | return s[:len(s)-1], true 248 | } 249 | } 250 | return nil, false 251 | } 252 | -------------------------------------------------------------------------------- /plugins/register/options.go: -------------------------------------------------------------------------------- 1 | package register 2 | 3 | import ( 4 | "crypto/tls" 5 | 6 | "go.uber.org/zap" 7 | ) 8 | 9 | /* 服务注册参数 */ 10 | 11 | // Option 实例值设置 12 | type Option func(*Options) 13 | 14 | // Options 注册相关参数 15 | type Options struct { 16 | Schema string // 服务注册根路径 17 | Name string // 服务名 18 | Addrs []string // 服务注册中间件地址 19 | TTL int64 // 注册信息生存有效期 20 | Secure bool // 是否启用安全连接 21 | TLSConfig *tls.Config // tls加密连接配置 22 | Logger *zap.SugaredLogger 23 | } 24 | 25 | // Addrs 服务注册中间件地址 26 | func Addrs(addrs ...string) Option { 27 | return func(o *Options) { 28 | o.Addrs = addrs 29 | } 30 | } 31 | 32 | // Secure 服务中间件是否使用加密连接 33 | func Secure(b bool) Option { 34 | return func(o *Options) { 35 | o.Secure = b 36 | } 37 | } 38 | 39 | // TLSConfig TLS 加密连接配置 40 | func TLSConfig(t *tls.Config) Option { 41 | return func(o *Options) { 42 | o.TLSConfig = t 43 | } 44 | } 45 | 46 | // TTL 注册信息生存有效期 47 | func TTL(t int64) Option { 48 | return func(o *Options) { 49 | o.TTL = t 50 | } 51 | } 52 | 53 | // Name 设置服务名 54 | func Name(n string) Option { 55 | return func(o *Options) { 56 | o.Name = n 57 | } 58 | } 59 | 60 | // Schema 服务注册根路径 61 | func Schema(s string) Option { 62 | return func(o *Options) { 63 | o.Schema = s 64 | } 65 | } 66 | 67 | // Logger 设置日志对象 68 | func Logger(logger *zap.SugaredLogger) Option { 69 | return func(o *Options) { 70 | o.Logger = logger 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /plugins/register/plugins.go: -------------------------------------------------------------------------------- 1 | package register 2 | 3 | import ( 4 | "google.golang.org/grpc/resolver" 5 | ) 6 | 7 | /* 服务插件定义 */ 8 | 9 | // Register 服务注册插件 10 | type Register interface { 11 | Register(n *Node) error // 注册一个服务 12 | UnRegister(n *Node) error // 取消注册一个服务 13 | GetResolver() resolver.Resolver // 获取服务名对应服务列表 14 | GetBuilder() resolver.Builder // 获取服务名对应服务列表 15 | } 16 | 17 | // Node 服务节点信息 18 | type Node struct { 19 | Id string `json:"id"` // 服务id 20 | Address string `json:"address"` // 服务地址ip或域名+端口 21 | Advertise string `json:"advertise"` // 服务注册发现地址 22 | Metadata map[string]string `json:"metadata"` // 服务注册附加信息,可以是系统资源信息 - TODO 当前采用轮训(grpc.WithBalancerName("round_robin"))后期可在此字段做文章 23 | } 24 | -------------------------------------------------------------------------------- /server/options.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/micro-kit/microkit/plugins/middleware" 5 | "github.com/micro-kit/microkit/plugins/register" 6 | ) 7 | 8 | /* 服务配置 */ 9 | 10 | const ( 11 | // 默认读写缓冲区 128KB 12 | defaultWriteBufSize = 128 * 1024 13 | defaultReadBufSize = 128 * 1024 14 | ) 15 | 16 | // Option 实例值设置 17 | type Option func(*Options) 18 | 19 | // Options 注册相关参数 20 | type Options struct { 21 | id string // 服务id 22 | serviceName string // 服务名 23 | address string // 监听地址 24 | advertise string // 服务注册地址 25 | middlewares []middleware.Middleware // 中间件 26 | reg register.Register // 注册中间件 27 | 28 | writeBufSize int // 写缓冲区 29 | readBufSize int // 读缓冲区 30 | 31 | metricsAddress string // 普罗米修斯监控信息接口 32 | } 33 | 34 | // Address 监听地址 35 | func Address(address string) Option { 36 | return func(o *Options) { 37 | o.address = address 38 | } 39 | } 40 | 41 | // Advertise 服务注册地址 42 | func Advertise(advertise string) Option { 43 | return func(o *Options) { 44 | o.advertise = advertise 45 | } 46 | } 47 | 48 | // Middleware 服务中间件 49 | func Middleware(middlewares ...middleware.Middleware) Option { 50 | return func(o *Options) { 51 | o.middlewares = middlewares 52 | } 53 | } 54 | 55 | // Register 服务注册中间件 56 | func Register(reg register.Register) Option { 57 | return func(o *Options) { 58 | o.reg = reg 59 | } 60 | } 61 | 62 | // ID 服务唯一id 63 | func ID(id string) Option { 64 | return func(o *Options) { 65 | o.id = id 66 | } 67 | } 68 | 69 | // ServiceName 服务名 70 | func ServiceName(serviceName string) Option { 71 | return func(o *Options) { 72 | o.serviceName = serviceName 73 | } 74 | } 75 | 76 | // WriteBufSize 写缓冲区 77 | func WriteBufSize(writeBufSize int) Option { 78 | return func(o *Options) { 79 | o.writeBufSize = writeBufSize 80 | } 81 | } 82 | 83 | // ReadBufSize 读缓冲区 84 | func ReadBufSize(readBufSize int) Option { 85 | return func(o *Options) { 86 | o.readBufSize = readBufSize 87 | } 88 | } 89 | 90 | // MetricsAddress 普罗米修斯监控信息接口 91 | func MetricsAddress(metricsAddress string) Option { 92 | return func(o *Options) { 93 | o.metricsAddress = metricsAddress 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "net" 7 | "net/http" 8 | 9 | "github.com/micro-kit/micro-common/config" 10 | "github.com/micro-kit/micro-common/logger" 11 | "github.com/micro-kit/microkit/internal/common" 12 | "github.com/micro-kit/microkit/plugins/middleware" 13 | "github.com/micro-kit/microkit/plugins/middleware/hystrixlimitter" 14 | zap "github.com/micro-kit/microkit/plugins/middleware/logger" 15 | "github.com/micro-kit/microkit/plugins/middleware/opentracing" 16 | "github.com/micro-kit/microkit/plugins/middleware/prometheus" 17 | "github.com/micro-kit/microkit/plugins/register" 18 | "github.com/prometheus/client_golang/prometheus/promhttp" 19 | "google.golang.org/grpc" 20 | ) 21 | 22 | /* 23 | 服务端 24 | 可以创建服务实例,只需传入部分简单配置即可启动服务 25 | */ 26 | 27 | var ( 28 | tracerCloser io.Closer // 链路追踪关闭 29 | ) 30 | 31 | // Server 用于构造grpc服务,中间件加载等通用代码,简化应用创建过程 32 | type Server struct { 33 | opts *Options 34 | } 35 | 36 | // NewDefaultServer 创建加载默认中间件服务 37 | func NewDefaultServer() (*Server, error) { 38 | serviceName := config.GetSvcName() 39 | // 存储默认选项 40 | opts := make([]middleware.Middleware, 0) 41 | // 日志中间件 42 | opts = append(opts, zap.NewZapLogger(nil, zap.Logger(logger.Logger))) 43 | // 链路追踪中间件 44 | tracer, closer, err := common.NewJaegerTracer(serviceName) 45 | if err != nil { 46 | return nil, err 47 | } 48 | tracerCloser = closer 49 | opts = append(opts, opentracing.NewOpentracing( 50 | opentracing.Logger(logger.Logger), 51 | opentracing.Tracer(tracer), 52 | )) 53 | // 监控中间件 54 | opts = append(opts, prometheus.NewPrometheus(prometheus.Enable(true))) 55 | // 熔断限流中间件 56 | opts = append(opts, hystrixlimitter.NewHystrixLimitter( 57 | hystrixlimitter.Type(hystrixlimitter.HystrixLimitterTypeServer), 58 | hystrixlimitter.Logger(logger.Logger), 59 | hystrixlimitter.ServiceName(serviceName), 60 | )) 61 | 62 | return NewServer(Middleware(opts...), MetricsAddress(":19999")) 63 | } 64 | 65 | // NewServer 创建一个grpc服务对象 66 | func NewServer(opts ...Option) (*Server, error) { 67 | s := &Server{ 68 | opts: new(Options), 69 | } 70 | // 配置 71 | configure(s, opts...) 72 | return s, nil 73 | } 74 | 75 | // 配置设置项 76 | func configure(s *Server, ops ...Option) { 77 | // 处理设置参数 78 | for _, o := range ops { 79 | o(s.opts) 80 | } 81 | // 默认值 82 | if s.opts.address == "" { 83 | s.opts.address = config.GetGRPCAddr() 84 | } 85 | if s.opts.advertise == "" { 86 | s.opts.advertise = config.GetGRPCAdvertiseAddr() 87 | } 88 | if s.opts.id == "" { 89 | s.opts.id = config.GetSvcID() 90 | } 91 | if s.opts.serviceName == "" { 92 | s.opts.serviceName = config.GetSvcName() 93 | } 94 | if s.opts.writeBufSize <= 0 { 95 | s.opts.writeBufSize = defaultWriteBufSize 96 | } 97 | if s.opts.readBufSize <= 0 { 98 | s.opts.readBufSize = defaultReadBufSize 99 | } 100 | if s.opts.reg == nil { 101 | // 默认注册到etcdv3 102 | s.opts.reg = common.NewServerEtcdRegister() 103 | } 104 | } 105 | 106 | // RegisterServer 注册服务回调 - 用于注册服务的grpc服务方法 107 | type RegisterServer func(grpcServer *grpc.Server) 108 | 109 | // Serve 启动服务 110 | func (s *Server) Serve(regServer ...RegisterServer) error { 111 | if len(regServer) == 0 { 112 | return errors.New("At least one RegisterServer passed in") 113 | } 114 | // 监听 115 | lis, err := net.Listen("tcp", s.opts.address) 116 | if err != nil { 117 | return err 118 | } 119 | // 组织流式调用和普通调用插件列表 120 | streamInterceptors := make([]grpc.StreamServerInterceptor, 0) 121 | unaryInterceptors := make([]grpc.UnaryServerInterceptor, 0) 122 | for _, v := range s.opts.middlewares { 123 | streamInterceptors = append(streamInterceptors, v.StreamHandler) 124 | unaryInterceptors = append(unaryInterceptors, v.UnaryHandler) 125 | } 126 | // 创建grpc server并设置中间件 127 | grpcServer := grpc.NewServer( 128 | grpc.StreamInterceptor(middleware.ChainStreamServer(streamInterceptors...)), 129 | grpc.UnaryInterceptor(middleware.ChainUnaryServer(unaryInterceptors...)), 130 | grpc.WriteBufferSize(s.opts.writeBufSize), 131 | grpc.ReadBufferSize(s.opts.readBufSize), 132 | ) 133 | // 注册grpc服务实现 134 | for _, regSrv := range regServer { 135 | regSrv(grpcServer) 136 | } 137 | // 注册服务 138 | err = s.Register() 139 | if err != nil { 140 | return err 141 | } 142 | // 启动普罗米修斯监控接口 143 | go s.Metrics() 144 | 145 | logger.Logger.Infow("启动服务", "service_name", s.opts.serviceName, "id", s.opts.id, "advertise", s.opts.advertise) 146 | // 启动服务 147 | return grpcServer.Serve(lis) 148 | } 149 | 150 | // Register 注册服务 151 | func (s *Server) Register() error { 152 | return s.opts.reg.Register(®ister.Node{ 153 | Id: s.opts.id, 154 | Address: s.opts.address, 155 | Advertise: s.opts.advertise, 156 | }) 157 | } 158 | 159 | // UnRegister 取消注册信息 160 | func (s *Server) UnRegister() error { 161 | if tracerCloser != nil { 162 | tracerCloser.Close() 163 | } 164 | return s.opts.reg.UnRegister(®ister.Node{ 165 | Id: s.opts.id, 166 | Address: s.opts.address, 167 | Advertise: s.opts.advertise, 168 | }) 169 | } 170 | 171 | // Stop 停止服务 172 | func (s *Server) Stop() error { 173 | err := s.UnRegister() 174 | if err != nil { 175 | return err 176 | } 177 | return nil 178 | } 179 | 180 | // Metrics 普罗米修斯监控信息接口 181 | func (s *Server) Metrics() { 182 | if s.opts.metricsAddress != "" { 183 | http.Handle("/metrics", promhttp.Handler()) 184 | http.ListenAndServe(s.opts.metricsAddress, nil) 185 | } 186 | } 187 | --------------------------------------------------------------------------------