今天找了半天没找到线程的,网上都是多个Redis示例。最还还是自己想办法实现了
两个Redis集群作为数据源 + 自定义缓存注解
不会做的是否发现好tm难。都不知道怎么动手。做完之后,发现原理其实特别简单。很容易就能实现。分两部分原理和示例代码部分
原理解析:
- 配置两个数据源,生成两个redisTemplate (这个配置是参考我之前配置单个集群的时候,从人家的data-redis里面复制出来的,也可能是看了那篇博客。我不清楚了)
- 分别操作这两个tempLate.
- 我这里是利用AOP+注解的方式。拦截所有配置了这个注解的方法。然后先通过template请求Redis 有结果直接返回。没有就查库,然后将结果放到Redis。再返回
- 我这里利用两个库的用途是相同场景不同城市使用不同的Redis数据源。 有的场景是不同业务使用不同数据源。那样应该实现起来更简单
代码示例:
- 需要的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 注意启动类要关掉redis的自动配置
@SpringBootApplication(exclude = {RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class})
- 主配置类
@Configuration
@EnableCaching
public class RedisConfig2 {
@Autowired
RedisClusterNode redisClusterNode;
@Bean(name = "jtClusterRedisConnectionFactory")
public LettuceConnectionFactory getJtRedisConnectionFactory() {
return new LettuceConnectionFactory(new RedisClusterConfiguration(redisClusterNode.getJtNodes()));
}
@Bean(name = "szClusterRedisConnectionFactory")
public LettuceConnectionFactory getSzRedisConnectionFactory() {
return new LettuceConnectionFactory(new RedisClusterConfiguration(redisClusterNode.getSzNodes()));
}
@Bean(name = "redisTemplate")
@Qualifier("jtClusterRedisConnectionFactory")
public RedisTemplate redisTemplate(LettuceConnectionFactory jtClusterRedisConnectionFactory){
return getRedisTemplate(jtClusterRedisConnectionFactory);
}
@Bean(name = "szRedisTemplate")
@Qualifier("szClusterRedisConnectionFactory")
public RedisTemplate szRedisTemplate(LettuceConnectionFactory szClusterRedisConnectionFactory) {
return getRedisTemplate(szClusterRedisConnectionFactory);
}
private RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式,value的序列化方式采用jackson
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// hash的key也采用String的序列化方式 value的序列化方式采用jackson
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
- 节点配置(这个应该可以和上面写在一起的。日后有时间在优化。将记录下来)
@Configuration
@Getter
@Setter
public class RedisClusterNode {
@Value("${redis.cluster.jt-nodes}")
private List<String> jtNodes;
@Value("${redis.cluster.sz-nodes}")
private List<String> szNodes;
}
- Redis集群数据源的配置(这里做了处理,总不能将公司的地址放出来把)
redis:
cluster:
jt-nodes: 127.0.0.1:7002,127.0.0.1:7003
sz-nodes: 127.0.0.2:7002,127.0.0.2:7003
- 自定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRedisCache {
String key();
}
- AOP拦截使用了注解的方法
@Aspect
@Component
@Slf4j
public class RedisCacheAspect {
@Resource(name = "szRedisTemplate")
private RedisTemplate<String, Object> szRedisTemplate;
@Resource(name = "redisTemplate")
private RedisTemplate<String, Object> jtRedisTemplate;
@Value("${spring.profiles.active}")
private String active;
@Around("@annotation(com.centanet.bizcom.config.redis.MyRedisCache)")
public Object handleCache(ProceedingJoinPoint joinPoint) {
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
// 取出key
MyRedisCache myRedisCache = method.getAnnotation(MyRedisCache.class);
String key = myRedisCache.key();
Object[] args = joinPoint.getArgs();
// 获取当前城市
String city = CityHelper.getCityen();
// 填充完整的key
String fullKey = this.buildFullKey(key,args);
// 先从缓存里查询。如果查询到直接返回。
Object result = this.getObjectFromRedis(city,fullKey);
if (!ObjectUtil.isEmpty(result)) {
return result;
}
// 查询不到,走原来的方法。并且把结果填充到Redis服务器
try {
result = joinPoint.proceed(args);
} catch (Throwable throwable) {
throw new MyRedisCacheAnnotationException("自定义的Redis注解这里出问题了。问题详情"+throwable.getMessage());
}
if (!ObjectUtil.isEmpty(result)) {
this.setObjectToRedis(city,fullKey,result);
}
return result;
}
private Object getObjectFromRedis(String city, String key) {
if (city.equalsIgnoreCase("SZ")) {
return szRedisTemplate.opsForValue().get(key);
}
return jtRedisTemplate.opsForValue().get(key);
}
private void setObjectToRedis(String city, String key, Object target) {
if (city.equalsIgnoreCase("SZ")) {
szRedisTemplate.opsForValue().set(key,target,6,TimeUnit.HOURS);
}else {
jtRedisTemplate.opsForValue().set(key,target,6,TimeUnit.HOURS);
}
}
private String buildFullKey(String key, Object[] objects) {
// 加个active的目的是因为测试环节深圳和线上是一个集群。避免数据混乱,加个个active进行区分
StringBuilder fullKey = new StringBuilder(key).append(":").append(active);
if (objects == null || objects.length <= 0) {
return fullKey.toString();
}
for (Object o : objects) {
fullKey.append(":").append(o.toString());
}
return fullKey.toString();
}
}
- 使用时将注解加在方法上就好了。key看自己的需求
@MyRedisCache(key = "bizCom:AD:listSchoolRequest")
public List<SchoolResponse> listSchoolRequest(ListSchoolRequest request) {
// 这里是一堆业务逻辑
return null;
}
小结
• 发现自己水平还可以哈
• 做的是否很复杂。实际弄完发现代码其实并不多
水平有限。这个之前其实就实现了多Redis数据源。但是之前把分支删除了。还好找回了。
今天完成的部分就是自定义注解和清除缓存
这次记录下。如果下次有需求直接用