Kubernetes 系列 - 6. apiserver(三、请求处理超时机制)

130 阅读1分钟

6. apiserver(三、请求处理超时机制)

apiserver处理请求的时候存在超时机制,一旦处理请求超出时间限制(默认为1min),会直接返回timeout。

Kubernetes 系列 - 5. apiserver(二、创建处理链)中曾提到,apiserver的handler是用DefaultBuildHandlerChain构建的,其中和超时机制有关的部分为:

...
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc)

handler = genericapifilters.WithRequestDeadline(handler, c.AuditBackend, c.AuditPolicyRuleEvaluator,
    c.LongRunningFunc, c.Serializer, c.RequestTimeout)
...

6.1 启动“闹钟”

WithRequestDeadline跟踪下去的逻辑如下:

func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc) {
    ...
    c := &timerCtx{
       deadline: d,
    }
    ...
    dur := time.Until(d)
    if dur <= 0 {
       c.cancel(true, DeadlineExceeded, cause) // deadline has already passed
       return c, func() { c.cancel(false, Canceled, nil) }
    }
    c.mu.Lock()
    defer c.mu.Unlock()
    if c.err == nil {
       c.timer = time.AfterFunc(dur, func() {
          c.cancel(true, DeadlineExceeded, cause)
       })
    }
    return c, func() { c.cancel(true, Canceled, nil) }
}

关键在于time.AfterFunc,会启动一个timer协程,达到定时点之后会调用传入的函数,即cancel:

func AfterFunc(d Duration, f func()) *Timer {
    t := &Timer{
       r: runtimeTimer{
          when: when(d),
          f:    goFunc,
          arg:  f,
       },
    }
    startTimer(&t.r)
    return t
}

6.2 监听超时信号

当执行cancel之后会关闭timeoutCh通道,WithTimeoutForNonLongRunningRequests里面会读取这个通道判断是否超时:

func (t *timeoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    ...
    timeoutCh := r.Context().Done()
    ...
    go func() {
       defer func() {
          err := recover()
          // do not wrap the sentinel ErrAbortHandler panic value
          if err != nil && err != http.ErrAbortHandler {
             // Same as stdlib http server code. Manually allocate stack
             // trace buffer size to prevent excessively large logs
             const size = 64 << 10
             buf := make([]byte, size)
             buf = buf[:runtime.Stack(buf, false)]
             err = fmt.Sprintf("%v\n%s", err, buf)
          }
          resultCh <- err
       }()
       t.handler.ServeHTTP(w, rCopy)
    }()
    select {
    case err := <-resultCh:
       ...
    case <-timeoutCh:
       ...
    }
}