记一次Redis缓存组件开发

记一次Redis缓存组件开发

背景

​ IM 系统需要高效存储和管理海量聊天数据,并通过 Redis 进行缓存,同时设定过期时间。为了确保业务连续性,当查询某个会话的历史消息时,如果数据在 Redis 中已过期,则需自动从持久化存储中加载。缓存组件需支持 String、Set、SortedSet 至少三种 Redis 数据结构,以匹配不同的 IM 业务需求,并具备智能加载和数据同步能力。

部分源码展示


/**
 * SortSet  数据结构  缓存策略处理类
 * @param <K>
 * @param <V>
 * @param <V2>
 */
public class SortSetCache<K extends String, V, V2> extends AbstractRedisCache<K, V> implements SetQueryCache<K, V2>, SaveCache<K, V> {


    /**
     *
     */
    Class<V2> v2Class;

    private SortSetQueryCondition sortSetQueryCondition;

    SortSetCache(RedisCache redisCache, RedisService redisService, Class<V2> v2Class, SortSetQueryCondition condition) {
        super(redisCache, redisService);
        this.v2Class = v2Class;
        sortSetQueryCondition = condition;
    }


    @Override
    public Set<V2> get(K key) {

        Set<V2> set = new HashSet<>();

        Direction direction = sortSetQueryCondition.getDirection();
        long count = sortSetQueryCondition.getCount();
        if (direction == Direction.ASC) {
            //顺序查找
            double max = sortSetQueryCondition.getReference() + count + 1;
            set = redisService.zSetRangeByScore(key, sortSetQueryCondition.getReference() + 1, Double.MAX_VALUE, sortSetQueryCondition.getCount());

        } else {
            //倒序查找
            double min = sortSetQueryCondition.getReference() - count - 1;
            min = min < 0 ? 0 : min;
            set = redisService.zSetReverseRangeByScore(key, Double.MIN_VALUE, sortSetQueryCondition.getReference() - 1, sortSetQueryCondition.getCount());
        }

        if (CollectionUtil.isNotEmpty(set)) {
            Object o = set.stream().findFirst().orElse(null);
            if (isPrimitiveOrWrapper(o) && !isPrimitiveOrWrapperClass(v2Class)) {
                //常规类型 需要转换
                set = set.stream().map(item -> {
                    V2 v2 = null;
                    try {
                        v2 = v2Class.newInstance();
                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }

                    setRealValue(v2Class, v2, transformRealKey(key), RedisKey.class);
                    setRealValue(v2Class, v2, item, RealValue.class);
                    return v2;
                }).collect(Collectors.toSet());
            }
        }

        return set;
    }


    @Override
    public boolean expire(K key, long timeout, TimeUnit unit) {
        return super.expire(key, timeout, unit);
    }

    @Override
    public List<K> getValidKey(Collection<K> keys) {
        return super.getValidKey(keys);
    }


    @Override
    public void save(K key, V value) {
        if (isPrimitiveOrWrapper(value)) {
            throw new RuntimeException(" SortSetCache save  V 返回值不允许基本数据类型");
        }

        Object sort = getRealValue(value, RedisSort.class);
        Object realVale = getRealValue(value, RealValue.class);
        if (realVale == null) {
            realVale = value;
        }

        redisService.zSetScore(key, realVale, Double.valueOf(sort.toString()));
    }

    @Override
    public void saveBatch(Collection<K> paramKeys, Collection<V> paramValues) {
        //
        if (CollectionUtil.isEmpty(paramKeys) || CollectionUtil.isEmpty(paramValues)) {
            return;
        }
        List<K> keys = collectonToList(paramKeys);

        V v = paramValues.stream().findFirst().orElse(null);
        if (isPrimitiveOrWrapper(v)) {
            //常规数据类型
            throw new RuntimeException("SortSetCache saveBatch 不允许存储常规数据类型(无法指定排序值)  ");
        } else {
            //
            Set<V> values = collectonToSet(paramValues);
            try {
                Map<K, Set<V>> kListMap = getSetGroupByKey(values);
                Map<K, Set<V>> finalKListMap = kListMap;
                kListMap = keys.stream()
                        // 过滤出 keys 中在 tempKvMap 的键中存在对应关系的元素
                        .filter(key -> finalKListMap.containsKey(transformRealKey(key)))
                        // 将符合条件的 keys 转换成新 Map
                        .collect(Collectors.toMap(
                                key -> key, // 保留原始 key 作为新 Map 的键
                                key -> finalKListMap.get(transformRealKey(key)) // 使用转换后的 key 获取值
                        ));

                kListMap.forEach((key, valueList) -> {

                    Set<ZSetOperations.TypedTuple<Object>> collect = valueList.stream().map(item -> {

                                Object value = getRealValue(item, RedisSort.class);
                                Double score = Double.valueOf(value.toString());
                                return new ZSetOperations.TypedTuple<Object>() {
                                    @Override
                                    public int compareTo(ZSetOperations.TypedTuple<Object> o) {
                                        return 0;
                                    }

                                    @Override
                                    public Object getValue() {
                                        Object realValue = getRealValue(item, RealValue.class);
                                        if (realValue == null) {
                                            return item;
                                        } else {
                                            return realValue;
                                        }
                                    }

                                    @Override
                                    public Double getScore() {
                                        return score;
                                    }
                                };
                            })
                            .collect(Collectors.toSet());
                    redisService.zMultiSetScore(key, collect);
                });


            } catch (Exception e) {
                throw new RuntimeException(e);
            }


        }

    }


}



Redis 缓存应用场景


//String 类型的缓存
String MESSAGE_CONTENT = "im:message:{sessionId}:{messageId}";
@RedisCache(cacheKey = RedisConstant.MESSAGE_CONTENT)
    public List<MessageVo> selectMessageVoByMessageIds(@RedisKey(key = "sessionId") List<Long> sessionIds, @RedisKey(key = "messageId", isValidKey = true) List<Long> messageIds) {
    //查询逻辑
    
    }


//SortSet类型的缓存
@RedisCache(cacheKey = RedisConstant.MESSAGE_SORT_SET, cacheType = CacheType.SORTED_SET)
    public List<MessageSortDto> selectMessageIdsBySessionId(PullMessageCondition messageCondition) {
    //MySQL 数据库查询逻辑
    }
    
//Set类型的缓存    
@RedisCache(cacheKey = "im:permission", cacheType = CacheType.SET)
    public List<String> selectTestPermission(Long sessionId) {
    //查询逻辑
    
    }    

部分源码截图

在这里插入图片描述
在这里插入图片描述

总结:

该Redis公共组件提供了注解式开发能力,业务代码不用关注缓存是否失效,简化了开发流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值