AnalogCache.java 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. package com.bizmatics.service.util;
  2. import com.bizmatics.model.utils.TimeRangeParams;
  3. import com.bizmatics.model.utils.TimeRangeUtils;
  4. import com.github.benmanes.caffeine.cache.Cache;
  5. import com.github.benmanes.caffeine.cache.Caffeine;
  6. import com.bizmatics.persistence.mapper.HtAnalogDataMapper;
  7. import com.github.benmanes.caffeine.cache.stats.CacheStats;
  8. import lombok.RequiredArgsConstructor;
  9. import lombok.extern.slf4j.Slf4j;
  10. import lombok.var;
  11. import org.springframework.scheduling.annotation.Scheduled;
  12. import org.springframework.stereotype.Component;
  13. import java.math.BigDecimal;
  14. import java.util.List;
  15. import java.util.Map;
  16. import java.util.concurrent.TimeUnit;
  17. import java.util.stream.Collectors;
  18. /**
  19. *
  20. * @author fyc
  21. * @email yuchuan.fu@chinausky.com
  22. * @date 2025/8/28
  23. */
  24. @Slf4j
  25. @Component
  26. @RequiredArgsConstructor
  27. public class AnalogCache {
  28. private final HtAnalogDataMapper htAnalogDataMapper;
  29. /**
  30. * 本地缓存:expireAfterWrite = 60 秒
  31. */
  32. private final Cache<String, Map<String, BigDecimal>> cache =
  33. Caffeine.newBuilder()
  34. // 根据设备量自行调节
  35. .maximumSize(5_000)
  36. .expireAfterAccess(2, TimeUnit.MINUTES)
  37. .expireAfterWrite(5, TimeUnit.MINUTES)
  38. // 命中率监控
  39. .recordStats()
  40. .build();
  41. /**
  42. * 对外唯一入口
  43. */
  44. public Map<String, BigDecimal> get(String deviceCode,
  45. TimeRangeUtils.TimeRanges ranges) {
  46. String key = AggCacheKey.of(deviceCode, ranges).signature();
  47. // 如果 key 不存在,自动调用 loadFromDb
  48. return cache.get(key, k -> loadFromDb(deviceCode, ranges));
  49. }
  50. /**
  51. * 回源:真正查数据库
  52. */
  53. private Map<String, BigDecimal> loadFromDb(String deviceCode,
  54. TimeRangeUtils.TimeRanges ranges) {
  55. TimeRangeParams params = TimeRangeParams.builder()
  56. .deviceCode(deviceCode)
  57. .today(ranges.getToday())
  58. .monthStart(ranges.getMonthRange()[0])
  59. .monthEnd(ranges.getMonthRange()[1])
  60. .yearStart(ranges.getYearRange()[0])
  61. .yearEnd(ranges.getYearRange()[1])
  62. .yesterday(ranges.getYesterdayRange()[0])
  63. .lastMonthStart(ranges.getLastMonthRange()[0])
  64. .lastMonthEnd(ranges.getLastMonthRange()[1])
  65. .lastYearStart(ranges.getLastYearRange()[0])
  66. .lastYearEnd(ranges.getLastYearRange()[1])
  67. .sameDayStart(ranges.getSameDayRange()[0])
  68. .sameDayEnd(ranges.getSameDayRange()[1])
  69. .sameMonthStart(ranges.getSameMonthRange()[0])
  70. .sameMonthEnd(ranges.getSameMonthRange()[1])
  71. .sameYearStart(ranges.getSameYearRange()[0])
  72. .sameYearEnd(ranges.getSameYearRange()[1])
  73. .lastYearSameDayStart(ranges.getLastYearSameDay()[0])
  74. .lastYearSameDayEnd(ranges.getLastYearSameDay()[1])
  75. .lastYearSameMonthStart(ranges.getLastYearSameMonth()[0])
  76. .lastYearSameMonthEnd(ranges.getLastYearSameMonth()[1])
  77. .build();
  78. List<Map<String, Object>> rows = htAnalogDataMapper.aggregateAll(params);
  79. return rows.stream()
  80. .collect(Collectors.toMap(
  81. r -> (String) r.get("type"),
  82. r -> new BigDecimal(r.get("value").toString())
  83. ));
  84. }
  85. /* ---------------- 命中率日志(可选) ---------------- */
  86. @Scheduled(fixedDelay = 30_000)
  87. public void logCacheStats() {
  88. CacheStats stats = cache.stats();
  89. long size = cache.estimatedSize();
  90. // 预先格式化数字
  91. String hitRateFormatted = String.format("%.2f", stats.hitRate() * 100);
  92. String avgLoadTimeFormatted = String.format("%.2f", stats.averageLoadPenalty() / 1_000_000.0);
  93. log.info("缓存统计 - 大小: {}, 命中率: {}%, 加载次数: {}, 加载失败: {}, 平均加载时间: {}ms",
  94. size,
  95. hitRateFormatted,
  96. stats.loadCount(),
  97. stats.loadFailureCount(),
  98. avgLoadTimeFormatted);
  99. }
  100. }