一个限流中间件的例子
func TestRateClientLimit(t *testing.T) {
b := ratelimit.NewBucketWithRate(float64(limit), int64(limit))
//...
c := client.NewClient(
// set the selector
client.Selector(s),
// add the breaker wrapper
client.Wrap(NewClientWrapper(b, false)),
)
//...
}
- 创建中间件 - NewBucketWithRate()
- 转成 Wrapper - NewClientWrapper(NewBucketWithRate())
- 转成 Option - Wrap(NewClientWrapper(NewBucketWithRate()))
- 转成 Options 并创建 Client - NewClient(client.Wrap(NewClientWrapper(NewBucketWithRate())))
创建中间件
b := ratelimit.NewBucketWithRate(float64(limit), int64(limit))
func limit(b *ratelimit.Bucket, wait bool, errId string) func() error {
return func() error {
if wait {
time.Sleep(b.Take(1))
} else if b.TakeAvailable(1) == 0 {
return errors.New(errId, "too many request", 429)
}
return nil
}
}
limit() 的返回值是 func() error 类型的函数,限流作用的中间件.
中间件转成 Wrapper
type Wrapper func(Client) Client
type clientWrapper struct {
fn func() error
client.Client
}
func NewClientWrapper(b *ratelimit.Bucket, wait bool) client.Wrapper {
fn := limit(b, wait, "go.micro.client")
return func(c client.Client) client.Client {
return &clientWrapper{fn, c}
}
}
NewClientWrapper() 的输入参数和返回值都是 client.Wrapper 类型,它是个包装函数(修饰者模式). 在输入对象上加个函数类型的字段(这里是限流的中间件),再把它返回.
Wrapper 转成 Option
type Options struct {
//...
Wrappers []Wrapper
//...
}
type Option func(*Options)
func Wrap(w Wrapper) Option {
return func(o *Options) {
o.Wrappers = append(o.Wrappers, w)
}
}
理解 Option 是重点,他是函数类型 func(*Options),没有返回值,用来设置传入的参数 Options.
Wrap() 函数把 Wrapper 类型转换成 Option 类型.这里 Option 是闭包(带数据的函数),它带有 Wrap() 函数的局部变量 w. 用它调用哪个 Options 就会把这个 w 添加到 Options 的 []Wrapper 切片里.这相当于用这个 Option 去设置了 Options.这或许正是它叫 Option 的原因.
用 Option 创建 Client, 用 Wrapper 包装 Client
func NewClient(opt ...Option) Client {
return newRpcClient(opt...)
}
func newRpcClient(opt ...Option) Client {
opts := newOptions(opt...) // 用 Option 创建 Options
rc := &rpcClient{ // 用Options 创建 rpcClient
once: sync.Once{},
opts: opts,
pool: newPool(opts.PoolSize, opts.PoolTTL),
seq: 0,
}
c := Client(rc) //rpcClient 转成 Client
for i := len(opts.Wrappers); i > 0; i-- {
c = opts.Wrappers[i-1](c) //Wrapper 包装 Clinet
}
return c
}
func newOptions(options ...Option) Options {
opts := Options{ //新建 Optoins
//...
}
for _, o := range options {
o(&opts) //用 Option 设置 Options
}
//...
return opts
}
调用链如下
client.NewClient(opt ...Option) Client
client.newRpcClient(opt ...Option) Client
client.newOptions(options ...Option) Options
rc := &rpcClient{
opts: opts,
}
c := Client(rc)
c = opts.Wrappers[i-1](c)
Option 经过 client.NewClient() -> client.newRpcClient() -> client.newOptions() 调用链传给 newOption().newOption() 创建 Options 实例并用传进来的 Option 设置 Options,然后返回 Options. newRpcClient() 获得 Options 后,用它创建 Client,最后用 Options 里的 Wrapper 包装 Client.
如果有多个 Wrapper 就会层层包装 Client, 形成中间件洋葱模型.
+---------------------------+
| middleware1 |
| +---------------------+ |
| | middleware2 | |
| | +---------------+ | |
| | | middleware3 | | |
| | | +---------+ | | |
| | | | Client | | | |
| | | +---------+ | | |
| | +---------------+ | |
| +---------------------+ |
+---------------------------+