likes
comments
collection
share

基于Redission高级应用-RMapCache,RSetCache实战应用

作者站长头像
站长
· 阅读数 6

概述:

最近工作中使用到redis,设置一个集合,其元素都设置过期时间,经查看同事的实现代码总感觉不是太合理: 以下是其

现实实现:

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. 遍历有序集合并筛选元素:

 

   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方法的示例:

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方法中,我们使用RSetCacheforEach方法来遍历集合中的所有元素。对于每个元素,我们使用remainTimeToLive方法来获取其剩余的存活时间。如果元素的剩余存活时间加上当前时间戳大于我们计算的subTime,则认为这个元素是有效的,并将其添加到结果集合ss中。

请注意,这个实现与原始的RScoredSortedSet实现有所不同,因为RSetCache不是基于分数来排序元素的,而是根据它们的到期时间来自动移除元素。因此,这个方法假定元素在添加到RSetCache时已经设置了正确的到期时间。如果到期时间设置正确,这个方法将返回所有在指定超时时间内仍然有效的元素。

扩展:

基于此在Redisson中,你可以使用RMapCache或者RSetCache来实现集合中每一个元素都有对应到期时间的功能。这两种类型的集合都允许你为每个元素设置一个TTL(Time To Live),一旦到了设定的时间,元素就会自动从集合中被移除。

以下是如何使用RMapCacheRSetCache的示例:

使用RMapCache

RMapCache是一个实现了java.util.concurrent.ConcurrentMap接口的Map,你可以为每个键值对设置到期时间。

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,你可以为每个元素设置到期时间。

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();
    }
}

在这两个示例中,我们使用了putadd方法的重载版本,它们接受一个额外的参数来指定元素的TTL。一旦元素的TTL到期,Redis会自动删除这些元素。

扩展总结:

请注意,RMapCacheRSetCache的行为依赖于Redis的EXPIRE命令,这意味着到期时间的精确度受到Redis时间精度的限制,通常是1秒。此外,由于Redis的定期清理策略,到期元素可能不会被立即删除,而是在下一次清理周期时被移除。

延申示例:

RMapCacheRSetCache在实际应用中可以用于多种场景,比如缓存数据、临时存储、限时访问等。以下是两个具体的实战用例:

1. RMapCache用例:用户登录令牌缓存:

在Web应用程序中,我们经常需要缓存用户的登录令牌,并且这些令牌通常在一段时间后会过期。以下是如何使用RMapCache来存储令牌和设置过期时间的示例:

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来实现这个功能的示例:

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过期事件。

这两个用例展示了RMapCacheRSetCache如何在实际应用中用于处理具有到期时间的数据。这些数据结构在需要自动清理过期数据的场景中非常有用。