go micro wrapper 中间件

LogicAetherMaster
• 阅读 5491

go micro wrapper 中间件

本篇不涉及中间件底层是如何运作的,如有兴趣请见[micro server]篇

在options.go中有如下定义

// WrapClient is a convenience method for wrapping a Client with
// some middleware component. A list of wrappers can be provided.
// Wrappers are applied in reverse order so the last is executed first.
func WrapClient(w ...client.Wrapper) Option {
    return func(o *Options) {
        // apply in reverse
        for i := len(w); i > 0; i-- {
            o.Client = w[i-1](o.Client)
        }
    }
}

// WrapCall is a convenience method for wrapping a Client CallFunc
func WrapCall(w ...client.CallWrapper) Option {
    return func(o *Options) {
        o.Client.Init(client.WrapCall(w...))
    }
}

// WrapHandler adds a handler Wrapper to a list of options passed into the server
func WrapHandler(w ...server.HandlerWrapper) Option {
    return func(o *Options) {
        var wrappers []server.Option

        for _, wrap := range w {
            wrappers = append(wrappers, server.WrapHandler(wrap))
        }

        // Init once
        o.Server.Init(wrappers...)
    }
}

// WrapSubscriber adds a subscriber Wrapper to a list of options passed into the server
func WrapSubscriber(w ...server.SubscriberWrapper) Option {
    return func(o *Options) {
        var wrappers []server.Option

        for _, wrap := range w {
            wrappers = append(wrappers, server.WrapSubscriber(wrap))
        }

        // Init once
        o.Server.Init(wrappers...)
    }
}

这些参数设置用于micro.NewService()参数

有以下几种类型

  1. WrapClient() 用于用一些中间件组件包装Client,包装器以相反的顺序应用,因此最后一个先执行。
  2. WrapCall() 用于方便包装Client CallFunc
  3. WrapHandler() 将一系列handler中间件传给server,并初始化
  4. WrapSubscriber() 将一系列subscriber中间件传给server,并初始化

在server/wrapper.go中,定义了wrapper

// HandlerFunc represents a single method of a handler. It's used primarily
// for the wrappers. What's handed to the actual method is the concrete
// request and response types.
type HandlerFunc func(ctx context.Context, req Request, rsp interface{}) error

// SubscriberFunc represents a single method of a subscriber. It's used primarily
// for the wrappers. What's handed to the actual method is the concrete
// publication message.
type SubscriberFunc func(ctx context.Context, msg Message) error

// HandlerWrapper wraps the HandlerFunc and returns the equivalent
type HandlerWrapper func(HandlerFunc) HandlerFunc

// SubscriberWrapper wraps the SubscriberFunc and returns the equivalent
type SubscriberWrapper func(SubscriberFunc) SubscriberFunc

// StreamWrapper wraps a Stream interface and returns the equivalent.
// Because streams exist for the lifetime of a method invocation this
// is a convenient way to wrap a Stream as its in use for trace, monitoring,
// metrics, etc.
type StreamWrapper func(Stream) Stream

服务端主要是

  1. HandlerWrapper() 用于handler处理器的中间件
  2. SubscriberWrapper() 用于订阅的中间件
  3. StreamWrapper() 用于流的中间件

在client/wrapper.go中,定义了client的中间件

// CallFunc represents the individual call func
type CallFunc func(ctx context.Context, node *registry.Node, req Request, rsp interface{}, opts CallOptions) error

// CallWrapper is a low level wrapper for the CallFunc
type CallWrapper func(CallFunc) CallFunc

// Wrapper wraps a client and returns a client
type Wrapper func(Client) Client

// StreamWrapper wraps a Stream and returns the equivalent
type StreamWrapper func(Stream) Stream

客户端主要是

  1. CallWrapper() client call中间件
  2. Wrapper() client中间件
  3. StreamWrapper() 流中间件

下面是一个client 中间件 Wrapper 的例子

type logWrapper struct {
    client.Client
}

func (l *logWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
    md, _ := metadata.FromContext(ctx)
    fmt.Printf("[Log Wrapper] ctx: %v service: %s method: %s\n", md, req.Service(), req.Endpoint())
    return l.Client.Call(ctx, req, rsp)
}

func NewLogWrapper(c client.Client) client.Client {
    return &logWrapper{c}
}

NewLogWrapper()实现了type Wrapper func(Client) Client

下面是一个 handler wrapper的例子

func NewLogWrapper(fn server.HandlerFunc) server.HandlerFunc {
    return func(ctx context.Context, req server.Request, rsp interface{}) error {
        log.Printf("[Log Wrapper] Before serving request method: %v", req.Endpoint())
        err := fn(ctx, req, rsp)
        log.Printf("[Log Wrapper] After serving request")
        return err
    }
}

NewLogWrapper()实现了type HandlerWrapper func(HandlerFunc) HandlerFunc

HandlerFunc定义是type HandlerFunc func(ctx context.Context, req Request, rsp interface{}) error

所以我们看到中间件都是函数里return的写法

具体实例可以参考官方plugins中的opentracing wrapper写法

https://github.com/micro/go-p...

这里包括

  1. client.Client定义
  2. NewClientWrapper
  3. NewCallWrapper
  4. NewHandlerWrapper
  5. NewSubscriberWrapper

如果你想要实现自己的中间件,这里的官方实现是个不错的参考

Service Wrapper

很多时候需要从处理程序内部访问服务。Service Wrapper就是方式之一。它将服务嵌入到ctx中,这样就可以在handler中使用它。

srv := micro.NewService(
    micro.Name("com.example.srv.foo"),
)

srv.Init(
    micro.WrapClient(service.NewClientWrapper(srv))
    micro.WrapHandler(service.NewHandlerWrapper(srv)),
)

在srv.Init()中添加wrapper

在handler中可以这样使用service

func (e *Example) Handler(ctx context.Context, req *example.Request, rsp *example.Response) error {
    service, ok := micro.FromContext(ctx)
    if !ok {
        return errors.InternalServerError("com.example.srv.foo", "Could not retrieve service")
    }

    // do something with the service
    fmt.Println("Got service", service)
    return nil
}

在client wrapper中也可以使用

type myWrapper struct {
    client.Client
}

func (m *myWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
    service, ok = micro.FromContext(ctx)
    if !ok {
        return errors.InternalServerError("com.example.srv.foo.mywrapper", "Could not retrieve service")
    }

    // do something with the service
    fmt.Println("Got service", service)

    // now do some call
    return c.Client.Call(ctx, req, rsp, opts...)
}

go micro 分析系列文章
go micro server 启动分析
go micro client
go micro broker
go micro cmd
go micro config
go micro store
go micro registry
go micro router
go micro runtime
go micro transport
go micro web
go micro registry 插件consul
go micro plugin
go micro jwt 网关鉴权
go micro 链路追踪
go micro 熔断与限流
go micro wrapper 中间件
go micro metrics 接入Prometheus、Grafana

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
10个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
4年前
Python+Selenium自动化篇
本篇文字主要学习selenium定位页面元素的集中方法,以百度首页为例子。0.元素定位方法主要有:id定位:find\_element\_by\_id('')name定位:find\_element\_by\_name('')class定位:find\_element\_by\_class\_name(''
Wesley13 Wesley13
4年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Stella981 Stella981
4年前
Nginx在Windows平台的配置与使用
<divclass"output\_wrapper"id"output\_wrapper\_id"style"fontsize:16px;color:rgb(62,62,62);lineheight:1.6;wordspacing:0px;letterspacing:0px;fontfamily:'Helvet
Wesley13 Wesley13
4年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0