微服务框架 micro 源码分析 - 中间件的加载

本文详细介绍了如何使用限流中间件来控制客户端请求频率,包括创建限流中间件、将其转换为Wrapper和Option,以及如何将这些组件应用于Client创建过程中。通过示例代码展示了限流中间件的具体实现和其在客户端请求管理中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个限流中间件的例子

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  |  |  |  |
	|  |  |  +---------+  |  |  |
	|  |  +---------------+  |  |
	|  +---------------------+  |
	+---------------------------+

参考

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值