gitee.com/sy_183/go-common@v1.0.5-0.20231205030221-958cfe129b47/lifecycle/retryable.go (about)

     1  package lifecycle
     2  
     3  import (
     4  	"gitee.com/sy_183/go-common/timer"
     5  	"sync/atomic"
     6  	"time"
     7  )
     8  
     9  const RetryableFieldName = "$retryable"
    10  
    11  type Retryable[LIFECYCLE Lifecycle] struct {
    12  	Lifecycle
    13  	lifecycle LIFECYCLE
    14  
    15  	interrupted bool
    16  
    17  	lazyStart     atomic.Bool
    18  	retryInterval atomic.Int64
    19  }
    20  
    21  func NewRetryable[LIFECYCLE Lifecycle](lifecycle LIFECYCLE) *Retryable[LIFECYCLE] {
    22  	r := &Retryable[LIFECYCLE]{
    23  		lifecycle: lifecycle,
    24  	}
    25  	lifecycle.SetField(RetryableFieldName, r)
    26  	r.Lifecycle = NewWithInterruptedStart(r.start)
    27  	return r
    28  }
    29  
    30  func (r *Retryable[LIFECYCLE]) Get() LIFECYCLE {
    31  	return r.lifecycle
    32  }
    33  
    34  func (r *Retryable[LIFECYCLE]) SetLazyStart(lazyStart bool) *Retryable[LIFECYCLE] {
    35  	r.lazyStart.Store(lazyStart)
    36  	return r
    37  }
    38  
    39  func (r *Retryable[LIFECYCLE]) SetRetryInterval(interval time.Duration) *Retryable[LIFECYCLE] {
    40  	r.retryInterval.Store(int64(interval))
    41  	return r
    42  }
    43  
    44  func (r *Retryable[LIFECYCLE]) start(_ Lifecycle, interrupter chan struct{}) (InterruptedRunFunc, error) {
    45  	var lazyStart bool
    46  
    47  	run := func(_ Lifecycle, interrupter chan struct{}) error {
    48  		var state State
    49  		runningFuture := make(ChanFuture[error], 1)
    50  		closedFuture := make(ChanFuture[error], 1)
    51  		startRetryTimer := timer.NewTimer(make(chan struct{}, 1))
    52  		defer func() {
    53  			r.interrupted = false
    54  			startRetryTimer.Stop()
    55  		}()
    56  
    57  		if lazyStart {
    58  			state = StateClosed
    59  			startRetryTimer.Trigger()
    60  		} else {
    61  			state = StateRunning
    62  			r.lifecycle.AddClosedFuture(closedFuture)
    63  		}
    64  
    65  		for {
    66  			select {
    67  			case <-startRetryTimer.C:
    68  				// 如果启动了定时器,说明生命周期组件一定为关闭状态,有中断信号则直接退出,所以此处一定没有标记中断,
    69  				// 此时需要启动组件并添加启动完成的追踪器到组件
    70  				r.lifecycle.AddStartedFuture(runningFuture)
    71  				state = StateStarting
    72  				r.lifecycle.Background()
    73  			case err := <-runningFuture:
    74  				// 生命周期组件启动完成,如果启动错误,在这种情况下如果标记了中断,则直接退出,否则启动定时器,定时器
    75  				// 触发后执行重启。如果启动成功,不管有没有中断信号,向组件添加关闭的追踪器
    76  				if err != nil {
    77  					state.ToClosed()
    78  					if r.interrupted {
    79  						return nil
    80  					}
    81  					startRetryTimer.After(time.Duration(r.retryInterval.Load()))
    82  					continue
    83  				}
    84  				state.ToRunning()
    85  				r.lifecycle.AddClosedFuture(closedFuture)
    86  			case <-closedFuture:
    87  				// 生命周期组件退出,如果标记了中断,则直接退出,否则启动定时器,定时器触发后执行重启
    88  				state.ToClosed()
    89  				if r.interrupted {
    90  					return nil
    91  				}
    92  				startRetryTimer.After(time.Duration(r.retryInterval.Load()))
    93  			case <-interrupter:
    94  				// 中断信号只会出现一次,如果此时生命周期组件为关闭状态,则直接退出,否则对组件执行关闭操作
    95  				r.interrupted = true
    96  				if state.Closed() {
    97  					return nil
    98  				}
    99  				r.lifecycle.Close(nil)
   100  			}
   101  		}
   102  	}
   103  
   104  	if !r.lazyStart.Load() {
   105  		r.lifecycle.Background()
   106  		for {
   107  			select {
   108  			case err := <-r.lifecycle.StartedWaiter():
   109  				if err != nil {
   110  					r.interrupted = false
   111  				}
   112  				return run, err
   113  			case <-interrupter:
   114  				r.interrupted = true
   115  				r.lifecycle.Close(nil)
   116  			}
   117  		}
   118  	}
   119  	lazyStart = true
   120  	return run, nil
   121  }