跳到主要内容
版本:v2

framework-starter-redis - Redis 自动配置模块

1. 模块概述

framework-starter-redis 是 epoch-framework 中的 Redis 自动配置模块,提供了 Redis 客户端的自动配置、Redis 缓存、分布式锁等功能。该模块基于 Spring Data Redis 实现,通过自动配置简化了 Redis 在 Spring Boot 应用中的集成和使用。

2. 模块结构

framework-starter-redis/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/epoch/framework/starter/redis/
│ │ │ ├── config/ # 自动配置类
│ │ │ ├── cache/ # Redis 缓存实现
│ │ │ ├── lock/ # 分布式锁实现
│ │ │ ├── support/ # 支持工具类
│ │ │ └── enums/ # 枚举定义
│ │ └── resources/
│ │ └── META-INF/
│ │ └── spring.factories # Spring Boot 自动配置入口
│ └── test/ # 测试代码
└── pom.xml # Maven 依赖配置

3. 核心功能

3.1 Redis 客户端自动配置

  • 基于 Spring Data Redis 实现 Redis 客户端自动配置
  • 支持 Lettuce 和 Jedis 客户端
  • 自动配置 RedisTemplate 和 StringRedisTemplate
  • 优化序列化配置,默认使用 JSON 序列化

3.2 Redis 缓存自动配置

  • 自动配置 Redis 缓存管理器
  • 支持缓存注解 @Cacheable、@CachePut、@CacheEvict 等
  • 支持自定义缓存过期时间和序列化方式
  • 支持缓存统计和监控

3.3 分布式锁自动配置

  • 基于 Redis 实现分布式锁
  • 支持可重入锁、公平锁等特性
  • 提供简单易用的锁接口
  • 支持锁超时和自动释放

3.4 Redis 工具类

  • 提供 Redis 常用操作的工具类
  • 简化 Redis 键值对、列表、集合、有序集合等数据结构的操作
  • 提供 Redis 发布订阅功能的封装
  • 支持 Redis 事务操作

4. 配置说明

4.1 Redis 客户端配置

spring:
redis:
host: localhost # Redis 服务器地址
port: 6379 # Redis 服务器端口
password: password # Redis 服务器密码(可选)
database: 0 # Redis 数据库索引
timeout: 5000 # 连接超时时间(毫秒)
lettuce:
pool:
max-active: 8 # 连接池最大连接数
max-wait: -1 # 连接池最大阻塞等待时间(负数表示没有限制)
max-idle: 8 # 连接池中的最大空闲连接数
min-idle: 0 # 连接池中的最小空闲连接数

4.2 Redis 缓存配置

spring:
cache:
type: redis # 缓存类型为 Redis
redis:
time-to-live: 3600000 # 缓存过期时间(毫秒)
cache-null-values: false # 是否缓存 null 值
key-prefix: "epoch:cache:" # 缓存键前缀
use-key-prefix: true # 是否使用键前缀

# 自定义缓存配置
epoch:
framework:
redis:
cache:
default-ttl: 3600 # 默认缓存过期时间(秒)
enable-statistics: true # 是否启用缓存统计

4.3 分布式锁配置

epoch:
framework:
redis:
lock:
default-timeout: 30000 # 默认锁超时时间(毫秒)
default-wait-time: 5000 # 默认获取锁等待时间(毫秒)
enable-reentrant: true # 是否启用可重入锁
enable-fair: false # 是否启用公平锁

5. 使用指南

5.1 引入依赖

<dependency>
<groupId>com.epoch</groupId>
<artifactId>framework-starter-redis</artifactId>
<version>${epoch.version}</version>
</dependency>

5.2 RedisTemplate 使用

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class RedisService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

// 字符串操作
public void stringOperations() {
redisTemplate.opsForValue().set("key", "value");
String value = (String) redisTemplate.opsForValue().get("key");
redisTemplate.opsForValue().increment("counter");
}

// 哈希操作
public void hashOperations() {
Map<String, Object> map = new HashMap<>();
map.put("field1", "value1");
map.put("field2", "value2");
redisTemplate.opsForHash().putAll("hashKey", map);
Object fieldValue = redisTemplate.opsForHash().get("hashKey", "field1");
}

// 列表操作
public void listOperations() {
redisTemplate.opsForList().leftPush("listKey", "value1");
redisTemplate.opsForList().rightPush("listKey", "value2");
List<Object> list = redisTemplate.opsForList().range("listKey", 0, -1);
}

// 集合操作
public void setOperations() {
redisTemplate.opsForSet().add("setKey", "value1", "value2", "value3");
Set<Object> set = redisTemplate.opsForSet().members("setKey");
redisTemplate.opsForSet().remove("setKey", "value1");
}

// 有序集合操作
public void zsetOperations() {
redisTemplate.opsForZSet().add("zsetKey", "value1", 1.0);
redisTemplate.opsForZSet().add("zsetKey", "value2", 2.0);
Set<Object> zset = redisTemplate.opsForZSet().range("zsetKey", 0, -1);
}
}

5.3 Redis 缓存使用

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

// 使用 Redis 缓存
@Cacheable(value = "user", key = "#id")
public User getUserById(Long id) {
// 从数据库查询用户信息
return userRepository.findById(id).orElse(null);
}

// 更新缓存
@CachePut(value = "user", key = "#user.id")
public User updateUser(User user) {
return userRepository.save(user);
}

// 删除缓存
@CacheEvict(value = "user", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}

5.4 分布式锁使用

import com.epoch.framework.starter.redis.lock.RedisLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

@Autowired
private RedisLock redisLock;

public void createOrder(Long userId, Long productId) {
String lockKey = "order:create:" + productId;

try {
// 尝试获取锁,等待时间 5 秒,锁超时时间 10 秒
if (redisLock.tryLock(lockKey, 5, 10, TimeUnit.SECONDS)) {
// 执行业务逻辑
System.out.println("获取到锁,开始创建订单");
// ... 创建订单逻辑
} else {
// 获取锁失败
System.out.println("获取锁失败,请稍后重试");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("获取锁被中断");
} finally {
// 释放锁
redisLock.unlock(lockKey);
}
}

// 使用可重入锁
public void processOrder(Long orderId) {
String lockKey = "order:process:" + orderId;

try {
redisLock.lock(lockKey, 10, TimeUnit.SECONDS);
// 执行业务逻辑
processStep1(orderId);
processStep2(orderId);
} finally {
redisLock.unlock(lockKey);
}
}

private void processStep1(Long orderId) {
String lockKey = "order:process:" + orderId;
try {
redisLock.lock(lockKey, 10, TimeUnit.SECONDS);
// 执行步骤1
System.out.println("执行步骤1");
} finally {
redisLock.unlock(lockKey);
}
}

private void processStep2(Long orderId) {
String lockKey = "order:process:" + orderId;
try {
redisLock.lock(lockKey, 10, TimeUnit.SECONDS);
// 执行步骤2
System.out.println("执行步骤2");
} finally {
redisLock.unlock(lockKey);
}
}
}

5.5 Redis 工具类使用

import com.epoch.framework.starter.redis.support.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RedisToolService {

@Autowired
private RedisUtils redisUtils;

public void useRedisUtils() {
// 字符串操作
redisUtils.set("key", "value", 3600);
String value = redisUtils.get("key");

// 哈希操作
redisUtils.hSet("hashKey", "field", "value");
Object fieldValue = redisUtils.hGet("hashKey", "field");

// 列表操作
redisUtils.lPush("listKey", "value1", "value2");
List<Object> list = redisUtils.lRange("listKey", 0, -1);

// 集合操作
redisUtils.sAdd("setKey", "value1", "value2");
Set<Object> set = redisUtils.sMembers("setKey");

// 有序集合操作
redisUtils.zAdd("zsetKey", "value1", 1.0);
Set<Object> zset = redisUtils.zRange("zsetKey", 0, -1);

// 发布订阅
redisUtils.publish("channel", "message");
}
}

6. 自定义配置

6.1 自定义 RedisTemplate

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

@Bean
public RedisTemplate<String, Object> customRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);

// 使用 StringRedisSerializer 序列化和反序列化 redis 的 key 值
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);

// 使用 GenericJackson2JsonRedisSerializer 序列化和反序列化 redis 的 value 值
Jackson2JsonRedisSerializer<Object> valueSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
template.setValueSerializer(valueSerializer);
template.setHashValueSerializer(valueSerializer);

template.afterPropertiesSet();
return template;
}
}

6.2 自定义 Redis 缓存配置

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableCaching
public class CacheConfig {

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues();

Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
cacheConfigurations.put("user", config.entryTtl(Duration.ofDays(1)));
cacheConfigurations.put("product", config.entryTtl(Duration.ofHours(2)));

return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.withInitialCacheConfigurations(cacheConfigurations)
.build();
}
}

6.3 自定义分布式锁配置

import com.epoch.framework.starter.redis.lock.RedisLock;
import com.epoch.framework.starter.redis.lock.RedisLockImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
public class LockConfig {

@Bean
public RedisLock redisLock(RedisTemplate<String, Object> redisTemplate) {
RedisLockImpl lock = new RedisLockImpl();
lock.setRedisTemplate(redisTemplate);
lock.setDefaultWaitTime(5); // 默认等待时间 5 秒
lock.setDefaultExpireTime(10); // 默认锁超时时间 10 秒
lock.setEnableReentrant(true); // 启用可重入锁
return lock;
}
}

7. 最佳实践

7.1 序列化方式选择

StringRedisTemplate:适用于键值都是字符串的场景

RedisTemplate<String, Object>:适用于键为字符串、值为对象的场景

自定义序列化器:根据实际需求选择合适的序列化方式,如 JSON、Protobuf 等

7.2 缓存使用建议

合理设置缓存过期时间:避免缓存数据过期时间过长或过短 使用缓存键前缀:防止不同应用或模块之间的缓存键冲突 禁止缓存 null 值:避免缓存穿透问题 使用缓存统计:监控缓存命中率和性能

7.3 分布式锁使用建议

合理设置锁超时时间:避免锁超时时间过长导致死锁 确保释放锁:使用 try-finally 确保锁被释放 避免锁竞争:减小锁的粒度,避免长时间持有锁 使用可重入锁:在需要的场景下使用可重入锁

7.4 性能优化建议

使用连接池:配置合理的 Redis 连接池大小 批量操作:使用 Redis 的批量操作减少网络请求 使用管道:对于多个独立的命令,使用 Redis 管道提高性能 避免大键:避免存储过大的键值对,影响性能

7.5 安全配置建议

设置 Redis 密码:防止未授权访问 限制 Redis 访问 IP:通过防火墙限制 Redis 只允许特定 IP 访问 使用 Redis ACL:在 Redis 6.0+ 中使用 ACL 进行更精细的权限控制 加密传输:对于敏感数据,使用 SSL/TLS 加密 Redis 连接

8. 常见问题

8.1 Redis 连接失败

原因:Redis 服务器地址或端口配置错误、Redis 服务器未启动、网络连接失败等。

解决方案: 检查 Redis 服务器地址和端口配置是否正确 确保 Redis 服务器已启动 检查网络连接是否正常 检查 Redis 服务器密码(如果有)是否正确

8.2 缓存数据不一致

原因:缓存更新不及时、缓存失效策略不合理、并发更新等。

解决方案: 采用合适的缓存更新策略(如双写一致性、失效一致性等) 设置合理的缓存过期时间 使用分布式锁控制并发更新 定期验证缓存数据一致性

8.3 分布式锁死锁

原因:锁未正确释放、锁超时时间设置不合理、应用异常退出等。

解决方案: 确保在 finally 块中释放锁 设置合理的锁超时时间 实现锁的自动过期机制 使用看门狗机制延长锁的有效期

8.4 Redis 性能问题

原因:大键值对、频繁的小命令、连接池配置不合理等。

解决方案: 避免存储大键值对 使用批量操作减少网络请求 配置合理的连接池大小 监控 Redis 性能指标,及时优化

8.5 序列化/反序列化错误

原因:序列化方式不匹配、对象结构变化等。

解决方案: 确保序列化和反序列化使用相同的方式 对于对象结构变化,使用兼容的序列化方式 考虑使用版本控制或其他方式处理对象结构变化

9. 版本变更

9.1 主要变更

  • 3.4.0

    • 优化 Redis 连接池配置
    • 完善 Redis 缓存自动配置
    • 优化分布式锁实现
    • 支持更多序列化方式
  • 3.3.0

    • 新增 Redis 自动配置模块
    • 实现 RedisTemplate 自动配置
    • 实现 Redis 缓存自动配置
    • 实现分布式锁基本功能
  • 3.2.0

    • 初始版本,提供 Redis 自动配置和缓存支持