Java教程

spring data 连接池优化

本文主要是介绍spring data 连接池优化,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

使用jedis2.9.1的连接池的小伙伴可能存在一个问题,在程序高并发运行一段时间后会出现Could not get a resource from the pool的报错信息,并且在停止接口调用后查看到的redistemplate的连接工厂里面的空闲连接idelPool始终为0,,这部分连接不会再释放,初步怀疑是jedis连接池泄露问题,经过资料的查阅发现jedis2.9.2以下版本存在连接池泄露的问题,在官方可以看到在2.9.2的bug修复说明(经过我的全方位的测试发现这个bug是在2.9.1改出来的,2.9.0的版本也不存在这个问题的,真的坑啊)

 

 

如果版本不能升级可以采用开启jedis连接池巡检功能:jedis连接池巡检定时释放不可用资源

@Configuration

@EnableCaching

public class RedisConfig extends CachingConfigurerSupport {

@Value("${spring.redis.host}")
private String host;

@Value("${spring.redis.port}")

private Integer port;

@Value("${spring.redis.password}")

private String password;

@Value("${spring.redis.database}")

private Integer dataBase;

@Value("${spring.redis.jedis.pool.max-wait}")

private Integer MAX_WAIT_MILLIS;

@Value("${spring.redis.jedis.pool.max-active}")

private Integer MAX_TOTAL;

@Value("${spring.redis.jedis.pool.max-idle}")

private Integer MAX_IDLE;


// @Bean(name = "redisTemplate")

@Bean

RedisTemplate<String, Object> redisTemplate() {

RedisTemplate<String, Object> template = new RedisTemplate<>();

template.setConnectionFactory(connectionFactory(host, port, password, MAX_IDLE, MAX_TOTAL, MAX_WAIT_MILLIS, dataBase));

Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

ObjectMapper om = new ObjectMapper();

om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

jacksonSeial.setObjectMapper(om);

template.setKeySerializer(new StringRedisSerializer());

template.setValueSerializer(jacksonSeial);

template.setHashKeySerializer(new StringRedisSerializer());

template.setHashValueSerializer(jacksonSeial);

template.afterPropertiesSet();

return template;

}





//配置工厂

public RedisConnectionFactory connectionFactory(String host, int port, String password, int maxIdle,

int maxTotal, long maxWaitMillis, int index) {

JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();

jedisConnectionFactory.setHostName(host);

jedisConnectionFactory.setPort(port);


if (!StringUtils.isEmpty(password)) {

jedisConnectionFactory.setPassword(password);

}


if (index != 0) {

jedisConnectionFactory.setDatabase(index);

}


jedisConnectionFactory.setPoolConfig(poolConfig(maxIdle, maxTotal, maxWaitMillis));

jedisConnectionFactory.afterPropertiesSet();

return jedisConnectionFactory;

}


//连接池配置

public JedisPoolConfig poolConfig(int maxIdle, int maxTotal, long maxWaitMillis) {

JedisPoolConfig poolConfig = new JedisPoolConfig();

poolConfig.setMaxIdle(maxIdle);

poolConfig.setMaxTotal(maxTotal);

poolConfig.setMaxWaitMillis(maxWaitMillis);

// poolConfig.setTestOnBorrow(true);

// poolConfig.setTestOnReturn(true);

// //Idle时进行连接扫描

// poolConfig.setTestWhileIdle(true);

// //表示idle object evitor两次扫描之间要sleep的毫秒数

// poolConfig.setTimeBetweenEvictionRunsMillis(30000);

// //表示idle object evitor每次扫描的最多的对象数

// poolConfig.setNumTestsPerEvictionRun(10);

// //表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义

// poolConfig.setMinEvictableIdleTimeMillis(60000);

return poolConfig;

}

}

 

不过jedis的团队规模也比较小,spring框架默认以及推荐的又是lettuce,lettuce有个很大的优点就是连接在线程之间共享,哪怕不用连接池性能都非常优秀,lettuce的社区比较活跃,所以还可以采用lettuce的方式解决连接池泄露问题

lettuce原理:Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例,lettuce连接池配置如下

spring:
  redis:
    host: 192.168.1.1        # 服务器地址
    port: 6379                # 服务端口号
    database: 0               # 数据库索引
    password: 123          # 服务器连接密码默认为空
    lettuce:
      pool:
        max-active: 1000  # 连接池最大连接数
        max-wait: 3000  # 连接池最大阻塞等待时间
        max-idle: 500    # 连接池中最大的空闲连接
        min-idle: 100     # 连接池中最小的空闲连接
        time-between-eviction-runs: 60000  #此项配置必须,lettuce最小空闲连接会根据是否有配置这个值生效,默认-1
    timeout: 200       # 连接超时时间

不过lettuce存在集群检测以及偶现超时的问题

lettuce不会自动扫描集群节点是否正常,当cluster模式存在redis单点故障后,一直会有调用不同报错的情况,要解决这个问题有两种方式:

1、使用springboot2.3.x以后的版本并同时加上拓扑刷新的配置

spring:
  redis:
    xxx...
    lettuce:
      cluster:
        refresh:
          adaptive: true
          #10秒自动刷新一次
          period: 10

 

此方法会每10秒检测一次节点是否可用,不过当节点真出问题的时候还是存在10秒的空档期,这期间会不断的请求失败,显然不符合预期,如果通过调短扫描间隔又会增加系统负担,也不会根本解决空档期的问题,所以还需要加上cluster的重定向次数的配置(max-redirects),当节点不可连接的时候剔除节点并连接到可用的节点,拓扑刷新也同时运行,当节点恢复的时候,节点重新加入客户端的连接中

spring:
  redis:
    cluster:
      nodes: 192.168.1.1:8001,192.168.1.1:8002,192.168.1.1:8003
      max-redirects: 2
    lettuce:
      cluster:
        refresh:
          adaptive: true
          #10秒自动刷新一次
          period: 10

 

当然拓扑刷新需要springboot升级,对现网的影响比较大,为了保障系统的稳定性可能还是不会大动干戈,所以此时采用jedis连接池的方案可能更好一点,jedis会自动拓扑刷新不用任何配置的,不过也需要加上cluster的重定向次数的配置(max-redirects)

spring:
  redis:
    cluster:
      nodes: 192.168.1.1:8001,192.168.1.1:8002,192.168.1.1:8003
      max-redirects: 2
    jedis:
      pool:
        max-active: 100  # 连接池最大连接数
        max-wait: 3000  # 连接池最大阻塞等待时间
        max-idle: 50    # 连接池中最大的空闲连接
        min-idle: 10     # 连接池中最小的空闲连接
        #time-between-eviction-runs: 60000
    timeout: 500        # 连接超时时间

 

 

 

这篇关于spring data 连接池优化的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!