【GO-Micro】micro 重試機制

github完整代碼地址 github.com/Allenxuxu/m…node

在分佈式系統中,常常會有服務出現故障,因此良好的重試機制能夠大大的提升系統的可用性。本文主要分析micro的客戶端重試機制,以及實例演示。git

micro 重試實現

micro框架提供方法設置客戶端重試的次數。github

Client.Init(
	client.Retries(3),
)
複製代碼

當client請求失敗時,客戶端會根據selector的策略選擇下一個節點重試請求。這樣當一個服務實例故障時,客戶端能夠自動調用另外一個實例。api

咱們來看看micro 客戶端內部重試的實現:bash

go-micro\client\rpc_client.goapp

func (r *rpcClient) Call(ctx context.Context, request Request, response interface{}, opts ...CallOption) error {
...
    //客戶端call 調用函數, 在下面的循環中調用
	call := func(i int) error {
		// call backoff first. Someone may want an initial start delay
		t, err := callOpts.Backoff(ctx, request, i)
		if err != nil {
			return errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
		}

		// only sleep if greater than 0
		if t.Seconds() > 0 {
			time.Sleep(t)
		}

		// 根據selector策略 選出 下一個節點
		node, err := next()
		if err != nil && err == selector.ErrNotFound {
			return errors.NotFound("go.micro.client", "service %s: %v", request.Service(), err.Error())
		} else if err != nil {
			return errors.InternalServerError("go.micro.client", "error getting next %s node: %v", request.Service(), err.Error())
		}

		// 客戶端調用
		err = rcall(ctx, node, request, response, callOpts)
		r.opts.Selector.Mark(request.Service(), node, err)
		return err
	}

	ch := make(chan error, callOpts.Retries+1)
	var gerr error
    //根據設定的**Retries**(重試次數)循環調用 call,若是執行成功,調用超時或者設置的**Retry**函數執行出錯則直接退出,不繼續重試
	for i := 0; i <= callOpts.Retries; i++ {
		go func(i int) {
			ch <- call(i)
		}(i)

		select {
		case <-ctx.Done(): //超時
			return errors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
		case err := <-ch:
			// if the call succeeded lets bail early
			if err == nil {  //調用成功
				return nil
			}

			retry, rerr := callOpts.Retry(ctx, request, i, err)
			if rerr != nil {
				return rerr
			}

			if !retry {
				return err
			}

			gerr = err
		}
	}

	return gerr
}
複製代碼

micro將選舉下一個節點,RPC調用封裝到一個匿名函數中,而後根據設定的重試次數循環調用。若是調用成功或者超時則直接返回,不繼續重試。其中,當callOpts裏設定的Retry函數執行失敗,即第一個返回值爲false,或者第二個返回值爲err不會nil時,也會退出循環直接返回。負載均衡

咱們來看下Retry是什麼:框架

type CallOptions struct {
	Retry RetryFunc
}
複製代碼

client的CallOptions中定義了Retry,咱們跳轉到RetryFunc分佈式

go-micro\client\retry.go函數

// note that returning either false or a non-nil error will result in the call not being retried
type RetryFunc func(ctx context.Context, req Request, retryCount int, err error) (bool, error) // RetryAlways always retry on error func RetryAlways(ctx context.Context, req Request, retryCount int, err error) (bool, error) {
	return true, nil
}

// RetryOnError retries a request on a 500 or timeout error
func RetryOnError(ctx context.Context, req Request, retryCount int, err error) (bool, error) {
	if err == nil {
		return false, nil
	}

	e := errors.Parse(err.Error())
	if e == nil {
		return false, nil
	}

	switch e.Code {
	// retry on timeout or internal server error
	case 408, 500:
		return true, nil
	default:
		return false, nil
	}
}

複製代碼

從中咱們能夠發現,做者預實現了兩個Retry函數:RetryAlwaysRetryOnErrorRetryAlways直接返回true, nil,即不退出重試。 RetryOnError只有當e.Code(上一次RPC調用結果)爲408或者500時纔會返回true, nil,繼續重試。 micro的默認RetryRetryOnError,可是咱們能夠自定義並設置,下面的實驗中將會演示。

DefaultRetry = RetryOnError
	// DefaultRetries is the default number of times a request is tried
	DefaultRetries = 1
	// DefaultRequestTimeout is the default request timeout
	DefaultRequestTimeout = time.Second * 5
複製代碼

實驗

當客戶端請求另外一個服務時,若是被請求的服務忽然掛了,而此時客戶端依舊會去請求,重試時客戶端會請求另外一個實例(有必定概率還會請求同一個實例,由於默認的負載均衡策略是哈希隨機)。

咱們修改api/user下的服務,在main函數中設置客戶端重試。

sClient := hystrixplugin.NewClientWrapper()(service.Options().Service.Client())
	sClient.Init(
		client.WrapCall(ocplugin.NewCallWrapper(t)),
		client.Retries(3),
		client.Retry(func(ctx context.Context, req client.Request, retryCount int, err error) (bool, error) {
			log.Log(req.Method(), retryCount, " client retry")
			return true, nil
		}),
	)
複製代碼

而後,咱們依次啓動 micro網關,user API服務,hello SRV服務(啓動兩個實例)。

cd micro && make run
cd api/user && make run
cd srv/hello && make run
cd srv/hello && make run
複製代碼

咱們經過kill -9 殺死其中一個hello服務,而後經過postman請求 GET 172.0.0.1:8080/user/test

[GIN] 2019/05/14 - 14:52:20 | 200 |    1.253576ms |       127.0.0.1 | GET      /user/test
2019/05/14 14:52:48 Received Say.Anything API request
2019/05/14 14:52:48 0x19a1680 0 retry func
2019/05/14 14:52:48 msg:"Hello xuxu"
[GIN] 2019/05/14 - 14:52:48 | 200 |   13.821193ms |       127.0.0.1 | GET      /user/test
複製代碼

經過usr API服務的輸出,咱們能夠看到重試一次後,客戶端成功請求了另外一個實例。

github完整代碼地址 github.com/Allenxuxu/m…

相關文章
相關標籤/搜索