diff --git a/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCache.java b/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCache.java index 819a726e..15198892 100644 --- a/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCache.java +++ b/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCache.java @@ -37,9 +37,9 @@ public class RedisProCache extends RedisCache { /** - * 指标写侧 seam — 6 个 metric 的注册 + null-safe 记录 + 快照读取全部收口在本字段。 + * 指标写侧 seam — 7 个 metric 的注册 + null-safe 记录 + 快照读取全部收口在本字段。 * - *

{@code MeterRegistry} 缺失时本字段构造为空 registry(全部 6 字段为 null),record 方法全 no-op。 + *

{@code MeterRegistry} 缺失时本字段构造为空 registry(全部 7 字段为 null),record 方法全 no-op。 */ private final RedisProCacheMetricsRegistry metricsRegistry; @@ -69,7 +69,7 @@ public class RedisProCache extends RedisCache { * *

构造期委派 3 个 deep seam: *

    - *
  1. {@link RedisProCacheMetricsRegistry} — 6 metric 注册
  2. + *
  3. {@link RedisProCacheMetricsRegistry} — 7 metric 注册
  4. *
  5. {@link CacheOperationResolver} — operation 解析
  6. *
  7. {@link LoaderOrchestrator} — loader 路径编排
  8. *
diff --git a/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCacheMetricsRegistry.java b/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCacheMetricsRegistry.java index dc04357d..603f710b 100644 --- a/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCacheMetricsRegistry.java +++ b/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCacheMetricsRegistry.java @@ -22,11 +22,9 @@ *

与 {@link CacheMetrics} 的对称性:本 seam 是"写侧"(注册 + 记录),与读侧 * 快照({@link CacheMetrics})配对形成指标领域 完整边界。读侧只读、写侧只写 — 关注点分离。 * - *

与 {@link RedisProCacheTimers} 的关系:{@link RedisProCacheTimers} 是 metric 原语 - * helper({@code registerTimer} / {@code registerCounter} / {@code timed} / {@code timedGet} / - * {@code safeIncrement}),本身是工具类无状态。本类是 metric 集合的容器(6 字段 + 6 - * 业务语义方法),把原语按"缓存实例"的语义组装起来。两层 seam 形成 composition — 删除任意 - * 一层,复杂度上浮。 + *

本类在 metric ownership seam 内部封装注册、自增与计时原语,并按缓存实例语义组装。 + * record 方法复用这些私有 helper;recordPut/recordEvict 保留计时启用时将 operation counter + * 放在 finally 中的局部结构,以维持异常路径的计数语义。 * *

公开方法(业务语义): *

* - *

null-safe 语义:{@link MeterRegistry} 为 null 时(即未启用指标),全部 6 个内部 + *

null-safe 语义:{@link MeterRegistry} 为 null 时(即未启用指标),全部 7 个内部 * 字段为 null,所有 record 方法走 no-op 路径。 * *

线程安全:本类仅在 cache 构造期由单线程初始化;运行期 record 方法调 @@ -69,7 +67,7 @@ final class RedisProCacheMetricsRegistry { private final String cacheName; - // 写侧 6 字段:3 Timer + 4 Counter + // 写侧 7 字段:3 Timer + 4 Counter // 注:clear 路径无 Counter(batch 操作语义不适合计数),仅 Timer @Nullable private final Timer getTimer; @@ -87,22 +85,21 @@ final class RedisProCacheMetricsRegistry { private final Counter evictCounter; /** - * 构造期一次性注册 6 个 metric — 在 cache 构造期调用一次,运行期 record 路径直接复用。 + * 构造期一次性注册 7 个 metric — 在 cache 构造期调用一次,运行期 record 路径直接复用。 * - *

内部委派 {@link RedisProCacheTimers} 原语(registerTimer / registerCounter)保证 null-safe 语义。 - * - * @param meterRegistry Micrometer 注册表(可为 null → 全部 6 字段为 null) + *

内部注册 helper 保证 {@code meterRegistry == null} 时所有字段保持 null。 + * @param meterRegistry Micrometer 注册表(可为 null → 全部 7 字段为 null) * @param cacheName cache 标识,作为 {@code tags("cache", cacheName)} 写入每个 metric */ public RedisProCacheMetricsRegistry(@Nullable MeterRegistry meterRegistry, String cacheName) { this.cacheName = cacheName; - this.getTimer = RedisProCacheTimers.registerTimer(meterRegistry, TIMER_GET, DESC_GET_TIMER, cacheName); - this.putTimer = RedisProCacheTimers.registerTimer(meterRegistry, TIMER_PUT, DESC_PUT_TIMER, cacheName); - this.evictTimer = RedisProCacheTimers.registerTimer(meterRegistry, TIMER_EVICT, DESC_EVICT_TIMER, cacheName); - this.hitCounter = RedisProCacheTimers.registerCounter(meterRegistry, COUNTER_HIT, DESC_HIT, cacheName); - this.missCounter = RedisProCacheTimers.registerCounter(meterRegistry, COUNTER_MISS, DESC_MISS, cacheName); - this.putCounter = RedisProCacheTimers.registerCounter(meterRegistry, COUNTER_PUT, DESC_PUT, cacheName); - this.evictCounter = RedisProCacheTimers.registerCounter(meterRegistry, COUNTER_EVICT, DESC_EVICT, cacheName); + this.getTimer = registerTimer(meterRegistry, TIMER_GET, DESC_GET_TIMER, cacheName); + this.putTimer = registerTimer(meterRegistry, TIMER_PUT, DESC_PUT_TIMER, cacheName); + this.evictTimer = registerTimer(meterRegistry, TIMER_EVICT, DESC_EVICT_TIMER, cacheName); + this.hitCounter = registerCounter(meterRegistry, COUNTER_HIT, DESC_HIT, cacheName); + this.missCounter = registerCounter(meterRegistry, COUNTER_MISS, DESC_MISS, cacheName); + this.putCounter = registerCounter(meterRegistry, COUNTER_PUT, DESC_PUT, cacheName); + this.evictCounter = registerCounter(meterRegistry, COUNTER_EVICT, DESC_EVICT, cacheName); } // ==================== 业务方法(get / put / evict / clear) ==================== @@ -120,21 +117,21 @@ public RedisProCacheMetricsRegistry(@Nullable MeterRegistry meterRegistry, Strin * @return body.get() 的结果 */ public T recordGet(Supplier body) { - return RedisProCacheTimers.timedGet(getTimer, body); + return timedGet(getTimer, body); } /** * 记录 hit 计数(get 返回非 null 时调用)— null-safe。 */ public void recordHit() { - RedisProCacheTimers.safeIncrement(hitCounter); + safeIncrement(hitCounter); } /** * 记录 miss 计数(get 返回 null 时调用)— null-safe。 */ public void recordMiss() { - RedisProCacheTimers.safeIncrement(missCounter); + safeIncrement(missCounter); } /** @@ -145,7 +142,7 @@ public void recordMiss() { public void recordPut(Runnable body) { if (putTimer == null) { body.run(); - increment(putCounter); + safeIncrement(putCounter); return; } long start = System.nanoTime(); @@ -153,7 +150,7 @@ public void recordPut(Runnable body) { body.run(); } finally { putTimer.record(System.nanoTime() - start, TimeUnit.NANOSECONDS); - increment(putCounter); + safeIncrement(putCounter); } } @@ -165,7 +162,7 @@ public void recordPut(Runnable body) { public void recordEvict(Runnable body) { if (evictTimer == null) { body.run(); - increment(evictCounter); + safeIncrement(evictCounter); return; } long start = System.nanoTime(); @@ -173,7 +170,7 @@ public void recordEvict(Runnable body) { body.run(); } finally { evictTimer.record(System.nanoTime() - start, TimeUnit.NANOSECONDS); - increment(evictCounter); + safeIncrement(evictCounter); } } @@ -183,7 +180,7 @@ public void recordEvict(Runnable body) { * @param body 实际的 clear 操作(不可为 null) */ public void recordClear(Runnable body) { - RedisProCacheTimers.timed(evictTimer, body); + timed(evictTimer, body); } /** @@ -212,8 +209,57 @@ String cacheName() { // ==================== 私有 helper ==================== - private static void increment(@Nullable Counter counter) { - RedisProCacheTimers.safeIncrement(counter); + private static Timer registerTimer(@Nullable MeterRegistry registry, String name, + String description, String cacheName) { + if (registry == null) { + return null; + } + return Timer.builder(name) + .tag("cache", cacheName) + .description(description) + .register(registry); + } + + private static Counter registerCounter(@Nullable MeterRegistry registry, String name, + String description, String cacheName) { + if (registry == null) { + return null; + } + return Counter.builder(name) + .tag("cache", cacheName) + .description(description) + .register(registry); + } + + private static void safeIncrement(@Nullable Counter counter) { + if (counter != null) { + counter.increment(); + } + } + + private static void timed(@Nullable Timer timer, Runnable body) { + if (timer == null) { + body.run(); + return; + } + long start = System.nanoTime(); + try { + body.run(); + } finally { + timer.record(System.nanoTime() - start, TimeUnit.NANOSECONDS); + } + } + + private static T timedGet(@Nullable Timer timer, Supplier body) { + if (timer == null) { + return body.get(); + } + long start = System.nanoTime(); + try { + return body.get(); + } finally { + timer.record(System.nanoTime() - start, TimeUnit.NANOSECONDS); + } } private static long countOf(@Nullable Counter counter) { diff --git a/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCacheTimers.java b/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCacheTimers.java deleted file mode 100644 index 14ff7b86..00000000 --- a/src/main/java/io/github/davidhlp/spring/cache/redis/cache/RedisProCacheTimers.java +++ /dev/null @@ -1,152 +0,0 @@ -package io.github.davidhlp.spring.cache.redis.cache; - - - - - -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Timer; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -/** - * 缓存层 timing & metric 注册与调用的单一 seam. - * - *

把 null-safe timer/counter 行为封装在四个静态入口: - *

- * - *

行为保真: - *

- * - *

接口是测试面:本类四个方法是单一测试目标。新增 metric(hit-ratio / 复合 timer 等) - * 只在 seam 内扩展,不污染调用点。 - * - *

deletion test:删本类 → timing/counter 样板在调用点重现 → 真 seam。 - * - * @see RedisProCache - */ -final class RedisProCacheTimers { - - private RedisProCacheTimers() { - // 工具类,不可实例化 - } - - /** - * 注册 Timer.{@code registry == null} 时返回 {@code null}(由 {@link #timed} / - * {@link #timedGet} 静默 no-op 吸收)。 - * - * @param registry Micrometer 注册中心,生产可为 null - * @param name Timer 名,如 {@code "resicache.cache.get"} - * @param description Timer 描述 - * @param cacheName cache tag 值,用于 {@code tags("cache", cacheName)} - * @return 注册成功的 Timer;{@code registry == null} 时返回 null - */ - static Timer registerTimer(MeterRegistry registry, String name, - String description, String cacheName) { - if (registry == null) { - return null; - } - return Timer.builder(name) - .tag("cache", cacheName) - .description(description) - .register(registry); - } - - /** - * 注册 Counter.{@code registry == null} 时返回 {@code null}(由 - * {@link #safeIncrement} 静默 no-op 吸收)。 - * - * @param registry Micrometer 注册中心,生产可为 null - * @param name Counter 名,如 {@code "resicache.cache.hit"} - * @param description Counter 描述 - * @param cacheName cache tag 值,用于 {@code tags("cache", cacheName)} - * @return 注册成功的 Counter;{@code registry == null} 时返回 null - */ - static Counter registerCounter(MeterRegistry registry, String name, - String description, String cacheName) { - if (registry == null) { - return null; - } - return Counter.builder(name) - .tag("cache", cacheName) - .description(description) - .register(registry); - } - - /** - * Counter null-safe increment.{@code counter == null} 时静默 no-op(等价于 - * {@link RedisProCache#metrics()} 对 null Counter 返回 {@code 0L} 的零返回值路径)。 - * - * @param counter 待自增 Counter,生产可为 null - */ - static void safeIncrement(Counter counter) { - if (counter != null) { - counter.increment(); - } - } - - /** - * void body 用 timing wrapper —— 语义与 try-finally 等价: - *

- * - * @param timer 待记录 Timer,生产可为 null - * @param body 待执行的操作,可抛出 RuntimeException - */ - static void timed(Timer timer, Runnable body) { - if (timer == null) { - body.run(); - return; - } - long start = System.nanoTime(); - try { - body.run(); - } finally { - timer.record(System.nanoTime() - start, TimeUnit.NANOSECONDS); - } - } - - /** - * 返回值 body 用 timing wrapper —— 语义与 try-finally 等价: - * - * - *

调用方如需把 body 异常翻译为 {@code Cache.ValueRetrievalException} 或自增 miss 计数, - * 应当在本 {@code timedGet} 之外再套一层 try-catch —— 调用点的 catch 与本类的 - * {@code finally} 互不干扰。 - * - * @param 返回值类型 - * @param timer 待记录 Timer,生产可为 null - * @param body 待执行的操作,可抛出任意 Exception - * @return body.get() 的结果 - */ - static T timedGet(Timer timer, Supplier body) { - if (timer == null) { - return body.get(); - } - long start = System.nanoTime(); - try { - return body.get(); - } finally { - timer.record(System.nanoTime() - start, TimeUnit.NANOSECONDS); - } - } -} diff --git a/src/main/java/io/github/davidhlp/spring/cache/redis/cache/metrics/CacheMetrics.java b/src/main/java/io/github/davidhlp/spring/cache/redis/cache/metrics/CacheMetrics.java index ada766e3..d4dcfc50 100644 --- a/src/main/java/io/github/davidhlp/spring/cache/redis/cache/metrics/CacheMetrics.java +++ b/src/main/java/io/github/davidhlp/spring/cache/redis/cache/metrics/CacheMetrics.java @@ -11,7 +11,8 @@ *