SpringBoot实现冷数据预热

SpringBoot实现冷数据预热功能

实现原理

  • 实现ServletContextListener接口,重写contextInitialized()方法,当SpringContext初始化完成后,会调用该方法
  • 在该方法里实现业务逻辑

示例

  • 定义冷数据预热业务类,实现ServletContextListener
import com.alibaba.fastjson.JSONObject;
import gk.springboot.preheat.dao.UserDao;
import gk.springboot.preheat.model.UserInfo;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.hash.Jackson2HashMapper;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Component
// 由于配置了redisTemplate,故考虑需要在该配置后加载,防止redisTemplate配置未生效
@AutoConfigureAfter(value = RedisTemplateConfig.class)
public class PreHeatUserToRedis implements ServletContextListener {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private UserDao userDao;

    public static final String USER_KEY_PREFIX = "user:";

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 先将原来数据清空(即使用unlink删除key)
        /*Set<String> keys = redisTemplate.keys(USER_KEY_PREFIX + "*");
        redisTemplate.unlink(keys);*/

        // 如果keys *命令被禁用,考虑使用scan命令,将user:*匹配到的key取出,然后删除
        // keys *命令当redis库中有很多key时,会阻塞redis,导致其他业务请求redis等待时间较长
        // 使用scan命令类似于limit查询,每次只查询N个符合pattern的key,不会造成redis阻塞
        Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keysTmp = new HashSet<>();
            Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(USER_KEY_PREFIX + "*").count(1000).build());
            while (cursor.hasNext()) {
                keysTmp.add(new String(cursor.next()));
            }
            return keysTmp;
        });
        redisTemplate.unlink(keys); // 删除

        // 数据库查询数据
        List<UserInfo> users = userDao.getAllUserInfo();
        // 使用 Spring-redis提供的Jackson2HashMapper将对象转Map类,
        // 快速将对象转成Map(具体可参考:https://blog.csdn.net/qq_16159433/article/details/121876883)
        users.forEach(u -> redisTemplate.opsForHash().putAll(
                USER_KEY_PREFIX + u.getId(),
                // 这里使用fastJSON转也可以
                JSONObject.parseObject(JSONObject.toJSONString(u)).getInnerMap()
                // new Jackson2HashMapper(true).toHash(u))
                )
        );
    }
}
  • 为了解决Spring中redisTemplate写数据出现乱码问题,需要重新对redisTemplate重新配置序列化对象
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisTemplateConfig {
    @Bean(value = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        // 重新配置 RedisTemplate中的序列对象,解决默认序列化对象出现乱码问题
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 用的是fastJSON
        redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericFastJsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}
  • 其他的UserDao、UserController、UserService类就是正常写,没有特别注意点。SpringBoot的启动类也没需要加特殊注解。

测试

  • 在Tomcat启动前,就已完成数据加载到redis中
    运行日志
  • redis中也同样能看到该2条数据
    reids中的数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值