From 0d3bc2e72d6edc3cf6c2897eb0303e33641b6fc3 Mon Sep 17 00:00:00 2001 From: FxRayHughes Date: Thu, 10 Oct 2024 19:41:32 +0800 Subject: [PATCH] =?UTF-8?q?[RedisCache]=20=E6=96=B0=E7=9A=84=E8=BD=AE?= =?UTF-8?q?=E6=A4=85=201.=20=E6=94=AF=E6=8C=81=E5=BF=AB=E6=8D=B7=E6=93=8D?= =?UTF-8?q?=E4=BD=9CRedis=202.=20=E6=8F=90=E4=BE=9B=E4=BA=86=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E7=AE=80=E5=8D=95=E7=9A=84Kotlin=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8C=96=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../build.gradle.kts | 21 ++ .../taboolib/expansion/tool/RedisCache.kt | 256 ++++++++++++++++++ settings.gradle.kts | 1 + 3 files changed, 278 insertions(+) create mode 100644 module/database/database-alkaid-redis-tool/build.gradle.kts create mode 100644 module/database/database-alkaid-redis-tool/src/main/kotlin/taboolib/expansion/tool/RedisCache.kt diff --git a/module/database/database-alkaid-redis-tool/build.gradle.kts b/module/database/database-alkaid-redis-tool/build.gradle.kts new file mode 100644 index 000000000..d180282d6 --- /dev/null +++ b/module/database/database-alkaid-redis-tool/build.gradle.kts @@ -0,0 +1,21 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +dependencies { + compileOnly("redis.clients:jedis:4.2.3") + compileOnly(project(":common")) + compileOnly(project(":common-env")) + compileOnly(project(":common-util")) + compileOnly(project(":common-platform-api")) + compileOnly(project(":module:basic:basic-configuration")) + compileOnly(project(":module:database:database-alkaid-redis")) + compileOnly("com.fasterxml.jackson.core:jackson-databind:2.17.2") + compileOnly("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2") +} + +tasks { + withType { + relocate("redis.clients.jedis.", "redis.clients.jedis_4_2_3.") + relocate("com.electronwill.nightconfig.core.conversion", "taboolib.library.configuration") + relocate("org.yaml.snakeyaml.", "org.yaml.snakeyaml_2_0.") + } +} diff --git a/module/database/database-alkaid-redis-tool/src/main/kotlin/taboolib/expansion/tool/RedisCache.kt b/module/database/database-alkaid-redis-tool/src/main/kotlin/taboolib/expansion/tool/RedisCache.kt new file mode 100644 index 000000000..66d08b980 --- /dev/null +++ b/module/database/database-alkaid-redis-tool/src/main/kotlin/taboolib/expansion/tool/RedisCache.kt @@ -0,0 +1,256 @@ +package taboolib.expansion.tool + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.module.kotlin.KotlinFeature +import com.fasterxml.jackson.module.kotlin.KotlinModule +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import taboolib.common.Inject +import taboolib.common.LifeCycle +import taboolib.common.PrimitiveSettings +import taboolib.common.env.JarRelocation +import taboolib.common.env.RuntimeDependencies +import taboolib.common.env.RuntimeDependency +import taboolib.common.env.RuntimeEnv +import taboolib.common.platform.Awake +import taboolib.common.platform.Schedule +import taboolib.expansion.AlkaidRedis +import taboolib.expansion.SingleRedisConnection +import taboolib.expansion.SingleRedisConnector +import taboolib.expansion.fromConfig +import taboolib.module.configuration.Configuration +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.TimeUnit + + +/** + * 更快捷的使用 Redis 只需要 link() 即可初始化工具 + * 使用 get() set() del() 来操作缓存 + * 使用 getObject() setCacheObject() 来操作对象缓存 -- 基于 Jackson-Kotlin + * 使用 setExpire() setEx() 来设置过期时间 + * 使用 delPrefix() 来删除某个前缀的所有key + * 使用 jsonMapper() 来获取 序列化工具 -- 基于 Jackson-Kotlin + */ +@Inject +@RuntimeDependencies( + RuntimeDependency( + "!com.fasterxml.jackson.core:jackson-core:2.17.2", + test = "!com.fasterxml.jackson.databind.ObjectMapper", + transitive = false + ), + RuntimeDependency( + "!com.fasterxml.jackson.core:jackson-databind:2.17.2", + test = "!com.fasterxml.jackson.databind.ObjectMapper", + transitive = false + ), + RuntimeDependency( + "!com.fasterxml.jackson.core:jackson-annotations:2.17.2", + test = "!com.fasterxml.jackson.annotation.JsonView", + transitive = false + ), +) +object RedisCache { + + @Awake(LifeCycle.LOAD) + fun load() { + val rel: MutableList = ArrayList() + if (!PrimitiveSettings.IS_ISOLATED_MODE) { + rel.add(JarRelocation(RuntimeEnv.KOTLIN_ID + ".", PrimitiveSettings.getRelocatedKotlinVersion() + ".")) + rel.add(JarRelocation(RuntimeEnv.KOTLIN_COROUTINES_ID + ".", PrimitiveSettings.getRelocatedKotlinCoroutinesVersion() + ".")) + } + RuntimeEnv.ENV_DEPENDENCY.loadDependency("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2", false, rel) + } + + private var connector: SingleRedisConnector? = null + private var connection: SingleRedisConnection? = null + private var isEnable: Boolean = false + private val cache = ConcurrentHashMap() + + data class CacheData( + var value: String, + var overTime: Long = -1 + ) + + val json by lazy { + val kotlinModule = KotlinModule.Builder() + .configure(KotlinFeature.NullIsSameAsDefault, true) + .build() + JsonMapper.builder() + .addModule(kotlinModule) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .build() + } + + // 这里使用了Jackson-Kotlin + // https://github.com/FasterXML/jackson-module-kotlin + fun jsonMapper(): ObjectMapper { + return json + } + + /** + * 定时清理过期本地缓存 + */ + @Schedule(delay = 1200, period = 1200, async = true) + fun check() { + if (!isEnable) { + cache.keys.forEach { + if ((cache[it]?.overTime ?: 0) < System.currentTimeMillis()) { + cache.remove(it) + } + } + } + } + + /** + * 链接 Redis + * @param configuration 配置文件 + * redis: + * # 是否启用redis 不启用则使用内部缓存 + * enable: false + * host: localhost + * port: 6379 + * #使用账号密码模式时取消注释 + * #user: user + * #password: password + * connect: 32 + * timeout: 1000 + */ + fun link(configuration: Configuration) { + val redis = configuration.getConfigurationSection("redis")!! + if (redis.getBoolean("enable")) { + try { + connector = AlkaidRedis.create().fromConfig(redis) + connection?.close() + connection = connector!!.connect().connection() + connection!!.setEx("test", "test", 1, TimeUnit.SECONDS) + isEnable = true + } catch (_: Exception) { + } + } + } + + /** + * 安全获取connection失败要重连 + */ + private fun getConnection(): SingleRedisConnection { + return try { + connection!! + } catch (_: Exception) { + connection = connector!!.connect().connection() + connection!! + } + } + + /** + * 获取缓存 + */ + fun get(key: String): String? { + return if (isEnable) { + getConnection()[key] + } else { + cache[key]?.let { + if (it.overTime == -1L || it.overTime > System.currentTimeMillis()) { + it.value + } else { + cache.remove(key) + null + } + } + } + } + + /** + * 获取缓存对象 + */ + inline fun getObject(key: String): T? { + val value = get(key) ?: return null + return jsonMapper().readValue(value, T::class.java) + } + + /** + * 设置不过期的缓存 + */ + fun set(key: String, value: String) { + if (isEnable) { + getConnection()[key] = value + } else { + cache[key] = CacheData(value) + } + } + + /** + * 设置缓存对象 + */ + fun setCacheObject(key: String, value: Any) { + set(key, jsonMapper().writeValueAsString(value)) + } + + /** + * 设置缓存对象 + */ + fun setCacheObject(key: String, value: Any, seconds: Long, timeUnit: TimeUnit) { + setEx(key, jsonMapper().writeValueAsString(value), seconds, timeUnit) + } + + /** + * 删除缓存 + */ + fun del(key: String) { + if (isEnable) { + getConnection().delete(key) + } else { + cache.remove(key) + } + } + + /** + * 删除某个前缀的所有key + */ + fun delPrefix(prefix: String) { + if (isEnable) { + val script = """ + local cursor = "0" + repeat + local result = redis.call('SCAN', cursor, 'MATCH', ARGV[1]) + cursor = result[1] + local keys = result[2] + for i, key in ipairs(keys) do + redis.call('DEL', key) + end + until cursor == "0" + """.trimIndent() + + getConnection().eval(script, listOf("${prefix}*"), emptyList()) + } else { + cache.keys.forEach { + if (it.startsWith(prefix)) { + cache.remove(it) + } + } + } + } + + /** + * 设置过期时间 + */ + fun setExpire(key: String, seconds: Long, timeUnit: TimeUnit) { + if (isEnable) { + getConnection().expire(key, seconds, timeUnit) + } else { + cache[key]?.overTime = System.currentTimeMillis() + timeUnit.toMillis(seconds) + } + } + + /** + * 设置带过期时间的缓存 + */ + fun setEx(key: String, value: String, seconds: Long, timeUnit: TimeUnit) { + if (isEnable) { + getConnection().setEx(key, value, seconds, timeUnit) + } else { + cache[key] = CacheData(value, System.currentTimeMillis() + timeUnit.toMillis(seconds)) + } + } + +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 75ca7e881..e5b219b18 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -45,6 +45,7 @@ include( "module:database:database-ptc", "module:database:database-ptc-object", "module:database:database-player-redis", + "module:database:database-alkaid-redis-tool", // 脚本环境 "module:script:script-javascript",