spring-boot-starter-data-redis初始化(使用lettuce)

前言

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.youkuaiyun.com/weixin_46828364/article/details/110821604

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值