框架建设实战6——缓存组件

说起分布式缓存,如今redis大行其道。不过,我们在创建缓存组件时,需要着重考虑如下几点:

1.客户端选型

本组件基于springboot2的默认实现,即lettuce客户端。不同客户端区别如下:

名称 描述 优缺点分析
jedis 1.springboot1.5.*默认 2.老牌客户端,使用稳定,但基于阻塞IO,其客户端实例本身非线程安全,要借助连接池建立物理连接 **优点:**支持全面的 Redis 操作特性(可以理解为API比较全面)。缺点:1.使用阻塞的 I/O,且其方法调用都是同步的,程序流需要等到 sockets 处理完 I/O 才能执行,不支持异步;2.Jedis 客户端实例不是线程安全的,所以需要通过连接池来使用 Jedis
Lettuce Springboot2.*默认,官方推荐 Lettuce底层基于netty,支持异步API。1个连接实例支持多线程复用。 **优点:**1.支持同步异步通信模式;2.Lettuce 的 API 是线程安全的,如果不是执行阻塞和事务操作,如BLPOP和MULTI/EXEC,多个线程就可以共享一个连接。**缺点:**学习成本相对jedis来说较高
Redisson Redisson是一个在Redis的基础上实现的 Java驻内存数据网格。基于netty框架通信机制,宗旨为促进使用者对Redis的关注分离 **优点:**1.提供一些高级用法,可提升开发效率,让开发者有更多的时间来关注业务逻辑2.提供很多分布式相关操作服务。例如分布式锁、分布式集合。可通过Redis支持延迟队列等。**缺点:**Redisson 对字符串的操作支持比较差。

2.序列化方式

同样,我们默认采用jackson的序列化方式进行缓存value的序列化和反序列化。

当然,根据大家的习惯,也许有些公司使用fastJson作为首选。或者二者兼而有之。

那如果既要满足jackson的方式,又要满足fastJson的方式应该怎么办呢?

敲重点:使用配置参数

比如我们可以使用如下配置,来控制业务接入使用何种序列化方式。

makefile 复制代码
frame.cache.serializer:jackson

指定了对应的序列化方式,可以看到配置类核心代码如下:

less 复制代码
@Bean
    @ConditionalOnMissingBean(
            name = {"redisTemplate"}
    )
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key的序列化直接使用StringRedisSerializer
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        //根据配置属性判断value使用何种序列化
        if(JASKSON.equalsIgnoreCase(serializerType)) {
            template.setValueSerializer(jacksonSerializer());
            template.setHashValueSerializer(jacksonSerializer());
        }
        if(FASTJSON.equalsIgnoreCase(serializerType)) {
            template.setValueSerializer(fastJsonSerializer());
            template.setHashValueSerializer(fastJsonSerializer());
        }
        template.afterPropertiesSet();
        log.info("redisClient2 configuration info:{}",template.getConnectionFactory().toString());
        return template;
    }

注意,上述的两个if,就是针对不同序列化方式做的兼容处理。

以jackson序列化为例,其配置bean如下:

typescript 复制代码
/**
     * jaskson方式
     * @return
     */
    private Jackson2JsonRedisSerializer<Object> jacksonSerializer(){
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
                new Jackson2JsonRedisSerializer<>(Object.class);

        // ObjectMapper 将Json反序列化成Java对象
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer;
    }

3.兼容性

抛个问题:如果有些业务不需要接入cache,有些需要接入,那我们有哪些方式可以处理呢?

a.pom方式

控制是否加入pom依赖,或者排除依赖

b.Conditional语义

读者可以使用springboot关于Conditional的语法控制相关service bean的创建

比如:@ConditionalOnMissingBean、@ConditionalOnBean、@@ConditionalOn****

c.注解开关

比如使用EnableCache注解(其他组件类似,通过Enable的语义控制业务是否激活某个功能组)

4.防腐层设计

我们需要考虑接入redis服务器的方式,就要对公司目前的redis部署架构进行调研。比如主从模式、集群模式,还是哨兵模式?

一般而言,公司的运维服务器架构不会轻易变化的。特别是如果中间件是基于云端的服务,那么架构模式一般是固定下来的。

如果采用的是主备模式,那么我们可以直接基于springboot的redis的starter就可以进行服务端连接。

xml 复制代码
<dependency> 
      <groupId>com.fasterxml.jackson.core</groupId>  
      <artifactId>jackson-annotations</artifactId> 
    </dependency>  
    <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId>  
      <artifactId>jackson-databind</artifactId> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework.boot</groupId>  
      <artifactId>spring-boot-starter-data-redis</artifactId> 
    </dependency> 

并配置客户端连接入参即可:

ini 复制代码
spring.redis.host=****
spring.redis.port=***
spring.redis.password=***

但,如果采取的是其他模式,就需要我们自行创建客户端的连接bean了。


思考题:

如果我们选用了redis作为分布式锁的实现,那么此功能是要跟缓存组件放在一起呢?还是独立出来一个分布式锁组件更合适?虽然两者都是基于redis的实现。

相关推荐
rannn_11142 分钟前
【Javaweb学习|黑马笔记|Day1】初识,入门网页,HTML-CSS|常见的标签和样式|标题排版和样式、正文排版和样式
css·后端·学习·html·javaweb
柏油1 小时前
Spring @Cacheable 解读
redis·后端·spring
柏油2 小时前
Spring @TransactionalEventListener 解读
spring boot·后端·spring
两码事3 小时前
告别繁琐的飞书表格API调用,让飞书表格操作像操作Java对象一样简单!
java·后端
shark_chili4 小时前
面试官再问synchronized底层原理,这样回答让他眼前一亮!
后端
灵魂猎手4 小时前
2. MyBatis 参数处理机制:从 execute 方法到参数流转全解析
java·后端·源码
易元4 小时前
模式组合应用-桥接模式(一)
后端·设计模式
柑木4 小时前
隐私计算-SecretFlow/SCQL-SCQL的两种部署模式
后端·安全·数据分析
灵魂猎手4 小时前
1. Mybatis Mapper动态代理创建&实现
java·后端·源码
泉城老铁4 小时前
在秒杀场景中,如何通过动态调整线程池参数来应对流量突增
后端·架构