基于Java的分布式缓存策略:某大学10万级交换机风扇巡检系统实战

#『Java分布式系统开发:从理论到实践』征文活动#

一、业务背景

某“双一流”大学拥有 3 个校区、10 000+ 台接入交换机、200 000+ 台终端
每年 6–9 月为高温高发期,风扇故障导致的机房过热告警占比 27.4 %
传统人工巡检 → 4 人/周,仅能覆盖 <5 % 设备;
因此信息中心立项:
“基于 SNMP 的交换机风扇状态分布式实时巡检系统”。


二、技术挑战

维度数值痛点
设备规模10 000+单节点轮询 ≥ 8 h
并发 SNMP Get20 000 req/s单线程阻塞 IO 撑爆
告警延迟<30 s传统 MySQL 写入瓶颈
双活中心异地灾备缓存一致性要求

三、总体架构

四、分布式缓存策略设计

4.1 选型:Redis Cluster vs Hazelcast
指标Redis ClusterHazelcast
水平扩容16384 slotsPartition
语言亲和Java 客户端 Lettuce原生 Java
持久化RDB/AOFMapStore
运维双副本3 副本

最终选择 Redis Cluster 原因:
• 信息中心已有 6 年 Redis 运维经验;
• 风扇状态为 写多读少,Redis 持久化即可满足;
• 跨语言(Python 采集脚本亦可写入)。

4.2 缓存模型
Key 设计示例TTL
设备实时状态switch:{ip}:fan:{index}60 s
告警去重alert:{ip}:{fanIndex}:{status}300 s
统计窗口`agg:{5m30m1h}`滑动窗口
4.3 一致性策略
  • 写穿透(Write-Through):Storm Bolt 写入 Redis 后立即写 MySQL,保证持久化;

  • 读修复(Read-Repair):管理后台每次查询 Redis 未命中时,回源 MySQL 并回填;

  • 最终一致性时间:<5 s(通过 NTP 同步时钟)。

五、核心代码(Spring Boot 3.2 + Lettuce)

5.1 动态分片配置
#ymal
spring:
  redis:
    cluster:
      nodes:
        - 10.10.1.11:6379
        - 10.10.1.12:6379
        - ...
      max-redirects: 3
    timeout: 2000ms
    lettuce:
      pool:
        max-active: 1000
        max-idle: 100
5.2 缓存工具类(含 Lua 脚本原子操作)
@Component
public class FanStatusCache {
    private final StringRedisTemplate redis;

    public FanStatusCache(StringRedisTemplate redis) {
        this.redis = redis;
    }

    private static final String LUA_SET_NX =
        "if redis.call('exists', KEYS[1]) == 0 then " +
        "  redis.call('setex', KEYS[1], ARGV[1], ARGV[2]); " +
        "  return 1; " +
        "else return 0; end";

    // 原子写缓存,防止并发覆盖
    public boolean trySet(String key, String value, long ttl) {
        return Boolean.TRUE.equals(
            redis.execute(
                new DefaultRedisScript<>(LUA_SET_NX, Boolean.class),
                Collections.singletonList(key),
                String.valueOf(ttl),
                value));
    }

    // 批量写入管道化
    public void batchSet(Map<String, String> map, long ttl) {
        redis.executePipelined((RedisCallback<Object>) conn -> {
            map.forEach((k, v) -> conn.stringCommands().setEx(
                k.getBytes(), ttl, v.getBytes()));
            return null;
        });
    }
}
5.3 告警去重逻辑
@Service
public class AlertService {
    @Autowired
    private FanStatusCache cache;

    public void onStatusChange(String ip, int fanIndex, String status) {
        String key = "alert:" + ip + ":" + fanIndex + ":" + status;
        if (cache.trySet(key, "1", 300)) {
            // 首次出现,发送钉钉机器人
            DingTalkClient.send(ip + " 风扇" + fanIndex + " 状态=" + status);
        }
    }
}

六、性能压测

  • 工具:JMeter 5.6 + 自定义 SNMP 插件

  • 场景:模拟 20 000 台交换机 × 2 个风扇 上报

  • 结果:

指标单节点 MySQL引入 Redis 后
QPS1 20022 000
平均延迟430 ms18 ms
告警延迟 99th110 s9 s

七、可视化监控

  • Grafana Dashboard:Redis 连接数、键空间命中率、MySQL 同步延迟

  • Prometheus 指标

redis_cluster_connected_clients{cluster="fan-monitor"}  987
redis_keyspace_hits_total{cluster="fan-monitor"}        1.2e+07
  • 流程图(巡检→告警)

八、踩坑与优化

  1. BigKey 问题
    早期把整台设备 12 个风扇状态拼成 JSON 数组 → 单 Key 15 KB,导致 Redis 分片迁移耗时 4 min。
    解决:拆分为 12 个小 Key,迁移时间降至 8 s。

  2. 时钟漂移
    两个 IDC 时钟差 200 ms,导致告警延迟抖动。
    解决:所有节点接入 NTP + 本地写入时记录服务器时间戳,统一用 System.currentTimeMillis()

  3. Lettuce 重连风暴
    网络闪断后,客户端瞬间创建 >10 000 连接。
    解决

    • 开启 netty.ioRatio=70

    • 设置 maxRedirects=3 限制重试;

    • 接入 Sentinel 做故障转移。


九、上线效果

指标人工时代系统上线 3 个月
故障发现时间平均 6 h平均 11 s
误报率18 %2.3 %
人力成本4 人/周0.2 人/周
机房温度异常停机2 次/月0 次

十、结语 & 展望

本次项目以“分布式缓存 + 流式计算”为核心,将大学网络运维从“人拉肩扛”升级为“秒级智能”。

下一步计划:

  1. 引入 RockDB 作为 Redis 冷数据溢出层,节省 30 % 内存;

  2. 接入 eBPF 采集,细化到 每端口温度

如果本文对你有帮助,别忘了 点赞 + 收藏 + 关注
评论区留下你的分布式缓存难题,下一篇手把手带你解决!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sam-August

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值