Skip to content

Commit

Permalink
retry: 增加闭包 Retry 方法,避免用户重复写类似的垃圾代码
Browse files Browse the repository at this point in the history
  • Loading branch information
flycash committed Nov 2, 2024
1 parent 44dea05 commit a4ff9fc
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 0 deletions.
4 changes: 4 additions & 0 deletions internal/errs/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ func NewErrInvalidIntervalValue(interval time.Duration) error {
func NewErrInvalidMaxIntervalValue(maxInterval, initialInterval time.Duration) error {
return fmt.Errorf("ekit: 最大重试间隔的时间 [%d] 应大于等于初始重试的间隔时间 [%d] ", maxInterval, initialInterval)
}

func NewErrRetryExhausted(lastErr error) error {
return fmt.Errorf("ekit: 超过最大重试次数,业务返回的最后一个 error %w", lastErr)
}
60 changes: 60 additions & 0 deletions retry/retry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2021 ecodeclub
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package retry

import (
"context"
"time"

"github.com/ecodeclub/ekit/internal/errs"
)

// Retry 会在以下条件满足的情况下返回:
// 1. 重试达到了最大次数,而后返回重试耗尽的错误
// 2. ctx 被取消或者超时
// 3. bizFunc 没有返回 error
// 而只要 bizFunc 返回 error,就会尝试重试
func Retry(ctx context.Context,
s Strategy,
bizFunc func() error) error {
var ticker *time.Ticker
defer func() {
if ticker != nil {
ticker.Stop()
}
}()
for {
err := bizFunc()
// 直接退出
if err == nil {
return nil
}
duration, ok := s.Next()
if !ok {
return errs.NewErrRetryExhausted(err)
}
if ticker == nil {
ticker = time.NewTicker(duration)
} else {
ticker.Reset(duration)
}
select {
case <-ctx.Done():
// 超时或者被取消了,直接返回
return ctx.Err()
case <-ticker.C:
}
}
}
84 changes: 84 additions & 0 deletions retry/retry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2021 ecodeclub
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package retry

import (
"context"
"errors"
"fmt"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestRetry(t *testing.T) {
bizErr := errors.New("biz error")
testCases := []struct {
name string
biz func() error
strategy Strategy
wantError error
}{
{
name: "第一次就成功",
biz: func() error {
t.Log("模拟业务")
return nil
},
strategy: func() Strategy {
res, _ := NewFixedIntervalRetryStrategy(time.Second, 3)
return res
}(),
},
{
name: "重试最终失败",
biz: func() error {
return bizErr
},
strategy: func() Strategy {
res, _ := NewFixedIntervalRetryStrategy(time.Second, 3)
return res
}(),
wantError: bizErr,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
err := Retry(ctx, tc.strategy, tc.biz)
assert.ErrorIs(t, err, tc.wantError)
})
}
}

func ExampleRetry() {
// 这是你的业务
bizFunc := func() error {
fmt.Print("hello, world")
return nil
}
strategy, _ := NewFixedIntervalRetryStrategy(time.Millisecond*100, 3)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
err := Retry(ctx, strategy, bizFunc)
if err != nil {
fmt.Println("error:", err)
}
// Output:
// hello, world
}

0 comments on commit a4ff9fc

Please sign in to comment.