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

相关推荐
小码哥_常8 小时前
MyBatis-Plus:让数据库操作飞起来的神器
后端
2301_811274319 小时前
基于SpringBoot的智能家居管理系统
spring boot·后端·智能家居
AI人工智能+电脑小能手9 小时前
【大白话说Java面试题】【Java基础篇】第15题:JDK1.7中HashMap扩容为什么会发生死循环?如何解决
java·开发语言·数据结构·后端·面试·哈希算法
舒一笑9 小时前
我把设备指纹生成逻辑拆开了:它到底凭什么区分不同设备?
后端·程序员·掘金技术征文
Nicander10 小时前
多数据源下@transcation事务踩坑
java·后端
郑州光合科技余经理10 小时前
同城O2O海外版二次开发实战:从支付网关到配送算法
开发语言·前端·后端·算法·架构·uni-app·php
sjsjsbbsbsn11 小时前
大模型核心知识总结
java·人工智能·后端
Moment11 小时前
2026 年,AI 全栈时代到了,前端简历别再只写前端技术了 🫠🫠🫠
前端·后端·面试
白晨并不是很能熬夜12 小时前
【PRC】第 2 篇:Netty 通信层 — NIO 模型 + 自定义协议 + 心跳
java·开发语言·后端·面试·rpc·php·nio
zshs00012 小时前
#从偶发无字幕到补偿探测链路:一次 B 站字幕导入问题的完整收敛过程
java·后端·重构