golang 中的定時器(timer),更巧妙的處理timeout

今天看到kite項目中的一段代碼,發現挺有意思的。html

 

// generateToken returns a JWT token string. Please see the URL for details:
// http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-13#section-4.1
func generateToken(aud, username, issuer, privateKey string) (string, error) {
    tokenCacheMu.Lock()
    defer tokenCacheMu.Unlock()

    uniqKey := aud + username + issuer // neglect privateKey, its always the same
    signed, ok := tokenCache[uniqKey]
    if ok {
        return signed, nil
    }

    tknID, err := uuid.NewV4()
    if err != nil {
        return "", errors.New("Server error: Cannot generate a token")
    }

    // Identifies the expiration time after which the JWT MUST NOT be accepted
    // for processing.
    ttl := TokenTTL

    // Implementers MAY provide for some small leeway, usually no more than
    // a few minutes, to account for clock skew.
    leeway := TokenLeeway

    tkn := jwt.New(jwt.GetSigningMethod("RS256"))
    tkn.Claims["iss"] = issuer                                       // Issuer
    tkn.Claims["sub"] = username                                     // Subject
    tkn.Claims["aud"] = aud                                          // Audience
    tkn.Claims["exp"] = time.Now().UTC().Add(ttl).Add(leeway).Unix() // Expiration Time
    tkn.Claims["nbf"] = time.Now().UTC().Add(-leeway).Unix()         // Not Before
    tkn.Claims["iat"] = time.Now().UTC().Unix()                      // Issued At
    tkn.Claims["jti"] = tknID.String()                               // JWT ID

    signed, err = tkn.SignedString([]byte(privateKey))
    if err != nil {
        return "", errors.New("Server error: Cannot generate a token")
    }

    // cache our token
    tokenCache[uniqKey] = signed

    // cache invalidation, because we cache the token in tokenCache we need to // invalidate it expiration time. This was handled usually within JWT, but // now we have to do it manually for our own cache. time.AfterFunc(TokenTTL-TokenLeeway, func() { tokenCacheMu.Lock() defer tokenCacheMu.Unlock() delete(tokenCache, uniqKey) }) return signed, nil
}

這裏的  time.AfterFunc 來作token的timeout處理,是我以前都不知道的。golang

我以前的作法,本身啓動一個 單獨的 goroutine,對全部的token作遍歷,判斷是否timeout,timout了就進行刪除操做。web

看到了這段代碼,第一個感受是很妙,第二個是若是用起來,會不會有啥反作用。json

翻看源碼:https://golang.org/src/time/sleep.go?h=AfterFunc#L116c#

// AfterFunc waits for the duration to elapse and then calls f
   114    // in its own goroutine. It returns a Timer that can
   115    // be used to cancel the call using its Stop method.
   116    func AfterFunc(d Duration, f func()) *Timer {
   117        t := &Timer{
   118            r: runtimeTimer{
   119                when: when(d),
   120                f:    goFunc,
   121                arg:  f,
   122            },
   123        }
   124        startTimer(&t.r) 125        return t
   126    }

這裏的startTimer 是用了系統自身的timer實現,只不過是golang在這裏作了一層兼容各個平臺的封裝,應該是沒有什麼反作用啦。less

14    // Interface to timers implemented in package runtime.
    15    // Must be in sync with ../runtime/runtime.h:/^struct.Timer$
    16    type runtimeTimer struct {
    17        i      int
    18        when   int64
    19        period int64
    20        f      func(interface{}, uintptr) // NOTE: must not be closure
    21        arg    interface{}
    22        seq    uintptr
    23    }
    24    
    25    // when is a helper function for setting the 'when' field of a runtimeTimer.
    26    // It returns what the time will be, in nanoseconds, Duration d in the future.
    27    // If d is negative, it is ignored.  If the returned value would be less than
    28    // zero because of an overflow, MaxInt64 is returned.
    29    func when(d Duration) int64 {
    30        if d <= 0 {
    31            return runtimeNano()
    32        }
    33        t := runtimeNano() + int64(d)
    34        if t < 0 {
    35            t = 1<<63 - 1 // math.MaxInt64
    36        }
    37        return t
    38    }
    39    
 40 func startTimer(*runtimeTimer) 41 func stopTimer(*runtimeTimer) bool

 

不得不感慨,原生庫仍是有不少好東東的,須要本身慢慢發覺。ide

相關文章
相關標籤/搜索