转载

关于SpringBoot中Redis线程池的有关探讨

最近在写一个小项目,用redis过期来实现验证码的时间限制。因为SpringBoot默认采用 lettuce作为客户端,引入了 commons-pool2 依赖之后做了如下配置:

spring:
  redis:
    host: 192.168.56.1
    lettuce:
      pool:
        min-idle: 2
        max-active: 8     #默认
        max-idle: 8       #默认
复制代码

本来以为这样做就行了,然后写了如下代码测了下

@Test
    public void test() throws InterruptedException {
        int i ;
        CountDownLatch c = new CountDownLatch(5000);
        for (i = 1; i <= 5000; i++) {
            new Thread(() -> {
                System.out.println(redisTemplate.execute(RedisConnection::ping));
                c.countDown();
            }).start();
        }
        c.await();
    }
复制代码

测试期间,实际客户端最大接入:

127.0.0.1:6379> info clients
# Clients
connected_clients:2
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
复制代码

???,我设置的配置去哪里了??,于是开始了漫长的探索

尝试

首先看下默认下是怎样的。去除了pool的设置,再跑一下。

127.0.0.1:6379> info clients
# Clients
connected_clients:2
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
复制代码

很好,同样的结果。说明刚刚的配置根本没有效果。没准是lettuce的原因?于是我修改了pom,用jedis测试了一下。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.1.7.RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>lettuce-core</artifactId>
                    <groupId>io.lettuce</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <!--    注意版本和SpringData兼容   -->
            <version>2.9.1</version>
        </dependency>
复制代码
spring:
  redis:
    host: 192.168.56.1
    jedis:
      pool:
        min-idle: 2
        max-active: 8
        max-idle: 8
复制代码

看下结果:

# Clients
connected_clients:9
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
127.0.0.1:6379> info clients
复制代码

最大换成15再试试:

# Clients
connected_clients:16
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
复制代码

很好,jedis没有问题,那说明是lettuce的配置上除了问题。

探索

我们看下lettuce的 LettuceConnectionFactory 中的 getConnection 是怎样实现的。

public RedisConnection getConnection() {
        if (this.isClusterAware()) {
            return this.getClusterConnection();
        } else {
            LettuceConnection connection;
            if (this.pool != null) {
                connection = new LettuceConnection(this.getSharedConnection(), this.getTimeout(), (AbstractRedisClient)null, this.pool, this.getDatabase());
            } else {
                connection = new LettuceConnection(this.getSharedConnection(), this.connectionProvider, this.getTimeout(), this.getDatabase());
            }

            connection.setConvertPipelineAndTxResults(this.convertPipelineAndTxResults);
            return connection;
        }
    }
复制代码

首先,是关于集群判断,因为我没设置任何集群相关,所以直接来到 elseLettuceConnection 的构造函数中的 this.getSharedConnection() 引起了我的注意,字面意思 获得共享连接 。好的,就决定是你了!

protected StatefulRedisConnection<byte[], byte[]> getSharedConnection() {
        return this.shareNativeConnection ? (StatefulRedisConnection)this.getOrCreateSharedConnection().getConnection() : null;
    }
复制代码

首先要判断这个 shareNativeConnection 默认值。从上面的属性能看到:

private boolean shareNativeConnection = true;
复制代码

OK,默认为 true 。先到这猜测一下,因为默认共享连接实例,所以此时使用的都为同一个实例,同时最多只有一个与redis连接。那我尝试认为改变一下设置:

@Configuration
public class Config {
    @Autowired
    public void setLettuceConnectionFactory(LettuceConnectionFactory lettuceConnectionFactory){
        lettuceConnectionFactory.setShareNativeConnection(false);
    }
}
复制代码

还是按下面的配置及测试:

spring:
  redis:
    host: 192.168.56.1
    lettuce:
      pool:
        min-idle: 2
        max-active: 8
        max-idle: 8
复制代码
@Test
    public void test() throws InterruptedException {
        int i ;
        CountDownLatch c = new CountDownLatch(5000);
        for (i = 1; i <= 5000; i++) {
            new Thread(() -> {
                System.out.println(redisTemplate.execute(RedisConnection::ping));
                c.countDown();
            }).start();
        }
        c.await();
    }
复制代码

之后结果:

127.0.0.1:6379> info clients
# Clients
connected_clients:9
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
复制代码

OK,验证成功,再改下10试试,结果也是相符。

127.0.0.1:6379> info clients
# Clients
connected_clients:11
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
127.0.0.1:6379> 
复制代码

注意

如果将刚才的 shareNativeConnection 的修改之后,而 application.ymlpool 的属性不进行设置的话,那结果相当可怕,我们看下测试结果:

127.0.0.1:6379> info clients
# Clients
connected_clients:573
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
127.0.0.1:6379> info clients
# Clients
connected_clients:683
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
127.0.0.1:6379> info clients
# Clients
connected_clients:890
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
127.0.0.1:6379> info clients
# Clients
connected_clients:1034
client_recent_max_input_buffer:4
client_recent_max_output_buffer:0
blocked_clients:0
复制代码

这连接数是直接放飞自我了。。。。。不过设置后则可得到限制。

总结

通过探索,发现Lettuce的 poolshareNativeConnection 息息相关,通过debug我发现了一些可能的原因不过看着太长了就懒得写了...如有不对的地方,望能指证出来。

原文  https://juejin.im/post/5d6fa3d2f265da03d7283a6c
正文到此结束
Loading...