今天看到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