التخزين المؤقت في Kotlin: Redis وCaffeine والبدائل
التخزين المؤقت هو أحد الأدوات الرئيسية لتحسين أداء التطبيقات. في Kotlin، بفضل التوافق مع Java والنظام البيئي الغني لـ JVM، تتوفر العديد من الحلول: بدءًا من ذاكرة التخزين المؤقت الخفيفة في الذاكرة (in-memory) وصولاً إلى الأنظمة الموزعة القائمة على Redis. في هذه المقالة، سنستعرض المكتبات الشائعة، وإعداداتها، وأمثلة على استخدامها.
لماذا نحتاج إلى التخزين المؤقت؟
الأهداف الرئيسية للتخزين المؤقت:
- تقليل زمن الاستجابة (latency) عن طريق تخزين البيانات المطلوبة بشكل متكرر في ذاكرة سريعة.
- تقليل الحمل على قواعد البيانات وواجهات برمجة التطبيقات الخارجية (API).
- توفير الموارد الحاسوبية عند الطلبات المتكررة.
يتيح Kotlin استخدام كل من مكتبات Java الأصلية والواجهات المتخصصة الخاصة بـ Kotlin.
1. Redis عبر Lettuce أو Redisson
Redis هو مخزن البيانات في الذاكرة (in-memory data store) الأكثر شيوعًا للتخزين المؤقت الموزع. في Kotlin، يتم توصيله عبر عملاء Lettuce (تفاعلي) أو Redisson (متزامن/غير متزامن).
مثال: التخزين المؤقت مع Lettuce
// build.gradle.ktsimplementation("io.lettuce:lettuce-core:6.3.1.RELEASE")
// CacheService.ktimport io.lettuce.core.RedisClientimport io.lettuce.core.api.sync.RedisCommands
class RedisCache(private val host: String = "localhost", private val port: Int = 6379) { private val client: RedisClient = RedisClient.create("redis://$host:$port") private val connection = client.connect() private val sync: RedisCommands<String, String> = connection.sync()
fun get(key: String): String? = sync.get(key) fun set(key: String, value: String, ttlSeconds: Long = 60) { sync.setex(key, ttlSeconds, value) } fun delete(key: String) = sync.del(key) fun close() { connection.close() client.shutdown() }}
// الاستخدامfun main() { val cache = RedisCache() cache.set("user:123", """{"name":"Alice","role":"admin"}""", 120) val data = cache.get("user:123") println(data) // {"name":"Alice","role":"admin"} cache.close()}Redisson — واجهة برمجة تطبيقات (API) ذات مستوى أعلى
implementation("org.redisson:redisson:3.27.2")
val config = Config().apply { useSingleServer().setAddress("redis://127.0.0.1:6379")}val redisson = Redisson.create(config)val map = redisson.getMap<String, String>("userCache")map["user:456"] = """{"name":"Bob"}"""val bob = map["user:456"]redisson.shutdown()2. Caffeine — تخزين مؤقت سريع في الذاكرة (in-memory)
Caffeine هو البديل الحديث لـ Guava Cache. تم تحسينه للأحمال العالية، ويدعم الإزالة التلقائية للإدخالات القديمة.
implementation("com.github.ben-manes.caffeine:caffeine:3.1.8")
import com.github.benmanes.caffeine.cache.Cacheimport com.github.benmanes.caffeine.cache.Caffeineimport java.util.concurrent.TimeUnit
val cache: Cache<String, String> = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(10_000) .recordStats() .build()
// الكتابة والقراءةcache.put("key1", "value1")val value = cache.getIfPresent("key1")println(value) // value1
// الإحصائياتval stats = cache.stats()println("Hit rate: ${stats.hitRate()}")3. Hazelcast — تخزين مؤقت موزع مع العنقودية (Clustering)
Hazelcast مناسب للهياكل المعمارية القائمة على الخدمات المصغرة (microservices): حيث تتم مزامنة ذاكرة التخزين المؤقت تلقائيًا بين العقد.
implementation("com.hazelcast:hazelcast:5.3.6")
import com.hazelcast.core.Hazelcastimport com.hazelcast.map.IMap
val instance = Hazelcast.newHazelcastInstance()val map: IMap<String, String> = instance.getMap("session-cache")map.put("session:token123", "userId=42", 30, TimeUnit.MINUTES)val userId = map.get("session:token123")instance.shutdown()