【Redis学习系列】Springboot 整合Redis

/ Redis / 1 条评论 / 15980浏览

Springboot 整合Redis

在Springboot 2.x之后,jedis被替换成了lettuce。

jedis lettuce 对比

jedis:采用直连的方式,多线程操作是不安全的。如果要规避多线程不安全要使用jedis pool连接池。BIO模式

lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况。可以减少线程数量。NIO模式

Springboot 对Redis的自动配置

自动配置类:org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

  //Springboot 为我们自动化初始化了 redisTemplate 模板类;
  //如果我们使用并自定义了模板类,则该配置不生效。
	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
  //Key为String类型的模板类。
	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

其中 RedisProperties 指定了Redis在application.yml中的配置项。

配置项默认值注解
spring.redis.client-nameClient name to be set on connections with CLIENT SETNAME.
spring.redis.cluster.max-redirectsMaximum number of redirects to follow when executing commands across the cluster.
spring.redis.cluster.nodesComma-separated list of "host:port" pairs to bootstrap from. This represents an "initial" list of cluster nodes and is required to have at least one entry.
spring.redis.database0.0Database index used by the connection factory.
spring.redis.hostlocalhostRedis 服务器端IP地址
spring.redis.jedis.pool.max-active8.0在指定的时间,连接池可以分配的最大活跃连接数;设置负数代表无限制.
spring.redis.jedis.pool.max-idle8.0Maximum number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
spring.redis.jedis.pool.max-wait-1msMaximum amount of time a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
spring.redis.jedis.pool.min-idle0.0Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if both it and time between eviction runs are positive.
spring.redis.jedis.pool.time-between-eviction-runsTime between runs of the idle object evictor thread. When positive, the idle object evictor thread starts, otherwise no idle object eviction is performed.
spring.redis.lettuce.cluster.refresh.adaptivefalseWhether adaptive topology refreshing using all available refresh triggers should be used.
spring.redis.lettuce.cluster.refresh.periodCluster topology refresh period.
spring.redis.lettuce.pool.max-active8.0Maximum number of connections that can be allocated by the pool at a given time. Use a negative value for no limit.
spring.redis.lettuce.pool.max-idle8.0Maximum number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
spring.redis.lettuce.pool.max-wait-1msMaximum amount of time a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
spring.redis.lettuce.pool.min-idle0.0Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if both it and time between eviction runs are positive.
spring.redis.lettuce.pool.time-between-eviction-runsTime between runs of the idle object evictor thread. When positive, the idle object evictor thread starts, otherwise no idle object eviction is performed.
spring.redis.lettuce.shutdown-timeout100ms关闭延迟时间.
spring.redis.passwordredis 服务器的登录密码.
spring.redis.port6379.0Redis 服务器端端口号
spring.redis.sentinel.masterRedis 服务器器名称.
spring.redis.sentinel.nodesComma-separated list of "host:port" pairs.
spring.redis.sentinel.passwordPassword for authenticating with sentinel(s).
spring.redis.sslfalse时候开启SSL支持.
spring.redis.timeout链接过期时间。
spring.redis.url链接 URL. 包括 host, port,和 password. User被忽略,例如: redis://user:password@example.com:6379

实例测试

导入依赖

        <!-- 导入springboot 2.x 后默认的redis支持 -->
				<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

配置

spring:
  redis:
    host: 128.190.252.226
    port: 6379
    password: hello
    database: 0

注意确认以下事项:

测试

测试String值的写入和读取

@RunWith(SpringRunner.class)
@DataRedisTest
public class SpringbootRedisTest {

    @Autowired
    RedisTemplate redisTemplate;

    @Test
    public void testValue(){
        redisTemplate.opsForValue().set("name","学习Redis鸭!");
        System.out.println(redisTemplate.opsForValue().get("name"));
    }

    @Test
    public void testConnection(){
        //获取连接;很少用
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        connection.flushDb();
        connection.flushAll();
    }
}

通过命令行查看redis服务器端情况:

127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04name"
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> get "\xac\xed\x00\x05t\x00\x04name"
"\xac\xed\x00\x05t\x00\x11\xe5\xad\xa6\xe4\xb9\xa0Redis\xe9\xb8\xad\xef\xbc\x81"

Redis中的key Value被编码?

原因分析:

默认我们注入的是RedisTemplate;通过源码分析可以知道:实际上注入的是RedisTemplate<Object,Object>.

但是这样是明显不方便我们日常使用的!

	//RedisAutoConfiguration 
	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

image-20210209142923136

RedisTemplate<Object,Object> 中有一段代码:

        boolean defaultUsed = false;
        if (this.defaultSerializer == null) {
            this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
        }
			  # 其中默认的序列化都是采用的Jdk Serialization

        if (this.enableDefaultSerializer) {
            if (this.keySerializer == null) {
                this.keySerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.valueSerializer == null) {
                this.valueSerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.hashKeySerializer == null) {
                this.hashKeySerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.hashValueSerializer == null) {
                this.hashValueSerializer = this.defaultSerializer;
                defaultUsed = true;
            }
        }

如何解决?

需要我们为Springboot配置一个自定义的RedisTemplate<String,Object>

如果使用Springboot默认的RedisTemplate<Object,Object>那么object 必须实现serializer接口。否则会报错!如下图所示:

image-20210209150106972

对象实现序列化接口后:

image-20210209150729810

自定义RedisTemplate

@Configuration
public class RedisConfig {

    @Autowired
    ObjectMapper objectMapper;

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

  1. 跟故宫ggg

    回复