Skip to content

Commit

Permalink
feat: 代码回滚,因redis版本问题,获取统一时间逻辑调整#1539
Browse files Browse the repository at this point in the history
  • Loading branch information
zacYL committed Oct 24, 2024
1 parent 760830e commit c9ef7a3
Show file tree
Hide file tree
Showing 12 changed files with 41 additions and 26 deletions.
2 changes: 0 additions & 2 deletions src/backend/buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,4 @@ object Versions {
const val JavaCpp = "1.5.9"
const val Notice = "1.0.0"
const val SpringCloudFunction = "3.2.11"
// it.ozimov:embedded-redis redis版本最高才2.8,不支持redis.replicate_commands()
const val NewEmbeddedRedis = "1.4.3"
}
2 changes: 1 addition & 1 deletion src/backend/common/common-ratelimiter/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ dependencies {
testImplementation("org.mockito.kotlin:mockito-kotlin")
testImplementation("io.mockk:mockk")
testImplementation(project(":common:common-redis"))
testImplementation("com.github.codemonstur:embedded-redis:${Versions.NewEmbeddedRedis}") {
testImplementation("it.ozimov:embedded-redis:${Versions.EmbeddedRedis}") {
exclude("org.slf4j", "slf4j-simple")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class DistributedFixedWindowRateLimiter(
acquireResult = result == 1L
}
if (logger.isDebugEnabled) {
logger.debug("acquire distributed fixed window rateLimiter elapsed time: $elapsedTime")
logger.debug("acquire distributed fixed window rateLimiter elapsed time: $elapsedTime ms")
}
return acquireResult
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,21 @@ class DistributedLeakyRateLimiter(
var acquireResult = false
val elapsedTime = measureTimeMillis {
val redisScript = DefaultRedisScript(LuaScript.leakyRateLimiterScript, List::class.java)
// 时间统一从redis server获取
// lua脚本中使用命令获取时间指令需要配合replicate_commands()使用,但是由于redis只有在某个特定版本上才支持该指令,
// 所以无法从lua脚本中去获取时间,只能分为多次调用。
val currentTime = redisTemplate.execute {
connection -> connection.time()
} ?: System.currentTimeMillis()
val currentSeconds = (currentTime / 1000)
val results = redisTemplate.execute(
redisScript, getKeys(key), permitsPerSecond.toString(),
capacity.toString(), permits.toString()
capacity.toString(), permits.toString(), currentSeconds.toString()
)
acquireResult = results[0] == 1L
}
if (logger.isDebugEnabled) {
logger.debug("acquire distributed leaky rateLimiter elapsed time: $elapsedTime")
logger.debug("acquire distributed leaky rateLimiter elapsed time: $elapsedTime ms")
}
return acquireResult
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,23 @@ class DistributedSlidingWindowRateLimiter(
var acquireResult = false
val elapsedTime = measureTimeMillis {
val redisScript = DefaultRedisScript(LuaScript.slidingWindowRateLimiterScript, List::class.java)
// 时间统一从redis server获取
// lua脚本中使用命令获取时间指令需要配合replicate_commands()使用,但是由于redis只有在某个特定版本上才支持该指令,
// 所以无法从lua脚本中去获取时间,只能分为多次调用。
val currentTime = redisTemplate.execute {
connection -> connection.time()
} ?: System.currentTimeMillis()
val currentSeconds = (currentTime / 1000)
val random = System.nanoTime()
// 注意, 由于redis expire只支持秒为单位,所以周期最小单位为秒
val results = redisTemplate.execute(
redisScript, getKeys(key), limit.toString(),
(duration.seconds).toString(), permits.toString()
redisScript, getKeys(key), limit.toString(), (duration.seconds).toString(),
permits.toString(), currentSeconds.toString(), random.toString()
)
acquireResult = results[0] == 1L
}
if (logger.isDebugEnabled) {
logger.debug("acquire distributed sliding window rateLimiter elapsed time: $elapsedTime")
logger.debug("acquire distributed sliding window rateLimiter elapsed time: $elapsedTime ms")
}
return acquireResult
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,21 @@ class DistributedTokenBucketRateLimiter(
var acquireResult: Boolean
val elapsedTime = measureTimeMillis {
val redisScript = DefaultRedisScript(LuaScript.tokenBucketRateLimiterScript, List::class.java)
// 时间统一从redis server获取
// lua脚本中使用命令获取时间指令需要配合replicate_commands()使用,但是由于redis只有在某个特定版本上才支持该指令,
// 所以无法从lua脚本中去获取时间,只能分为多次调用。
val currentTime = redisTemplate.execute {
connection -> connection.time()
} ?: System.currentTimeMillis()
val currentSeconds = (currentTime / 1000)
val results = redisTemplate.execute(
redisScript, getKeys(key), permitsPerSecond.toString(),
capacity.toString(), permits.toString()
capacity.toString(), permits.toString(), currentSeconds.toString()
)
acquireResult = results[0] == 1L
}
if (logger.isDebugEnabled) {
logger.debug("acquire distributed token bucket rateLimiter elapsed time: $elapsedTime")
logger.debug("acquire distributed token bucket rateLimiter elapsed time: $elapsedTime ms")
}
return acquireResult
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ import java.nio.charset.StandardCharsets
*/
object LuaScript {
private val logger = LoggerFactory.getLogger(LuaScript::class.java)
private const val FIX_WINDOW_RATE_LIMITER_FILE_PATH = "META-INF/fix-window-rate-limiter.lua"
private const val TOKEN_BUCKET_RATE_LIMITER_FILE_PATH = "META-INF/token-bucket-rate-limiter.lua"
private const val SLIDING_WINDOW_RATE_LIMITER_FILE_PATH = "META-INF/sliding-window-rate-limiter.lua"
private const val LEAKY_RATE_LIMITER_FILE_PATH = "META-INF/leaky-rate-limiter.lua"
private const val FIX_WINDOW_RATE_LIMITER_FILE_PATH = "fix-window-rate-limiter.lua"
private const val TOKEN_BUCKET_RATE_LIMITER_FILE_PATH = "token-bucket-rate-limiter.lua"
private const val SLIDING_WINDOW_RATE_LIMITER_FILE_PATH = "sliding-window-rate-limiter.lua"
private const val LEAKY_RATE_LIMITER_FILE_PATH = "leaky-rate-limiter.lua"

lateinit var fixWindowRateLimiterScript: String
lateinit var tokenBucketRateLimiterScript: String
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
redis.replicate_commands()
local leaky_bucket_key = KEYS[1]
-- last update key
local last_bucket_key = KEYS[2]
Expand All @@ -9,8 +8,7 @@ local capacity = tonumber(ARGV[2])
-- request count
local requested = tonumber(ARGV[3])
-- current timestamp seconds
local currentTime = redis.call('TIME')
local now = tonumber(currentTime[1])
local now = tonumber(ARGV[4])

-- the key life time
local key_lifetime = math.ceil((capacity / rate) + 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
-- limit: 限流器的容量
-- interval: 时间窗口的长度(单位为秒)
-- count: 一次获取的令牌数量
redis.replicate_commands()
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local interval = tonumber(ARGV[2])
local count = tonumber(ARGV[3])
local now_mill = tonumber(ARGV[4])
local currentTime = redis.call('TIME')
local now_sec = tonumber(currentTime[1])
local random = tonumber(currentTime[2])
local now_sec = tonumber(ARGV[5])
local random = tonumber(ARGV[6])

-- 删除时间窗口之外的令牌
redis.call('zremrangebyscore', key, 0, now_sec - interval)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
redis.replicate_commands()
local tokens_key = KEYS[1]
local timestamp_key = KEYS[2]
-- 每秒填充速率
Expand All @@ -8,8 +7,8 @@ local capacity = tonumber(ARGV[2])
-- 消耗令牌数量
local requested = tonumber(ARGV[3])
-- now 当前时间秒
local currentTime = redis.call('TIME')
local now = tonumber(currentTime[1])
local now = tonumber(ARGV[4])

-- 计算令牌桶填充满需要多久
local fill_time = capacity/rate
-- *2保证时间充足
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import javax.annotation.PreDestroy

@TestConfiguration
class RedisTestConfiguration {
private val redisServer = RedisServer.newRedisServer().build()
private val redisServer = RedisServer.builder().build()

@PostConstruct
fun postConstruct() {
Expand Down

0 comments on commit c9ef7a3

Please sign in to comment.