分布式缓存基于 redis 进行设计,在 Framework 设计中,一方面是支持缓存配置动态刷新加载;另一方面是缓存管理能力针对一些特定场景进行增强,如加密、解密可以支持国密算法、缓存磁盘、缓存监控如发现大 key(Big keys)。
分布式缓存抽象设计如下:
@startuml
package "cloudapp-base-api" {
package "cache" {
interface ComponentLifeCycle {
void postStart();
void preStop();
}
interface Interceptor<R, P> {
R intercept(P param)
}
class CacheDiskInterceptor {
void expire(byte[] key, long millis);
void persist(byte[] key);
void delete(byte[]... keys);
void notifyChanged(Set<byte[]> keys);
void notifyChanged(byte[]... keys);
}
abstract class DecryptInterceptor
abstract class EncryptInterceptor
abstract class MonitoringInterceptor {
abstract boolean needMonitoring(byte[] value);
}
abstract class RefreshableRedisTemplate<K, V>
Interceptor <-- EncryptInterceptor : implements
Interceptor <-- DecryptInterceptor : implements
Interceptor <-- MonitoringInterceptor : implements
RedisTemplate <-- RefreshableRedisTemplate : extends
Interceptor <-- RefreshableRedisTemplate : uses
ComponentLifeCycle <-- RefreshableRedisTemplate : implements
}
}
@enduml-
定义缓存拦截器接口 Interceptor ,主要方法有:
- intercept(P param) 缓存键值对处理方法;
-
定义缓存磁盘拦截器接口 CacheDiskInterceptor ,主要方法由:
-
expire(byte[] key, long millis) 设置过期时间;
-
persist(byte[] key) 删除指定 key 的过期时间;
-
delete(byte[]... keys) 删除 key;
-
notifyChanged(Set<byte[]> keys) 值变更时通知;
-
notifyChanged(byte[]... keys) 值变更时通知;
-
-
定义加密拦截器抽象类 DecryptInterceptor ,实现接口 Interceptor ;
-
定义解密拦截器抽象类 EncryptInterceptor ,实现接口 Interceptor ;
-
定义缓存监控拦截器抽象类 MonitoringInterceptor ,实现接口 Interceptor ,只要方法有:
- needMonitoring(byte[] value) 是否开启监控;
-
定义可刷新 Redis 操作模板抽象类 RefreshableRedisTemplate ,实现接口 ComponentLifeCycle ,继承类 RedisTemplate ;
分布式缓存实现的配置如下:
- 定义分布式缓存实现的配置参数类 CloudAppRedisProperties ,使用注解 @ConfigurationProperties(CloudAppRedisProperties.PREFIX) ,其中 CloudAppRedisProperties.PREFIX = "com.alibaba.cloudapp.redis" ,继承类 RefreshableProperties ,配置参数类字段如下:
| 字段名 | 数据类型 | 默认值 | 备注 |
|---|---|---|---|
| enabled | boolean | - | 启用分布式缓存 |
| hashMonitoring | boolean | false | 是否监控哈希类型 |
| valueMonitoring | boolean | false | 是否监控 value |
| keyEncrypt | boolean | true | 是否加密 key |
| usedHash | boolean | false | 是否加密和解密哈希类型的 key 和 value |
| base | org.springframework.boot.autoconfigure.data.redis.RedisProperties | - | - |
| org.springframework.boot.autoconfigure.data.redis.RedisProperties 类型的 base 字段,其主要字段如下: | - | - | - |
| host | String | - | redis 访问地址 |
| username | String | - | redis 访问账号 |
| password | String | - | redis 访问密码 |
| port | int | - | redis 访问端口 |
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudapp</groupId>
<artifactId>cloudapp-framework-dependencies</artifactId>
<version>${cloudapp.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloudapp</groupId>
<artifactId>spring-boot-starter-cloudapp</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloudapp</groupId>
<artifactId>cloudapp-spring-redis</artifactId>
</dependency>
</dependencies>在环境变量中配置 redis 服务访问地址为例
spring:
application:
name: redis-demo
io:
cloudapp:
redis:
enabled: true
base:
host: ${REDIS_HOST}
port: 6379@Service
public class RedisDemoService implements InitializingBean {
@Autowired
@Qualifier("refreshableRedisTemplate")
private RefreshableRedisTemplateImpl redisTemplate;
@Override
public void afterPropertiesSet() {
redisTemplate.opsForValue().set("testValue", "test");
System.out.println(redisTemplate.opsForValue().get("testValue"));
redisTemplate.opsForList().rightPushAll("testList", "test", "test2", "test3");
System.out.println(redisTemplate.opsForList().range("testList", 0, -1));
redisTemplate.opsForGeo().add("testGeo", new Point(1.1, 2.2), "test");
System.out.println(redisTemplate.opsForGeo().radius("testGeo", "test", 10));
redisTemplate.opsForHash().put("testHash", "test", "test2");
System.out.println(redisTemplate.opsForHash().get("testHash", "test"));
redisTemplate.opsForSet().add("testSet", "test", "test2");
System.out.println(redisTemplate.opsForSet().members("testSet"));
redisTemplate.opsForHyperLogLog().add("testLog", Collections.singletonMap("test", "test2"));
System.out.println(redisTemplate.opsForHyperLogLog().size("testLog"));
RecordId id = redisTemplate.opsForStream().add("testStream", Collections.singletonMap("test", "test2"));
redisTemplate.opsForValue().getAndDelete("testValue");
redisTemplate.opsForList().leftPop("testList", 3);
redisTemplate.opsForGeo().remove("testGeo", "test");
redisTemplate.opsForHash().delete("testHash", "test");
redisTemplate.opsForSet().remove("testSet", "test", "test2");
redisTemplate.opsForHyperLogLog().delete("testLog");
redisTemplate.opsForStream().delete("testStream", id);
}
}