micro source code analysis of microservice architecture middleware loading

Keywords: github

An example of current limiting Middleware

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)),
		)
        //...
}
  • Create Middleware - NewBucketWithRate()
  • Convert to Wrapper - NewClientWrapper(NewBucketWithRate())
  • Convert to Option - Wrap(NewClientWrapper(NewBucketWithRate()))
  • Convert to Options and create client - newclient (client. Wrap (newclientwrapper (newbucketwithrate()))

Create Middleware

b := ratelimit.NewBucketWithRate(float64(limit), int64(limit))

Middleware to Wrapper

type Wrapper func(Client) Client

type clientWrapper struct {
	fn func() error
	client.Client
}

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

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

The return value of limit() is a function of type func() error, which is a middleware for current limiting.
The input parameter and return value of NewClientWrapper() are both of client.Wrapper type, which is a wrapper function (decorator mode). Add a function type field to the input type object (in this case, flow limiting Middleware), and then return it.

Wrapper to 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)
	}
}

The key is to understand Option, which is function type func(*Options).
The Wrap() function converts the Wrapper type to Option type. Here Option is a closure, which has a local variable W. which Option it calls will add this w to the [] Wrapper slice of Options. This is equivalent to setting Options with this Option. This may be the reason why it is called Option.

Create Client with Option and wrap Client with Wrapper

func NewClient(opt ...Option) Client {
	return newRpcClient(opt...)
}

func newRpcClient(opt ...Option) Client {
	opts := newOptions(opt...)	// Creating Options with Option

	rc := &rpcClient{	// Creating rpcClient with Options
		once: sync.Once{},
		opts: opts,
		pool: newPool(opts.PoolSize, opts.PoolTTL),
		seq:  0,
	}

	c := Client(rc)	//rpcClient to Client

	for i := len(opts.Wrappers); i > 0; i-- {
		c = opts.Wrappers[i-1](c)	//Wrapper wrapper Clinet
	}

	return c
}

func newOptions(options ...Option) Options {
	opts := Options{	//New Optoins
		//...
	}
	for _, o := range options {	
		o(&opts)		//Set Options with Option
	}
	//...
	return opts
}

The call chain is as follows

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)

Options go through the call chain of Client. Newclient() - > Client. Newrpcclient() - > Client. Newoptions(), create options in newOption(), set options with the Option passed in, return Options. newRpcClient() to get options, use it to create a Client, and finally wrap the Clinet with the Wrapper in options.

If there are multiple wrappers, the Client will be packaged layer by layer to form a middleware onion model.

            +-------------------------------------+
            |            middleware1              |
            |    +---------------------------+    |
            |    |       middleware2         |    |
            |    |    +-----------------+    |    |
            |    |    |  middleware3    |    |    |
            |    |    |  +---------+    |    |    |
            |    |    |  |         |    |    |    |
            |    |    |  | Client  |    |    |    |
            |    |    |  +---------+    |    |    |
            |    |    +-----------------+    |    |
            |    +---------------------------+    |
            +-------------------------------------+

Reference resources

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

Posted by xstevey_bx on Sat, 02 Nov 2019 21:52:02 -0700