redis学习之:lettuce链接池+RedisTemplate

本文介绍如何在Spring Boot项目中使用Lettuce和RedisTemplate进行Redis操作,并对比了Lettuce与Jedis的不同之处。同时提供了详细的配置示例及常见问题解决方案。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

背景介绍

由于很多人推荐使用RedisTemplate来操作redis,而且springboot至2.2版本开始就已经使用lettuce了,所以换成了lettuce+RedisTemplate


lettuce与jedis链接池对比

Lettuce 和 Jedis 的定位都是Redis的client,所以他们当然可以直接连接redis server。

spring boot框架中已经集成了redis,在1.x.x的版本时默认使用的jedis客户端,现在是2.x.x版本默认使用的lettuce客户端。

Jedis
Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接

Lettuce
Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
lettuce主要利用netty实现与redis的同步和异步通信。

注意:jedis连接池每一个jedis实例就是一个jedis链接,所以jedis可以动态的切换redis,db库.new一个jedis实例可以设置一个db库. 但是RedisTemplate不行需要在启动的时候创建一个链接,而且一旦切换就所有的链接都切换了.所以需要启动的时候需要几个db就配置几个template实例.这样不需要动态的切换,需要那个,注入那个就可以了

一、pom引用

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-pool2</artifactId>
</dependency>

二、yml配置

spring:
  application:
    name: ${parm.service.name}
  #redis单机版
  redis:
    host: ${parm.redis.host-name}
    port: ${parm.redis.port}
    # 密码 没有则可以不填
    password: ${parm.redis.password}
    database: ${parm.redis.db1}
    timeout: 2000
    # 如果使用的jedis 则将lettuce改成jedis即可
    lettuce:
      pool:
        max-total: 12
        # 最大活跃链接数 默认8(使用负值表示没有限制)
        max-active: 12
        # 最大空闲连接数 默认8
        max-idle: 12
        # 最小空闲连接数 默认0
        min-idle: 0
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1

config配置

@Configuration
public class LettuceRedisConfig {

    @Autowired
    private Environment environment;

    /**
     * 获取连接池信息
     *
     * @return
     * @Scope(value = "prototype")代表原型模式
     * @Scope(value = "singleton")代表单例模式
     * Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
     * 单例( Singleton):在整个应用中,只创建bean的一个实例。
     * 原型( Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
     * 会话( Session):在Web应用中,为每个会话创建一个bean实例。
     * 请求( Rquest):在Web应用中,为每个请求创建一个bean实例。
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
    @Scope(value = "prototype")
    public GenericObjectPoolConfig redisPool() {
        return new GenericObjectPoolConfig();
    }

    /**
     * 获取redis连接池信息
     *
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "parm.redis")
    public RedisStandaloneConfiguration redisConfigA() {
        return new RedisStandaloneConfiguration();
    }

    @Primary
    @Bean("redisTemplate1")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = getRedisTemplate(redisConnectionFactory);
        return redisTemplate;
    }

    @Bean("redisTemplate2")
    public RedisTemplate<String, Object> redisTemplate2(GenericObjectPoolConfig redisPool, RedisStandaloneConfiguration redisConfigA,
                                                        @Value("${parm.redis.db2}") int database2) {
        redisConfigA.setDatabase(database2);
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
        builder.poolConfig(redisPool);
        LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(redisConfigA, builder.build());
        connectionFactory.afterPropertiesSet();
        return getRedisTemplate(connectionFactory);
    }


    public RedisTemplate getRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        //设置序列化接口
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);
        SimpleFilterProvider filterProvider = new SimpleFilterProvider();
        filterProvider.setDefaultFilter(SimpleBeanPropertyFilter.serializeAll());
        mapper.setFilterProvider(filterProvider);
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(mapper);
        // key序列化
        redisTemplate.setKeySerializer(stringSerializer);
        // value序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash key序列化
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value序列化
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

引用

   @Autowired
    @Qualifier("redisTemplate1")
    private RedisTemplate<String, Object> redisTemplate;

#redisTemplate里有各种redis类型的操作通过 opsFor**()方法来操作具体需要
redisTemplate.opsForHash().put("OTT_" + code + "_" + hashKey, key, value);

获取所有的key

keys方法比较耗费性能,不推荐使用
 public static List<String> getkeys(RedisTemplate redisTemplate) {
        List<String> result = new ArrayList<>();

        try {
            String patternKey = "*";
            ScanOptions options = ScanOptions.scanOptions()
                    //这里指定每次扫描key的数量(很多博客瞎说要指定Integer.MAX_VALUE,这样的话跟 keys有什么区别?)
                    .count(20)
                    .match(patternKey).build();
            RedisSerializer<String> redisSerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();
            Cursor cursor = (Cursor) redisTemplate.executeWithStickyConnection(redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));
            while (cursor.hasNext()) {
                result.add(cursor.next().toString());
            }
            //切记这里一定要关闭,否则会耗尽连接数。报Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisException: Could not get a
            cursor.close();
        } catch (IOException e) {
            return null;
        }
        return result;
    }

问题解决

1、问题一、链接超时

io.lettuce.core.RedisCommandTimeoutException: Command timed out

原因具体没有调查清除,有的说是springboot中配置的timeout时间过短,导致的 但是我设置了5s过段时间不连接还是超时,后来修改redis中的配置文件,把tcp-keepalive设置成0-20之间。timeout设置为0 然后重启了redis。然后到现在没有发现有问题 。先观察一段时间。

2、问题二、无法连接

io.lettuce.core.RedisConnectionException: Unable to connect to 192.168.43.129:6379

通常是参数比如ip、端口、密码等不正确

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值