概述:
最近工作中使用到redis,设置一个集合,其元素都设置过期时间,经查看同事的实现代码总感觉不是太合理: 以下是其
现实实现:
ini
public Set<String> get(String s) {
String redisKey = String.format("aa:bb:%s", s);
RScoredSortedSet<String> scoredSortedSet = redissonClient.getScoredSortedSet(redisKey);
Set<String> ss = Sets.newHashSet();
int sendBackedTimeout = SysConfigUtils.getIntegerValue("aa_bb_timeout");
long subTime = System.currentTimeMillis() - sendBackedTimeout;
scoredSortedSet.forEach(
key -> {
double score = scoredSortedSet.getScore(key);
if(score > subTime){
ss.add(key);
}
}
);
return ss;
}
解释:
1. 方法签名:public Set<String> get(String s)
-
这个方法是公开的,返回一个
Set<String>
,代表一组字符串。 -
方法接受一个名为
siteCode
的字符串参数,通常用于标识特定的站点或位置。
2. 构造Redis键:String redisKey = String.format("aa:bb:%s", s);
- 使用
String.format
方法和siteCode
参数来构造一个用于Redis的键。键的格式是"aa:bb:<s>"
,其中<s>
将被替换为传入的站点代码。
3. 获取Redisson的有序集合实例:RScoredSortedSet<String> scoredSortedSet = redissonClient.getScoredSortedSet(redisKey);
- 通过Redisson客户端
redissonClient
获取一个名为scoredSortedSet
的有序集合实例,该实例与redisKey
对应。
4. 创建一个HashSet:
Set<String> ss = Sets.newHashSet();
- 使用Guava库的Sets.newHashSet()
方法创建一个新的空HashSet,用于存储筛选后的元素。
5. 获取配置的超时时间:int sendBackedTimeout = SysConfigUtils.getIntegerValue("aa_bb_timeout");
- 从系统配置中获取一个名为
"aa_bb_timeout"
的整数值,这个值表示超时时间(单位为分钟)。
6. 计算过滤时间戳:long subTime = System.currentTimeMillis() - sendBackedTimeout * 1000 * 60L;
- 通过当前时间戳减去超时时间(转换为毫秒)来计算一个过滤时间戳
subTime
。只有分数(通常是时间戳)大于这个值的元素才会被认为是有效的。
7. 遍历有序集合并筛选元素:
ini
scoredSortedSet.forEach(
key -> {
double score = scoredSortedSet.getScore(key);
if (score > subTime) {
ss.add(key);
}
}
);
-
使用
forEach
方法遍历scoredSortedSet
中的所有元素。 -
对于每个元素,使用
getScore
方法获取它的分数(在这个上下文中,分数可能代表时间戳)。 -
如果该分数大于计算出的
subTime
,则将元素的key
添加到ss
集合中。
8. 返回筛选后的元素集合:
return ss;
- 最后,方法返回包含所有有效元素的
ss
集合。
老代码总结:
总的来说,这段代码的目的是从Redis的有序集合中检索出在指定的超时时间内仍然有效的元素,并将它们作为一个集合返回。这种方法可以用于各种场景,例如,跟踪和管理临时数据、限时活动的参与者或者其他需要基于时间有效性筛选的数据集合。
RSetCache改造:
要使用RSetCache
来实现类似的功能,你需要在添加元素时设置它们的到期时间。RSetCache
中的每个元素都可以有一个独立的到期时间,而不是使用分数(如RScoredSortedSet
)来表示。
以下是如何利用RSetCache
重写这个get
方法的示例:
java
public Set<String> get(String s) {
// 构造Redis键
String redisKey = String.format("aa:bb:%s", s);
// 获取RSetCache实例
RSetCache<String> setCache = redissonClient.getSetCache(redisKey);
// 创建一个HashSet来存储筛选后的元素
Set<String> ss = Sets.newHashSet();
// 获取配置的超时时间
int sendBackedTimeout = SysConfigUtils.getIntegerValue("aa_bb_timeout");
// 计算过滤时间戳
long subTime = System.currentTimeMillis() -sendBackedTimeout;
// 遍历RSetCache并筛选元素
setCache.forEach(
key -> {
// 获取元素的剩余存活时间
long ttl = setCache.remainTimeToLive(key);
// 如果剩余存活时间与当前时间的差值大于配置的超时时间,则认为元素有效
if (ttl > 0 && System.currentTimeMillis() + ttl > subTime) {
ss.add(key);
}
}
);
// 返回筛选后的元素集合
return ss;
}
在这个重写的get
方法中,我们使用RSetCache
的forEach
方法来遍历集合中的所有元素。对于每个元素,我们使用remainTimeToLive
方法来获取其剩余的存活时间。如果元素的剩余存活时间加上当前时间戳大于我们计算的subTime
,则认为这个元素是有效的,并将其添加到结果集合ss
中。
请注意,这个实现与原始的RScoredSortedSet
实现有所不同,因为RSetCache
不是基于分数来排序元素的,而是根据它们的到期时间来自动移除元素。因此,这个方法假定元素在添加到RSetCache
时已经设置了正确的到期时间。如果到期时间设置正确,这个方法将返回所有在指定超时时间内仍然有效的元素。
扩展:
基于此在Redisson中,你可以使用RMapCache
或者RSetCache
来实现集合中每一个元素都有对应到期时间的功能。这两种类型的集合都允许你为每个元素设置一个TTL(Time To Live),一旦到了设定的时间,元素就会自动从集合中被移除。
以下是如何使用RMapCache
和RSetCache
的示例:
使用RMapCache
RMapCache
是一个实现了java.util.concurrent.ConcurrentMap
接口的Map,你可以为每个键值对设置到期时间。
java
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class MapCacheExample {
public static void main(String[] args) {
RedissonClient redisson = Redisson.create();
RMapCache<String, String> mapCache = redisson.getMapCache("anyMapCache"); // 将键值对添加到MapCache中,并设置60秒后到期
mapCache.put("key1", "value1", 60, TimeUnit.SECONDS);
mapCache.put("key2", "value2", 120, TimeUnit.SECONDS);
// ...
redisson.shutdown();
}
}
使用RSetCache
RSetCache
是一个实现了java.util.Set
接口的Set,你可以为每个元素设置到期时间。
java
import org.redisson.api.RSetCache;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class SetCacheExample {
public static void main(String[] args) {
RedissonClient redisson = Redisson.create();
RSetCache<String> setCache = redisson.getSetCache("anySetCache");
// 将元素添加到SetCache中,并设置60秒后到期
setCache.add("value1", 60, TimeUnit.SECONDS);
setCache.add("value2", 120, TimeUnit.SECONDS);
// ...
redisson.shutdown();
}
}
在这两个示例中,我们使用了put
和add
方法的重载版本,它们接受一个额外的参数来指定元素的TTL。一旦元素的TTL到期,Redis会自动删除这些元素。
扩展总结:
请注意,RMapCache
和RSetCache
的行为依赖于Redis的EXPIRE
命令,这意味着到期时间的精确度受到Redis时间精度的限制,通常是1秒。此外,由于Redis的定期清理策略,到期元素可能不会被立即删除,而是在下一次清理周期时被移除。
延申示例:
RMapCache
和RSetCache
在实际应用中可以用于多种场景,比如缓存数据、临时存储、限时访问等。以下是两个具体的实战用例:
1. RMapCache用例:用户登录令牌缓存:
在Web应用程序中,我们经常需要缓存用户的登录令牌,并且这些令牌通常在一段时间后会过期。以下是如何使用RMapCache
来存储令牌和设置过期时间的示例:
java
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.api.map.event.EntryExpiredListener;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class TokenCacheExample {
public static void main(String[] args) {
RedissonClient redisson = Redisson.create();
// 获取RMapCache实例
RMapCache<String, String> tokenCache = redisson.getMapCache("userTokens");
// 用户登录后生成令牌
String userId = "user123";
String token = UUID.randomUUID().toString();
// 将令牌存储到缓存中,并设置30分钟后过期
tokenCache.put(token, userId, 30, TimeUnit.MINUTES);
// 添加过期监听器,可以用于记录日志或者执行清理操作
tokenCache.addListener((EntryExpiredListener<String, String>) event -> {
System.out.println("Token expired: " + event.getKey());
});
// ...
// 检查令牌有效性
String cachedUserId = tokenCache.get(token);
if (cachedUserId != null) {
// 令牌有效
} else {
// 令牌无效或已过期
}
// ...
redisson.shutdown();
}
}
在这个例子中,每当用户登录时,我们生成一个唯一的令牌,并将其与用户ID关联存储在RMapCache
中。令牌会在30分钟后自动过期。我们还添加了一个监听器来处理令牌过期事件。
2. RSetCache用例:限时优惠活动:
假设你正在运行一个电商平台,你想要为限时优惠活动存储一组用户ID,这些用户ID在活动结束后应该自动从集合中移除。以下是如何使用RSetCache
来实现这个功能的示例:
java
import org.redisson.api.RSetCache;
import org.redisson.api.RedissonClient;
import org.redisson.api.map.event.EntryExpiredListener;
import java.util.concurrent.TimeUnit;
public class LimitedTimeOfferExample {
public static void main(String[] args) {
RedissonClient redisson = Redisson.create();
// 获取RSetCache实例
RSetCache<String> offerUsers = redisson.getSetCache("limitedTimeOfferUsers");
// 活动开始,用户加入活动
String userId = "user123";
long offerDuration = 2; // 活动持续时间,单位小时
// 将用户ID添加到集合中,并设置活动结束后自动过期
offerUsers.add(userId, offerDuration, TimeUnit.HOURS);
// 添加过期监听器,可以用于记录日志或者执行清理操作
offerUsers.addListener((EntryExpiredListener<String>) event -> {
System.out.println("Offer expired for user: " + event.getValue());
});
// ...
// 检查用户是否仍在活动中
if (offerUsers.contains(userId)) {
// 用户仍在活动中
} else {
// 活动已结束或用户未参加
}
// ...
redisson.shutdown();
}
}
在这个例子中,我们将参与限时优惠活动的用户ID存储在RSetCache
中,并为每个用户设置了活动的持续时间。当活动时间结束后,用户ID会自动从集合中移除。我们同样添加了一个监听器来处理用户ID过期事件。
这两个用例展示了RMapCache
和RSetCache
如何在实际应用中用于处理具有到期时间的数据。这些数据结构在需要自动清理过期数据的场景中非常有用。