searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Java lettuce redis驱动,单链接和连接池使用和性能测试

2023-05-22 12:17:19
471
0

背景

lettuce默认提供了线程安全的链接,大部分场景下,使用单个链接即可满足需求,但是在大并发场景,还是需要连接池,lettuce默认提供了 异步连接池配置,本文对Redis 的单链接,和连接池场景进行测试,比对性能差异和适用场景。

测试条件

Redis cluser 3主3从 6节点,

本机Intel 8核处理器,lettuce版本

       <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
            <version>5.3.7.RELEASE</version>
        </dependency>

单链接(不用连接池)

做法可以参考我上篇文章《java 服务基于lettuce实现redis sentinel/cluster的高可用连接

直接上测试代码

   @Autowired
   private RedisCluster redis;
  @Autowired
    protected ThreadPoolTaskScheduler taskScheduler;

    private AtomicLong count = new AtomicLong(0);
    private Long lastCount =0L;

    @PostConstruct
    public void initService() {
        LOGGER.info("initService hello"); 
      
       for(int i=0;i<10;i++)
    {
       taskScheduler.schedule(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    try {
                        long start = System.currentTimeMillis();
                        String clusterRe = redis.getCMD().set("testCluster", RandomUtils.nextInt()+"-hello",
                        SetArgs.Builder.ex(10));
                      
                        start = System.currentTimeMillis();
                        String value = redis.get("testCluster");
                        count.incrementAndGet();
                    }catch (Exception e) {
                        LOGGER.error("redisSentinel test error:" + e.getMessage()); }
                    }
                 }
            },new Date(System.currentTimeMillis() + 10000));
        }
       //10秒后开始每秒统计
       taskScheduler.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                      long nowCount = count.get();
                      LOGGER.info("one cluster QPS " + (nowCount -lastCount )+"/s"; }
                     lastCount = nowCount;
            },new Date(System.currentTimeMillis() + 10000),1000);

    }

 

设置大于1个线程,逐步增加到60线程数,循环操作,qps不稳定,并且持续下降,本机8核cpu被打满

 

 可以看到,一开始有20万QPS,但是持续下降,一直掉到 1000以下,猜测是由于竞争激烈导致

设置60个线程情况下循环操作,每次操作停1ms,减少竞争,qps稳定在3.2w,本机8核cpu维持在40%左右

结论

不使用连接池的情况下异步操作,多线程竞争激烈情况下,qps会持续下降,模拟真实业务场景(停顿1ms )场景,可以维持3.2w QPS

使用连接池

lettuce的连接池也是异步的,构建方法如下

/**
     * 异步连接池
     */
    private BoundedAsyncPool<StatefulRedisClusterConnection<String, String>> asyncPool;

// cluster client 创建异步连接池 clusterClient 需要提前创建好
asyncPool = AsyncConnectionPoolSupport.createBoundedObjectPool(
                () -> clusterClient.connectAsync(StringCodec.UTF8).thenApply(connect->{
                    connect.setTimeout(Duration.ofSeconds(5));
                    connect.setAutoFlushCommands(false);
                    connect.setReadFrom(ReadFrom.MASTER_PREFERRED);
                    return connect;
        }), BoundedPoolConfig.builder().maxTotal(getPoolMaxTotal()).maxIdle(getPoolMaxIdle()).minIdle(getPoolMinIdle()).build());


 // 单节点,或者哨兵的client 创建异步连接池  asyncClient
        RedisClient asyncClient = RedisClient.create();
//TODO 这里需要初始化uri,需要区别单节点和哨兵
  RedisURI redisURI;
       //单节点带有密码时,会有无法正确识别协议问题,需要手工指定  https://github.com/lettuce-io/lettuce-core/issues/1543
        if (SINGLE.equals(type) && StringUtils.isNotBlank(password)) {
            asyncClient.setOptions(ClientOptions.builder().build());
        }
        asyncPool = AsyncConnectionPoolSupport.createBoundedObjectPool(
                () -> MasterReplica.connectAsync(asyncClient, StringCodec.UTF8, redisURI).thenApply(connect -> {
                    connect.setAutoFlushCommands(false);
                    connect.setReadFrom(ReadFrom.MASTER_PREFERRED);
                    return connect;
                }), BoundedPoolConfig.builder().maxTotal(getPoolMaxTotal()).maxIdle(getPoolMaxIdle()).minIdle(getPoolMinIdle()).build());

这里最大最小连接数靠参数注入

poolMaxTotal #总连接数

poolMinIdle #最小空闲连接数

poolMaxIdle #最大空闲的连接数

连接数测试

 这里设置最大链接数30,尝试获取50个链接且不释放

可以看到,获取到30个后,就无法获取

吞吐量测试

一开始实例化10个链接,用60线程循环操作的情况下,qps稳定在12w左右,本机8核cpu被打满

然后慢慢增加连接数,加到12个链接,执行一段时间后开始出现重连现象,QPS变得不稳定

结论

连接数建议设置成<12

总结

1、lettuce 连接池配置使用poolMaxTotal、poolMaxIdle配置,建议设置成一样,为了避免短连接的增加而引起资源上的消耗

2、poolMaxTotal建议设置成<12(默认设置8,自定义设置不建议超过12链接)

    在12个链接下,redis cluster 多线程吞吐可以达到12万QPS
3、高并发场景(单节点 对redis 请求超过 2.5wQPS )下建议使用连接池,单个连接适用于普通业务

0条评论
0 / 1000
张****钏
15文章数
1粉丝数
张****钏
15 文章 | 1 粉丝
原创

Java lettuce redis驱动,单链接和连接池使用和性能测试

2023-05-22 12:17:19
471
0

背景

lettuce默认提供了线程安全的链接,大部分场景下,使用单个链接即可满足需求,但是在大并发场景,还是需要连接池,lettuce默认提供了 异步连接池配置,本文对Redis 的单链接,和连接池场景进行测试,比对性能差异和适用场景。

测试条件

Redis cluser 3主3从 6节点,

本机Intel 8核处理器,lettuce版本

       <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
            <version>5.3.7.RELEASE</version>
        </dependency>

单链接(不用连接池)

做法可以参考我上篇文章《java 服务基于lettuce实现redis sentinel/cluster的高可用连接

直接上测试代码

   @Autowired
   private RedisCluster redis;
  @Autowired
    protected ThreadPoolTaskScheduler taskScheduler;

    private AtomicLong count = new AtomicLong(0);
    private Long lastCount =0L;

    @PostConstruct
    public void initService() {
        LOGGER.info("initService hello"); 
      
       for(int i=0;i<10;i++)
    {
       taskScheduler.schedule(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    try {
                        long start = System.currentTimeMillis();
                        String clusterRe = redis.getCMD().set("testCluster", RandomUtils.nextInt()+"-hello",
                        SetArgs.Builder.ex(10));
                      
                        start = System.currentTimeMillis();
                        String value = redis.get("testCluster");
                        count.incrementAndGet();
                    }catch (Exception e) {
                        LOGGER.error("redisSentinel test error:" + e.getMessage()); }
                    }
                 }
            },new Date(System.currentTimeMillis() + 10000));
        }
       //10秒后开始每秒统计
       taskScheduler.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                      long nowCount = count.get();
                      LOGGER.info("one cluster QPS " + (nowCount -lastCount )+"/s"; }
                     lastCount = nowCount;
            },new Date(System.currentTimeMillis() + 10000),1000);

    }

 

设置大于1个线程,逐步增加到60线程数,循环操作,qps不稳定,并且持续下降,本机8核cpu被打满

 

 可以看到,一开始有20万QPS,但是持续下降,一直掉到 1000以下,猜测是由于竞争激烈导致

设置60个线程情况下循环操作,每次操作停1ms,减少竞争,qps稳定在3.2w,本机8核cpu维持在40%左右

结论

不使用连接池的情况下异步操作,多线程竞争激烈情况下,qps会持续下降,模拟真实业务场景(停顿1ms )场景,可以维持3.2w QPS

使用连接池

lettuce的连接池也是异步的,构建方法如下

/**
     * 异步连接池
     */
    private BoundedAsyncPool<StatefulRedisClusterConnection<String, String>> asyncPool;

// cluster client 创建异步连接池 clusterClient 需要提前创建好
asyncPool = AsyncConnectionPoolSupport.createBoundedObjectPool(
                () -> clusterClient.connectAsync(StringCodec.UTF8).thenApply(connect->{
                    connect.setTimeout(Duration.ofSeconds(5));
                    connect.setAutoFlushCommands(false);
                    connect.setReadFrom(ReadFrom.MASTER_PREFERRED);
                    return connect;
        }), BoundedPoolConfig.builder().maxTotal(getPoolMaxTotal()).maxIdle(getPoolMaxIdle()).minIdle(getPoolMinIdle()).build());


 // 单节点,或者哨兵的client 创建异步连接池  asyncClient
        RedisClient asyncClient = RedisClient.create();
//TODO 这里需要初始化uri,需要区别单节点和哨兵
  RedisURI redisURI;
       //单节点带有密码时,会有无法正确识别协议问题,需要手工指定  https://github.com/lettuce-io/lettuce-core/issues/1543
        if (SINGLE.equals(type) && StringUtils.isNotBlank(password)) {
            asyncClient.setOptions(ClientOptions.builder().build());
        }
        asyncPool = AsyncConnectionPoolSupport.createBoundedObjectPool(
                () -> MasterReplica.connectAsync(asyncClient, StringCodec.UTF8, redisURI).thenApply(connect -> {
                    connect.setAutoFlushCommands(false);
                    connect.setReadFrom(ReadFrom.MASTER_PREFERRED);
                    return connect;
                }), BoundedPoolConfig.builder().maxTotal(getPoolMaxTotal()).maxIdle(getPoolMaxIdle()).minIdle(getPoolMinIdle()).build());

这里最大最小连接数靠参数注入

poolMaxTotal #总连接数

poolMinIdle #最小空闲连接数

poolMaxIdle #最大空闲的连接数

连接数测试

 这里设置最大链接数30,尝试获取50个链接且不释放

可以看到,获取到30个后,就无法获取

吞吐量测试

一开始实例化10个链接,用60线程循环操作的情况下,qps稳定在12w左右,本机8核cpu被打满

然后慢慢增加连接数,加到12个链接,执行一段时间后开始出现重连现象,QPS变得不稳定

结论

连接数建议设置成<12

总结

1、lettuce 连接池配置使用poolMaxTotal、poolMaxIdle配置,建议设置成一样,为了避免短连接的增加而引起资源上的消耗

2、poolMaxTotal建议设置成<12(默认设置8,自定义设置不建议超过12链接)

    在12个链接下,redis cluster 多线程吞吐可以达到12万QPS
3、高并发场景(单节点 对redis 请求超过 2.5wQPS )下建议使用连接池,单个连接适用于普通业务

文章来自个人专栏
java开发实践
6 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0