Java缓存预热案例

wen java案例 3

本文目录导读:

Java缓存预热案例

  1. 基础缓存预热架构
  2. Spring Boot 缓存预热方案
  3. 分层预热策略
  4. 健康检查与监控
  5. 启动时自动预热
  6. 使用示例

我来为您提供一个完整的Java缓存预热案例,包含多种实现方案。

基础缓存预热架构

1 缓存接口定义

public interface CacheService<K, V> {
    V get(K key);
    void put(K key, V value);
    void clear();
    boolean isPreheated();
}

2 本地缓存实现

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class LocalCacheService<K, V> implements CacheService<K, V> {
    private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
    private final AtomicBoolean preheated = new AtomicBoolean(false);
    @Override
    public V get(K key) {
        return cache.get(key);
    }
    @Override
    public void put(K key, V value) {
        cache.put(key, value);
    }
    @Override
    public void clear() {
        cache.clear();
        preheated.set(false);
    }
    @Override
    public boolean isPreheated() {
        return preheated.get();
    }
    public void setPreheated(boolean preheated) {
        this.preheated.set(preheated);
    }
}

Spring Boot 缓存预热方案

1 数据模型

import lombok.Data;
import java.time.LocalDateTime;
@Data
public class Product {
    private Long id;
    private String name;
    private Double price;
    private Integer stock;
    private String category;
    private LocalDateTime createTime;
}

2 数据源模拟

import org.springframework.stereotype.Repository;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Repository
public class ProductRepository {
    private final Map<Long, Product> database = new ConcurrentHashMap<>();
    @PostConstruct
    public void init() {
        // 模拟数据库数据
        for (long i = 1; i <= 10000; i++) {
            Product product = new Product();
            product.setId(i);
            product.setName("Product-" + i);
            product.setPrice(100.0 + i);
            product.setStock(100);
            product.setCategory("Category-" + (i % 10));
            product.setCreateTime(LocalDateTime.now());
            database.put(i, product);
        }
    }
    public Product findById(Long id) {
        // 模拟数据库查询延迟
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return database.get(id);
    }
    public List<Product> findAll() {
        return new ArrayList<>(database.values());
    }
}

3 缓存预热服务

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
@Service
public class CacheWarmUpService {
    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private LocalCacheService<Long, Product> productCache;
    // 预热线程池
    private final ExecutorService warmUpExecutor = Executors.newFixedThreadPool(10);
    // 预热进度
    private final AtomicInteger progress = new AtomicInteger(0);
    @PostConstruct
    public void initialize() {
        if (!productCache.isPreheated()) {
            warmUpCache();
        }
    }
    /**
     * 全量缓存预热
     */
    public void warmUpCache() {
        log.info("开始缓存预热...");
        long startTime = System.currentTimeMillis();
        List<Product> allProducts = productRepository.findAll();
        int totalSize = allProducts.size();
        // 使用CountDownLatch等待所有预热任务完成
        CountDownLatch latch = new CountDownLatch(totalSize);
        allProducts.forEach(product -> {
            warmUpExecutor.submit(() -> {
                try {
                    // 执行预热
                    productCache.put(product.getId(), product);
                    progress.incrementAndGet();
                } finally {
                    latch.countDown();
                }
            });
        });
        try {
            // 等待所有任务完成,设置超时时间
            boolean completed = latch.await(5, TimeUnit.MINUTES);
            if (completed) {
                productCache.setPreheated(true);
                long endTime = System.currentTimeMillis();
                log.info("缓存预热完成,共加载{}条数据,耗时:{}ms", totalSize, (endTime - startTime));
            } else {
                log.error("缓存预热超时");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("缓存预热被中断", e);
        }
    }
    /**
     * 获取预热进度
     */
    public double getProgress() {
        return (double) progress.get() / productRepository.findAll().size() * 100;
    }
    /**
     * 检查缓存状态
     */
    public boolean isCacheReady() {
        return productCache.isPreheated();
    }
}

分层预热策略

1 热门数据优先预热

import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class SmartCacheWarmUpService {
    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private LocalCacheService<Long, Product> productCache;
    // 热门商品ID(可以从历史数据中获取)
    private final List<Long> hotProductIds = Arrays.asList(1L, 2L, 3L, 4L, 5L, 100L, 200L, 500L);
    /**
     * 分层预热
     * 第一层:热门数据
     * 第二层:常规数据
     * 第三层:冷门数据
     */
    public void tieredWarmUp() {
        log.info("开始分层缓存预热...");
        // 第一层:预热热门数据(立即执行)
        warmUpHotData();
        // 第二层:预热常规数据(延迟执行)
        scheduleWarmUpNormalData(1000); // 延迟1秒
        // 第三层:预热冷门数据(后台执行)
        scheduleWarmUpColdData(5000); // 延迟5秒
    }
    private void warmUpHotData() {
        hotProductIds.parallelStream().forEach(id -> {
            Product product = productRepository.findById(id);
            if (product != null) {
                productCache.put(id, product);
                log.debug("热门商品缓存预热:{}", id);
            }
        });
        log.info("热门数据预热完成");
    }
    private void scheduleWarmUpNormalData(long delayMs) {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                // 预热访问频率较高的数据
                productRepository.findAll().stream()
                    .filter(p -> !hotProductIds.contains(p.getId()))
                    .limit(1000) // 预热前1000条常规数据
                    .forEach(p -> productCache.put(p.getId(), p));
                log.info("常规数据预热完成");
            }
        }, delayMs);
    }
    private void scheduleWarmUpColdData(long delayMs) {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                // 后台预热剩余数据
                CompletableFuture.runAsync(() -> {
                    productRepository.findAll().stream()
                        .filter(p -> productCache.get(p.getId()) == null)
                        .forEach(p -> productCache.put(p.getId(), p));
                    log.info("冷数据预热完成");
                });
            }
        }, delayMs);
    }
}

健康检查与监控

1 缓存健康检查

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicLong;
@Component
public class CacheHealthChecker {
    @Autowired
    private LocalCacheService<Long, Product> productCache;
    @Autowired
    private ProductRepository productRepository;
    private final AtomicLong cacheHitCount = new AtomicLong(0);
    private final AtomicLong cacheMissCount = new AtomicLong(0);
    /**
     * 定时检测缓存命中率
     */
    @Scheduled(fixedRate = 60000) // 每分钟检测一次
    public void checkCacheHealth() {
        if (!productCache.isPreheated()) {
            log.warn("缓存尚未预热完成");
            return;
        }
        // 随机抽样检测
        long testCount = 100;
        long hitCount = 0;
        for (int i = 0; i < testCount; i++) {
            Long randomId = ThreadLocalRandom.current().nextLong(1, 10001);
            Product cachedProduct = productCache.get(randomId);
            Product dbProduct = productRepository.findById(randomId);
            if (cachedProduct != null && cachedProduct.equals(dbProduct)) {
                hitCount++;
            }
        }
        double hitRate = (double) hitCount / testCount;
        if (hitRate < 0.95) {
            log.warn("缓存命中率异常:{}%,需要重新预热", hitRate * 100);
            // 触发重新预热
            triggerReWarmUp();
        } else {
            log.info("缓存健康状态良好,命中率:{}%", hitRate * 100);
        }
    }
    private void triggerReWarmUp() {
        productCache.clear();
        // 执行重新预热逻辑
    }
}

启动时自动预热

1 配置类

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cache.warmup")
public class CacheWarmUpConfig {
    private boolean enabled = true;
    private int threadPoolSize = 10;
    private long timeoutMinutes = 5;
    private String strategy = "full"; // full, tiered, hot_only
    // getters and setters
}

2 启动监听器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class CacheWarmUpListener implements ApplicationListener<ApplicationReadyEvent> {
    @Autowired
    private CacheWarmUpService warmUpService;
    @Autowired
    private SmartCacheWarmUpService smartWarmUpService;
    @Autowired
    private CacheWarmUpConfig warmUpConfig;
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        if (!warmUpConfig.isEnabled()) {
            log.info("缓存预热已禁用");
            return;
        }
        log.info("应用程序已启动,开始执行缓存预热...");
        // 根据配置选择预热策略
        switch (warmUpConfig.getStrategy()) {
            case "full":
                warmUpService.warmUpCache();
                break;
            case "tiered":
                smartWarmUpService.tieredWarmUp();
                break;
            case "hot_only":
                smartWarmUpService.warmUpHotData();
                break;
            default:
                warmUpService.warmUpCache();
        }
    }
}

使用示例

1 application.yml配置

cache:
  warmup:
    enabled: true
    thread-pool-size: 10
    timeout-minutes: 5
    strategy: tiered

2 业务使用示例

@Service
public class ProductService {
    @Autowired
    private LocalCacheService<Long, Product> productCache;
    @Autowired
    private ProductRepository productRepository;
    public Product getProductById(Long id) {
        // 从缓存获取
        Product cachedProduct = productCache.get(id);
        if (cachedProduct != null) {
            return cachedProduct;
        }
        // 缓存未命中,从数据库加载
        Product dbProduct = productRepository.findById(id);
        if (dbProduct != null) {
            productCache.put(id, dbProduct);
        }
        return dbProduct;
    }
}

3 REST API监控

@RestController
@RequestMapping("/api/cache")
public class CacheController {
    @Autowired
    private CacheWarmUpService warmUpService;
    @GetMapping("/status")
    public ResponseEntity<Map<String, Object>> getCacheStatus() {
        Map<String, Object> status = new HashMap<>();
        status.put("preheated", warmUpService.isCacheReady());
        status.put("progress", warmUpService.getProgress());
        return ResponseEntity.ok(status);
    }
    @PostMapping("/warmup")
    public ResponseEntity<String> triggerWarmUp() {
        CompletableFuture.runAsync(() -> warmUpService.warmUpCache());
        return ResponseEntity.accepted().body("缓存预热已启动");
    }
}

这个缓存预热方案包含了:

  1. 多种预热策略:全量预热、分层预热、热点优先
  2. 并发控制:使用线程池和CountDownLatch
  3. 健康监控:定时检查缓存命中率
  4. 失败处理:超时机制和重试逻辑
  5. 配置灵活:通过配置文件控制预热行为
  6. 启动自动执行:利用Spring事件机制

你可以根据实际业务需求选择合适的预热策略,并调整线程池大小、超时时间等参数。

抱歉,评论功能暂时关闭!