今天看到kite项目中的一段代码,发现挺有意思的。
// 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处理,是我之前都不知道的。
我之前的做法,自己启动一个 单独的 goroutine,对所有的token做遍历,判断是否timeout,timout了就进行删除操作。
看到了这段代码,第一个感觉是很妙,第二个是如果用起来,会不会有啥副作用。
翻看源码:https://golang.org/src/time/sleep.go?h=AfterFunc#L116
// 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在这里做了一层兼容各个平台的封装,应该是没有什么副作用啦。
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
不得不感慨,原生库还是有很多好东东的,需要自己慢慢发觉。