依赖jar包

引入包 版本
jdk 1.8
spring boot 2.7.4
spring-boot-starter-data-redis 2.7.4
caffeine 2.9.3
allbs-common 1.1.8
jackson-databind 2.13.4.2
micrometer-core 1.9.4

使用

添加依赖

1
2
3
4
5
<dependency>
<groupId>cn.allbs</groupId>
<artifactId>allbs-cache</artifactId>
<version>1.1.8</version>
</dependency>
1
implementation 'cn.allbs:allbs-cache:1.1.8'
1
implementation("cn.allbs:allbs-cache:1.1.8")

说明

集成了一级caffeine和二级redisred缓存联合使用, key序列化方式为StringRedisSerializer value 序列化方式为GenericJackson2JsonRedisSerializer

开启二级缓存

添加注解@EnableAllbsCache

缓存核心配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
spring:
cache:
multi:
# 指定需要缓存的key
cache-names:
- allbs
# 是否存储空值,默认true,防止缓存穿透
cache-null-values: true
# 是否动态根据cacheName创建Cache的实现,默认true
dynamic: true
# 缓存key的前缀
cache-prefix: ds
caffeine:
# 是否开启caffeine缓存默认为true
enable: false
# 访问后过期时间
expire-after-access: 20m
# 写入后过期时间
expire-after-write: 20m
# 写入后刷新时间
refresh-after-write: 20m
# 初始化大小
initial-capacity: 20
# 最大缓存对象个数,超过此数量时之前放入的缓存将失效
maximum-size: 100
redis:
# 是否开启redis缓存默认为true
enable: true
# 全局过期时间Duration,15s代表过期时间15秒,默认为0不过期
default-expiration: 15s
# 全局空值过期时间Duration,默认和有值的一致
default-null-values-expiration: 15s
# 每个cacheName的过期时间,优先级比defaultExpiration高
expires: {allbs: 15s, testds:60h}
# 缓存更新时通知其他节点的topic名称
topic: cache:redis:caffeine:topic

缓存使用

加入缓存

1
2
3
4
5
6
7
8
9
10
// 添加缓存
@Cacheable(value = CacheConstants.TEST_2, key = "'test'")
@GetMapping("/test")
public String test() {
// 第二次将直接读取缓存,不会进入该方法
String randomStr = RandomUtil.randomString(5);
redisTemplate.opsForValue().set(CacheConstants.TEST_2_1, randomStr);
log.info("返回结果将永远是第一次随机的数据,除非删除redis中的该key" + CacheConstants.TEST_2_1);
return randomStr;
}

加入缓存,但是每次仍然进入方法内部

1
2
// 添加注解
@CachePut

移出缓存

1
2
// 添加注解
@CacheEvict

一个方法或者类上同时指定多个Spring Cache相关的注解

1
@Caching

redis配置示例

yml中添加如下配置,不用在启动类添加注解@EnableAllbsCache

1
2
3
4
5
6
7
8
9
10
spring:
redis:
database: 0
host: ${REDIS-HOST:cq-server}
port: 6379
password: ${REDIS-PWD:111111}
timeout: 5000
lettuce:
pool:
min-idle: 2

自定义序列化方式说明

考虑到项目中实际使用需要使用的序列化方式与缓存不同,所以不再像此前版本定义好redis基础操作的序列化方式,由项目自主决定。同时添加配置可自定义当前项目的缓存的序列化方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
* 类 RedisTemplateConfig
* </p>
*
* @author ChenQi
* @since 2022/10/30 14:49
*/
@Configuration
@AllArgsConstructor
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisTemplateConfig {

/**
* 用于设置redis基础操作的序列化方式
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 使用Jackson2JsonRedisSerialize 替换默认jdk序列化明文储存
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}

/**
* 用于设置缓存中的序列化方法
* @param redisConnectionFactory
* @return
*/
@Bean(name = "stringKeyRedisTemplate")
public RedisTemplate<Object, Object> stringKeyRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 使用Jackson2JsonRedisSerialize 替换默认序列化会明文储存
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}

消息监听

只限于轻量化使用,大规模场景请使用rocketmq、rabbitmq等

方法一

自定义topic, 继承KeyspaceEventMessageListener类并实现ApplicationEventPublisherAware接口。重写doRegister、doHandleMessage、setApplicationEventPublisher方法。多个topic监听可以定义一个Topic List并放到addMessageListener中去即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class CustomMessageListener extends KeyspaceEventMessageListener implements ApplicationEventPublisherAware {
private static final Topic KEYEVENT_EXPIRED_TOPIC = new ChannelTopic("自定义的topic名称");

@Nullable
private ApplicationEventPublisher publisher;

public CustomMessageListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}

@Override
protected void doRegister(RedisMessageListenerContainer listenerContainer) {
listenerContainer.addMessageListener(this, KEYEVENT_EXPIRED_TOPIC);
}

@Override
protected void doHandleMessage(Message message) {
this.publishEvent(new RedisKeyExpiredEvent(message.getBody()));
}

protected void publishEvent(RedisKeyExpiredEvent event) {
if (this.publisher != null) {
this.publisher.publishEvent(event);
}

}

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}

方法二

接收方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
@RequiredArgsConstructor
@Slf4j
public class CustomMessageListenerHandle {

/**
* 方法功能:
*
* @param redisConnectionFactory
* @return org.springframework.data.redis.listener.RedisMessageListenerContainer
* @date 2021/3/29 15:03
*/
@Bean
public RedisMessageListenerContainer redisContainer(RedisConnectionFactory redisConnectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory);
container.addMessageListener((message, bytes) -> {
log.info("接收到监听消息");
// 业务处理
}, new ChannelTopic("customMq"));
return container;
}
}
发送方式
1
redisTemplate.convertAndSend("自定义的topic名称", "发送内容");

过期key监听

继承KeyExpirationEventMessageListener重写onMessage方法即可

过期key和消息监听服务如果集群部署,或者多个服务配置了相同的监听配置,会每个服务都消费一次!!注意业务场景实际使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class KeyExpirationListener extends KeyExpirationEventMessageListener {

public KeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}

@Override
public void onMessage(Message message, @Nullable byte[] pattern) {
String key = message.toString();
System.out.println("监听到key:" + key + "过期");
}

}

发号器,使用redis实现,用于自增的企业编码、站点编码等

注入

1
2
@Resource
private IdGeneratorUtil idGeneratorUtil;

使用

生成id(每日重置自增序列)generateYMDId(String key, int length)
1
2
3
4
5
6
7
8
9
/**
* 生成id(每日重置自增序列)
* 格式:日期 + 指定位位自增数
* 如:20210804000001
*
* @param key 储存序号的redis key
* @param length 生成的后缀长度
* @return 日期 + 指定位位自增数序号
*/
生成指定开头的自增序列 generatePreId(String key, String prefix, int length)
1
2
3
4
5
6
7
8
9
10
/**
* 生成指定开头的自增序列
* 格式: 指定字符 + 指定位自增数
* 如W00001
*
* @param key redis 储存的key
* @param prefix 指定的开头
* @param length 格式化长度
* @return 指定字符 + 指定位自增数
*/
生成指定开头的自增序列 generatePreIdStep(String key, String prefix, int length, int step)
1
2
3
4
5
6
7
8
9
10
/**
* 生成指定开头的自增序列
* 格式: 指定字符 + 指定位自增数
* 如W00001
*
* @param key redis 储存的key
* @param prefix 指定的开头
* @param length 格式化长度
* @return 指定字符 + 指定位自增数
*/
生成指定开头的自增序列 generatePreId(String key, String prefix, int length, int step)
1
2
3
4
5
6
7
8
9
10
11
/**
* 生成指定开头的自增序列
* 格式: 指定字符 + 指定位自增数
* 如W00001
*
* @param key redis 储存的key
* @param prefix 指定的开头
* @param length 格式化长度
* @param step 所在步长
* @return 指定字符 + 指定位自增数
*/
指定带有前缀的发号器初始值 initGenerate(String key, String prefix, int initialValue)
1
2
3
4
5
6
7
/**
* 初始化发号器默认值
*
* @param key redis 的key
* @param prefix 序列化的编码起始字符串
* @param initialValue 初始值
*/
指定发号器初始值 initGenerate(String key, int initialValue)
1
2
3
4
5
6
/**
* 指定发号器初始值
*
* @param key redis 的key
* @param initialValue 初始值
*/