别再new RedisService了!分享一个我司生产在用的Spring Boot Redis工具类(支持分布式锁与缓存注解)

张开发
2026/4/19 16:57:23 15 分钟阅读

分享文章

别再new RedisService了!分享一个我司生产在用的Spring Boot Redis工具类(支持分布式锁与缓存注解)
从零构建生产级Spring Boot Redis工具类分布式锁与缓存注解实战在微服务架构成为主流的今天Redis早已从简单的缓存中间件升级为分布式系统的核心基础设施。但很多团队在使用Spring Boot集成Redis时依然停留在基础的CRUD封装层面或是直接使用RedisTemplate导致代码重复且难以维护。本文将分享一个经过线上千万级流量验证的Redis工具类设计方案它不仅封装了通用操作更整合了分布式锁、缓存注解、故障降级等生产级特性。1. 工具类基础架构设计一个生产可用的Redis工具类首先要解决序列化混乱和API不统一的问题。我们采用泛型函数式编程的方式构建类型安全的操作接口。以下是核心设计要点public class RedisOperatorT { private final RedisTemplateString, T redisTemplate; private final ValueOperationsString, T valueOps; public RedisOperator(RedisTemplateString, T redisTemplate) { this.redisTemplate redisTemplate; this.valueOps redisTemplate.opsForValue(); } public OptionalT get(String key) { return Optional.ofNullable(valueOps.get(key)); } public void set(String key, T value, Duration timeout) { valueOps.set(key, value, timeout); } }关键改进点使用Optional避免NPE问题采用Duration替代原始的longTimeUnit参数通过泛型保证类型安全序列化配置建议采用混合模式Key使用String序列化Value使用JSONConfiguration public class RedisConfig { Bean public RedisTemplateString, Object redisTemplate( RedisConnectionFactory factory) { RedisTemplateString, Object template new RedisTemplate(); template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); template.setValueSerializer(RedisSerializer.json()); template.setHashValueSerializer(RedisSerializer.json()); template.setConnectionFactory(factory); return template; } }2. 分布式锁的工业级实现基于Redis的分布式锁需要考虑四个核心问题原子性、过期时间、误删保护和可重入性。我们整合Redisson实现企业级方案public class DistributedLockService { private final RedissonClient redisson; public T T executeWithLock(String lockKey, Duration waitTime, Duration leaseTime, SupplierT supplier) { RLock lock redisson.getLock(lockKey); try { if (lock.tryLock(waitTime.toMillis(), leaseTime.toMillis(), TimeUnit.MILLISECONDS)) { return supplier.get(); } throw new LockAcquisitionException(获取锁失败); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new LockAcquisitionException(锁获取被中断, e); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } }生产环境必须关注的要点问题类型解决方案配置建议锁续期使用Redisson的watchdog机制默认30秒检测一次锁等待设置合理的waitTime根据业务RT设置锁释放增加持有线程检查isHeldByCurrentThread集群故障转移采用RedLock算法至少3个独立Redis实例提示对于金融级场景建议结合数据库唯一约束作为兜底方案3. 增强型缓存注解集成Spring Cache的标准注解功能有限我们通过自定义注解扩展了多项实用特性Caching( put CachePut(value users, key #user.id), cacheable Cacheable(value users, key #id) ) Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface UserCache { boolean nullValue() default false; String condition() default ; String unless() default ; }实现多级缓存策略的配置示例Configuration EnableCaching public class CacheConfig extends CachingConfigurerSupport { Bean public CacheManager cacheManager(RedisConnectionFactory factory) { return RedisCacheManager.builder(factory) .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(SerializationPair.fromSerializer(RedisSerializer.json())) .entryTtl(Duration.ofMinutes(30))) .withInitialCacheConfigurations(Map.of( users, RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofHours(1)), products, RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(15)) )).build(); } }缓存穿透解决方案对比空值缓存简单但可能污染缓存Bloom过滤器内存效率高但实现复杂接口限流保护下游但影响用户体验4. 生产环境调优实战高并发场景下的Redis配置需要特别关注连接池和集群模式spring: redis: lettuce: pool: max-active: 16 max-idle: 8 min-idle: 4 max-wait: 2000ms cluster: nodes: 10.0.0.1:6379,10.0.0.2:6379 max-redirects: 3 timeout: 1000ms哨兵模式下的故障转移配置Bean public RedisConnectionFactory redisConnectionFactory() { RedisSentinelConfiguration config new RedisSentinelConfiguration() .master(mymaster) .sentinel(sentinel1, 26379) .sentinel(sentinel2, 26379); return new LettuceConnectionFactory(config); }性能监控指标清单连接池活跃连接数命令执行P99延迟缓存命中率锁竞争频率内存碎片率在Kubernetes环境中还需要处理Redis的拓扑变化# 通过Headless Service发现集群节点 kubectl get endpoints redis-cluster -o jsonpath{.subsets[*].addresses[*].ip}5. 高级特性实现对于需要强一致性的场景我们实现了分布式计数器public class RedisAtomicCounter { private final RedisTemplateString, Long redisTemplate; public long increment(String key, long delta) { return redisTemplate.opsForValue() .increment(key, delta); } public long get(String key) { return Optional.ofNullable(redisTemplate.opsForValue().get(key)) .orElse(0L); } }实现发布订阅模式的消息队列public class RedisMessagePublisher { private final RedisTemplateString, Object redisTemplate; public void publish(String channel, Object message) { redisTemplate.convertAndSend(channel, message); } } Component public class RedisMessageListener { RedisListener(topics order-events) public void handleOrderEvent(OrderEvent event) { // 处理订单事件 } }批量操作性能对比测试数据操作方式1000次操作耗时网络请求次数单条命令循环1250ms1000Pipeline批量320ms1Lua脚本280ms1实际项目中我们最终采用的方案是在工具类中同时支持三种模式根据数据量自动切换public T ListT multiGet(ListString keys, ClassT type) { if (keys.size() 100) { return redisTemplate.executePipelined(...); } else if (keys.size() 10) { return redisTemplate.execute(...); } else { return keys.stream().map(k - get(k, type)).collect(...); } }

更多文章