jetcache icon indicating copy to clipboard operation
jetcache copied to clipboard

两级缓存不支持缓存预热

Open zhengsh opened this issue 9 months ago • 4 comments

问题:

  • 两级缓存,本地缓存 linkedhashmap 和远程缓存 redis, 我在服务启动的时候希望能够自动将 redis 中已经缓存的数据预热刷新到 local cache 里面,
  • 我看了 cache 里面的方法, getall 需要通过 key 集合去获取,我这边 key 大概有 50w 个,如果我去查询数据库来构建这个 key 的话,查询的时间比较长。
  • 希望能够提供一种方法,或者 api 去通过将 redis 已经暂存的缓存,刷到 local cache 里面去

代码:

import com.alibaba.fastjson2.JSON;
import com.alicp.jetcache.*;
import com.alicp.jetcache.embedded.LinkedHashMapCache;
import com.alicp.jetcache.embedded.LinkedHashMapCacheBuilder;
import com.alicp.jetcache.redis.lettuce.RedisLettuceCacheBuilder;
import com.alicp.jetcache.support.*;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@SpringBootApplication
public class Main {


    private Cache<String, Order> orderCache;

    @PostConstruct
    public void init() throws InterruptedException {

       // todo 初始化 redis 缓存到 local cache 里面

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //内存缓存,读取 hashmap
        Cache<String, Order> memoryCache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                //缓存 null
                .cacheNullValue(true)
                .limit(100)
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();

        //空缓redis缓存
        RedisClient redisClient =
                //这里可以直接配置 slave
                RedisClient.create("redis://127.0.0.1");
        redisClient.setOptions(ClientOptions.builder().
                disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                .build());

        Cache<String, String> redisCache = RedisLettuceCacheBuilder.createRedisLettuceCacheBuilder()
                .keyConvertor(Fastjson2KeyConvertor.INSTANCE)
                //缓存 null
                .cacheNullValue(true)
                //key 前缀
                .keyPrefix("c:p:")
                //广播
                .broadcastChannel("c:h:")
                //value 编码
                .valueEncoder(Fastjson2ValueEncoder.INSTANCE)
                //value 解码
                .valueDecoder(Fastjson2ValueDecoder.INSTANCE)
                //过期时间
                .expireAfterWrite(3600, TimeUnit.SECONDS)
                .addMonitor(event -> System.out.println("date=" + getDate(dateFormat) + "cache event=" + JSON.toJSONString(event)))
                //redis client
                .redisClient(redisClient)
                .buildCache();

        orderCache = MultiLevelCacheBuilder.createMultiLevelCacheBuilder()
                .addCache(memoryCache, redisCache)
                .expireAfterWrite(100, TimeUnit.SECONDS)
                .buildCache();


        MultiGetResult<String, Order> allResult = orderCache.GET_ALL(new HashSet<>());

        Order value = createOrder();
        orderCache.PUT("bp001", value);

        soutLocalCacheAll(dateFormat, memoryCache);

        int i = 1;

        while (i++ < 100) {
            Order bp001 = orderCache.get("bp001");
            System.out.println("date=" + getDate(dateFormat) + ", p=" + getOrderToString(bp001));

            if (i == 2) {
                orderCache.put("bp" + i, createOrder());
            }

            if (i == 3) {
                orderCache.put("bp" + i, createOrder());
            }

            soutLocalCacheAll(dateFormat, memoryCache);

            Thread.sleep(1000L);
        }
    }

    private static void soutLocalCacheAll(SimpleDateFormat dateFormat, Cache<String, Order> memoryCache) {
        if (memoryCache instanceof LinkedHashMapCache) {
            LinkedHashMapCache<String, CacheValueHolder<Order>> linkHashMapCache = (LinkedHashMapCache) memoryCache;
            LinkedHashMap<String, CacheValueHolder<Order>> map = linkHashMapCache.unwrap(LinkedHashMap.class);
            String strPosi = map.values().stream().map((CacheValueHolder<Order> h) -> {
                Order p = h.getValue();
                return getOrderToString(p);
            }).collect(Collectors.joining(",", "[", "]"));
            System.out.println("date=" + getDate(dateFormat) + ", allP=" + strPosi);
        }
    }

    private static String getDate(SimpleDateFormat dateFormat) {
        return dateFormat.format(new Date());
    }

    private static String getOrderToString(Order p) {
        return "p=" + p.getPrice() + ",q=" + p.getQuantity();
    }

    private static Order createOrder() {
        Random random = new Random(System.currentTimeMillis());
        int randomCount = random.nextInt(100);
        Order value = new Order();
        value.setQuantity(randomCount * 1000);
        value.setPrice(new BigDecimal(randomCount));
        return value;
    }

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }

    public static class Order {

        private BigDecimal price;

        private Integer quantity;

        public BigDecimal getPrice() {
            return price;
        }

        public void setPrice(BigDecimal price) {
            this.price = price;
        }

        public Integer getQuantity() {
            return quantity;
        }

        public void setQuantity(Integer quantity) {
            this.quantity = quantity;
        }
    }

}

zhengsh avatar Nov 14 '23 03:11 zhengsh

这缺少普遍适用的方法来实现它,或者能实现它的方法在大多数情况下不是好的实践,所以没有这个能力。

areyouok avatar Nov 14 '23 07:11 areyouok

理解,确实这个是一个特殊的场景,因为大多数情况下,确实不需要预热全部的缓存数据

zhengsh avatar Nov 15 '23 13:11 zhengsh

mark

HealerJean avatar Nov 21 '23 12:11 HealerJean

mark

wp973 avatar Feb 21 '24 09:02 wp973