@@ -3,11 +3,14 @@ package wechat | |||||
import ( | import ( | ||||
"code.gitea.io/gitea/modules/redis/redis_client" | "code.gitea.io/gitea/modules/redis/redis_client" | ||||
"code.gitea.io/gitea/modules/redis/redis_key" | "code.gitea.io/gitea/modules/redis/redis_key" | ||||
"code.gitea.io/gitea/modules/redis/redis_lock" | |||||
"time" | "time" | ||||
) | ) | ||||
const EMPTY_REDIS_VAL = "Nil" | const EMPTY_REDIS_VAL = "Nil" | ||||
var accessTokenLock = redis_lock.NewDistributeLock() | |||||
func GetWechatAccessToken() string { | func GetWechatAccessToken() string { | ||||
token, _ := redis_client.Get(redis_key.WechatAccessTokenKey()) | token, _ := redis_client.Get(redis_key.WechatAccessTokenKey()) | ||||
if token != "" { | if token != "" { | ||||
@@ -17,14 +20,37 @@ func GetWechatAccessToken() string { | |||||
live, _ := redis_client.TTL(redis_key.WechatAccessTokenKey()) | live, _ := redis_client.TTL(redis_key.WechatAccessTokenKey()) | ||||
//refresh wechat access token when expire time less than 5 minutes | //refresh wechat access token when expire time less than 5 minutes | ||||
if live > 0 && live < 300 { | if live > 0 && live < 300 { | ||||
refreshAccessTokenCache() | |||||
refreshAccessToken() | |||||
} | } | ||||
return token | return token | ||||
} | } | ||||
return refreshAccessTokenCache() | |||||
return refreshAndGetAccessToken() | |||||
} | |||||
func refreshAccessToken() { | |||||
if ok := accessTokenLock.Lock(redis_key.AccessTokenLockKey(), 3*time.Second); ok { | |||||
defer accessTokenLock.UnLock(redis_key.AccessTokenLockKey()) | |||||
callAccessTokenAndUpdateCache() | |||||
} | |||||
} | |||||
func refreshAndGetAccessToken() string { | |||||
if ok := accessTokenLock.LockWithWait(redis_key.AccessTokenLockKey(), 3*time.Second, 3*time.Second); ok { | |||||
defer accessTokenLock.UnLock(redis_key.AccessTokenLockKey()) | |||||
token, _ := redis_client.Get(redis_key.WechatAccessTokenKey()) | |||||
if token != "" { | |||||
if token == EMPTY_REDIS_VAL { | |||||
return "" | |||||
} | |||||
return token | |||||
} | |||||
return callAccessTokenAndUpdateCache() | |||||
} | |||||
return "" | |||||
} | } | ||||
func refreshAccessTokenCache() string { | |||||
func callAccessTokenAndUpdateCache() string { | |||||
r := callAccessToken() | r := callAccessToken() | ||||
var token string | var token string | ||||
@@ -36,9 +62,6 @@ func refreshAccessTokenCache() string { | |||||
redis_client.Setex(redis_key.WechatAccessTokenKey(), EMPTY_REDIS_VAL, 10*time.Second) | redis_client.Setex(redis_key.WechatAccessTokenKey(), EMPTY_REDIS_VAL, 10*time.Second) | ||||
return "" | return "" | ||||
} | } | ||||
redis_client.Setex(redis_key.WechatAccessTokenKey(), token, time.Duration(r.Expires_in)*time.Second) | redis_client.Setex(redis_key.WechatAccessTokenKey(), token, time.Duration(r.Expires_in)*time.Second) | ||||
return token | return token | ||||
} | } |
@@ -6,7 +6,7 @@ func GetWechatQRCode4Bind(sceneStr string) *QRCodeResponse { | |||||
result, retryFlag := callQRCodeCreate(sceneStr) | result, retryFlag := callQRCodeCreate(sceneStr) | ||||
if retryFlag { | if retryFlag { | ||||
log.Info("retry wechat qr-code calling,sceneStr=%s", sceneStr) | log.Info("retry wechat qr-code calling,sceneStr=%s", sceneStr) | ||||
refreshAccessTokenCache() | |||||
refreshAccessToken() | |||||
result, _ = callQRCodeCreate(sceneStr) | result, _ = callQRCodeCreate(sceneStr) | ||||
} | } | ||||
return result | return result | ||||
@@ -9,7 +9,6 @@ import ( | |||||
"time" | "time" | ||||
) | ) | ||||
//todo redis连接池 | |||||
func Setex(key, value string, timeout time.Duration) (bool, error) { | func Setex(key, value string, timeout time.Duration) (bool, error) { | ||||
redisClient := labelmsg.Get() | redisClient := labelmsg.Get() | ||||
defer redisClient.Close() | defer redisClient.Close() | ||||
@@ -26,6 +25,22 @@ func Setex(key, value string, timeout time.Duration) (bool, error) { | |||||
} | } | ||||
func Setnx(key, value string, timeout time.Duration) (bool, error) { | |||||
redisClient := labelmsg.Get() | |||||
defer redisClient.Close() | |||||
seconds := int(math.Floor(timeout.Seconds())) | |||||
reply, err := redisClient.Do("SET", key, value, "NX", "EX", seconds) | |||||
if err != nil { | |||||
return false, err | |||||
} | |||||
if reply != "OK" { | |||||
return false, nil | |||||
} | |||||
return true, nil | |||||
} | |||||
func Get(key string) (string, error) { | func Get(key string) (string, error) { | ||||
redisClient := labelmsg.Get() | redisClient := labelmsg.Get() | ||||
defer redisClient.Close() | defer redisClient.Close() | ||||
@@ -9,3 +9,6 @@ func WechatBindingUserIdKey(sceneStr string) string { | |||||
func WechatAccessTokenKey() string { | func WechatAccessTokenKey() string { | ||||
return KeyJoin(PREFIX, "access_token") | return KeyJoin(PREFIX, "access_token") | ||||
} | } | ||||
func AccessTokenLockKey() string { | |||||
return KeyJoin(PREFIX, "access_token_lock") | |||||
} |
@@ -0,0 +1,40 @@ | |||||
package redis_lock | |||||
import ( | |||||
"code.gitea.io/gitea/modules/redis/redis_client" | |||||
"time" | |||||
) | |||||
type DistributeLock struct { | |||||
} | |||||
func NewDistributeLock() *DistributeLock { | |||||
return &DistributeLock{} | |||||
} | |||||
func (lock *DistributeLock) Lock(lockKey string, expireTime time.Duration) bool { | |||||
isOk, _ := redis_client.Setnx(lockKey, "", expireTime) | |||||
return isOk | |||||
} | |||||
func (lock *DistributeLock) LockWithWait(lockKey string, expireTime time.Duration, waitTime time.Duration) bool { | |||||
start := time.Now().UnixMilli() | |||||
duration := waitTime.Milliseconds() | |||||
for { | |||||
isOk, _ := redis_client.Setnx(lockKey, "", expireTime) | |||||
if isOk { | |||||
return true | |||||
} | |||||
if time.Now().UnixMilli()-start > duration { | |||||
return false | |||||
} | |||||
time.Sleep(50 * time.Millisecond) | |||||
} | |||||
return false | |||||
} | |||||
func (lock *DistributeLock) UnLock(lockKey string) error { | |||||
_, err := redis_client.Del(lockKey) | |||||
return err | |||||
} |