前言

1、查了许多资料,整合成一个通用的RedisTemplate。
2、推荐Redis可视化工具 Another Redis DeskTop Manager,免费好用,人机交互性能好。

配置

当前版本
<parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.4.0</version>
       <relativePath/> <!-- lookup parent from repository -->
   </parent>
引入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.4.0</version>
</dependency>
        
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.62</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.8.0</version>
</dependency>

<dependency>
	<groupId>org.springframework.session</groupId>
	<artifactId>spring-session-data-redis</artifactId>
</dependency>

引入依赖说明

  1. 引入commons-pools2的目的,是因为spring-boot-data-redis中使用的是lettuce,取代了旧的Jedis。spring-boot-data-redis既然取代了Jedis,那就侧面说明这个luttuce性能更好。不过为了保证稳定性、可靠性,需要引入commons-pools2(含有Jedis)。
  2. 使用fastjson,常用的JSON转换器。
  3. 引入spring-session-data-redis,是因为有个小Bug,在查看了源码之后,还未发现能替换这个依赖库的原生的redisTemplate,后面会有演示。
编辑application.yml
spring:
	redis:
	    host: 127.0.0.1
	    port: 6379
	    timeout: 3000
	    password: 123456
	    lettuce:
	      pool:
	        max-wait: 16
	        max-active: 16
	        max-idle: 16
	        min-idle: 1
	      shutdown-timeout: 10000ms  # 关闭超时时间
	    database: 0
配置Redis配置类

配置类的注解

@Configuration
@EnableCaching
public class RedisConfig 

配置类从application.yml中引入配置元数据

public class RedisConfig {
	
	// 倘若 spring.redis.host 不存在,则会默认为127.0.0.1.
    @Value("${spring.redis.host:#{'127.0.0.1'}}")
    private String hostName;

    @Value("${spring.redis.port:#{6379}}")
    private int port;

    @Value("${spring.redis.password:#{123456}}")
    private String password;

    @Value("${spring.redis.timeout:#{3000}}")
    private int timeout;

    @Value("${spring.redis.lettuce.pool.max-idle:#{16}}")
    private int maxIdle;

    @Value("${spring.redis.lettuce.pool.min-idle:#{1}}")
    private int minIdle;

    @Value("${spring.redis.lettuce.pool.max-wait:#{16}}")
    private long maxWaitMillis;

    @Value("${spring.redis.lettuce.pool.max-active:#{16}}")
    private int maxActive;

    @Value("${spring.redis.database:#{0}}")
    private int databaseId;
	
	// 其它配置
	.....
}

配置LettuceConnectionFactory的连接池

@Bean
public LettuceConnectionFactory lettuceConnectionFactory() {

	RedisConfiguration redisConfiguration = new RedisStandaloneConfiguration(
	        hostName, port
	);
	
	// 设置选用的数据库号码
	((RedisStandaloneConfiguration) redisConfiguration).setDatabase(databaseId);
	
	// 设置 redis 数据库密码
	((RedisStandaloneConfiguration) redisConfiguration).setPassword(password);
	
	// 连接池配置
	GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
	poolConfig.setMaxIdle(maxIdle);
	poolConfig.setMinIdle(minIdle);
	poolConfig.setMaxTotal(maxActive);
	poolConfig.setMaxWaitMillis(maxWaitMillis);
	
	LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder
	        = LettucePoolingClientConfiguration.builder()
	        .commandTimeout(Duration.ofMillis(timeout));
	
	LettucePoolingClientConfiguration lettucePoolingClientConfiguration = builder.build();
	
	builder.poolConfig(poolConfig);
	
	// 根据配置和客户端配置创建连接
	LettuceConnectionFactory factory = new LettuceConnectionFactory(redisConfiguration, lettucePoolingClientConfiguration);
	return factory;
    }

说明:

  1. redis其实单线程性能更好,也不需要配置线程数之类的。当然,最近的版本都有支持多线程,就适当的配上线程数。
  2. 此处可以设置不同的databaseId,来达到应对不同database的操作目的。

配置KeyGenerator

@Bean
public KeyGenerator wiselyKeyGenerator() {
    return (target, method, params) -> {
        StringBuilder builder = new StringBuilder();
        builder.append(target.getClass().getName());
        builder.append(method.getName());
        for (Object obj : params) {
            builder.append(obj.toString());
        }
        return builder.toString();
    };
}

这是一个通用的keyGenerator,可以解决分布式的命名问题。

配置RedisTemplate<String, Object>

@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(
        LettuceConnectionFactory lettuceConnectionFactory
) {

    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(lettuceConnectionFactory);

    // 使用 FastJsonRedisSerializer 来序列化和反序列化redis 的 value的值
    FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<>(Object.class);
    ParserConfig.getGlobalInstance().addAccept("com.muzz");
    FastJsonConfig fastJsonConfig = new FastJsonConfig();
    fastJsonConfig.setCharset(StandardCharsets.UTF_8);
    serializer.setFastJsonConfig(fastJsonConfig);

    // key 的 String 序列化采用 StringRedisSerializer
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    redisTemplate.setKeySerializer(stringRedisSerializer);
    redisTemplate.setHashKeySerializer(stringRedisSerializer);

    // value 的值序列化采用 fastJsonRedisSerializer
    redisTemplate.setValueSerializer(serializer);
    redisTemplate.setHashValueSerializer(serializer);

    redisTemplate.afterPropertiesSet();

    System.out.println(redisTemplate.getDefaultSerializer());

    return redisTemplate;
}

有几个地方要注意

  1. 使用的lettuceConnectionFactory是由上面的lettuceConnectionFactory配置的。
  2. StringRedisSerializer更加的健壮,他是继承了RedisSerializer这个接口,并且指定的是String类型。
  3. 此处使用的是阿里巴巴的FastJsonRedisSerializer。在传入对象时,可以将对象转换成JSON格式,并以字符串的形式传入redis缓存中。在设计对象的时候,可以考虑使用 transient 来反序列化。
此时配置完成,下面配置选择使用
@Bean
//    @ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
        LettuceConnectionFactory lettuceConnectionFactory
) {

    StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
    stringRedisTemplate.setConnectionFactory(lettuceConnectionFactory);
    return stringRedisTemplate;
}

这是更严格的 redisTemplate,网络上大佬写的。

@Bean
public RedisTemplate<String, Serializable> limitRedisTemplate(
        RedisConnectionFactory redisConnectionFactory) {

    RedisTemplate<String, Serializable> template = new RedisTemplate<>();
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}

数据测试

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

在测试之前,先说明一下,使用上面的依赖,会有小Bug出现,貌似是使用JDK的序列器,等会演示。

@CrossOrigin
@PostMapping(value = "login", consumes = {"application/json"})
public String sysUserLogin(
        HttpServletRequest request,
        HttpServletResponse response,
        @RequestBody UserLoginReq req
        ) {
    // 此处调用了 spring-session-data-redis 里面自带的 redisTemplate 进行操作
    System.out.println(request.getSession().getId());
    System.out.println(redisTemplate.keys("*"));

	// 此处是注入了上面已经配置好的 redisTemplate,需要注意区分
    redisTemplate.opsForHash().put("这是一个测试key", "这是一个测试hashkey", "这个一个测试value");
    redisTemplate.opsForHash().put("这是另一个测试key", "这是另一个测试hashkey", req);

//        Result result = sysUserLoginService.login(req);
    return Result.newSuccessResult(req).toString();
}

request.getSession().getId()的结果:
在这里插入图片描述
spring-session-data-redis在获取了session( 调用getSession )后,会自动写入到redis里面。
在这里插入图片描述

测试问题

spring-session-data-redis写入的数据会出现十六进制的乱码问题
开头都是\xac\xed\x00\x05sr\x00\x0e,这是一个不应该出现乱码的问题,这会在redis中占据一定的存储空间,这是不能忍的。
在这里插入图片描述
而对于刚刚自定义的RedisTemplate<String, Object>来说不会出现这个问题。
value是字符串类型。
在这里插入图片描述
value是Object类型,Object被转换成JSON格式的字符串。
在这里插入图片描述
同样的Hash数据结构存储,结果却不大相同。
可以暂时推断,spring-session-data-redis是使用了默认的存储序列,这是需要注意的大坑。

翻看源码

在这里插入图片描述
其实可以看到,spring-session-data-redis是默认使用JdkSerializationRedisSerializer的。

后续解决办法

解决办法
https://blog.csdn.net/weixin_46828364/article/details/110821604

GitHub 加速计划 / sp / spring-boot
39
7
下载
spring-projects/spring-boot: 是一个用于简化Spring应用开发的框架。适合用于需要快速开发企业级Java应用的项目。特点是可以提供自动配置、独立运行和内置的Tomcat服务器,简化Spring应用的构建和部署。
最近提交(Master分支:2 个月前 )
2336cddb Previously, the necessary infrastructure to create a RestClient was only configured in a servlet-based application or in a reactive application if virtual threads are enabled. While this extra care was important in Spring Boot 4 as the aut-configuration is always available, this is no longer the case with Spring Boot 4. Indeed, an explicit module has to be added to the classpath now. This commit therefore relaxes the condition. If the module has been added, then the infrastructure is auto-configured. Closes gh-48308 12 小时前
46f22b23 Closes gh-48307 1 天前
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐