框架建设实战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的实现。

相关推荐
秋难降1 分钟前
SQL 索引突然 “罢工”?快来看看为什么
数据库·后端·sql
Access开发易登软件1 小时前
Access开发导出PDF的N种姿势,你get了吗?
后端·低代码·pdf·excel·vba·access·access开发
中国胖子风清扬2 小时前
Rust 序列化技术全解析:从基础到实战
开发语言·c++·spring boot·vscode·后端·中间件·rust
bobz9652 小时前
分析 docker.service 和 docker.socket 这两个服务各自的作用
后端
野犬寒鸦2 小时前
力扣hot100:旋转图像(48)(详细图解以及核心思路剖析)
java·数据结构·后端·算法·leetcode
phiilo3 小时前
golang 设置进程退出时kill所有子进程
后端
花花无缺3 小时前
python自动化-pytest-用例发现规则和要求
后端·python
程序员小假3 小时前
我们来说一说 Cglib 与 JDK 动态代理
后端
摆烂工程师5 小时前
教你如何认证 Gemini 教育优惠的二次验证,薅个 1年的 Gemini Pro 会员
后端·程序员·gemini
绝无仅有5 小时前
未来教育行业的 Go 服务开发解决方案与实践
后端·面试·github