本文进行Redisson多个代码版本性能调优和压测,最终百万并发下,单机能达成27万QPS的高性能。
文章末尾分别对每个版本产生的现象做了原因解析。
程序准备
Redisson客户端代码
版本1 - 没有调优
主程序
package org.example;
import io.netty.channel.nio.NioEventLoopGroup;
import org.redisson.Redisson;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;
import org.redisson.misc.CompletableFutureWrapper;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
// 按两次 Shift 打开“随处搜索”对话框并输入 `show whitespaces`,
// 然后按 Enter 键。现在,您可以在代码中看到空格字符。
public class Main {
private static final ExecutorService worker = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("worker-thread-" + thread.getId());
return thread;
}));
private static final ExecutorService printer = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("printer-thread-" + thread.getId());
return thread;
}));
public static void main(String[] args) throws InterruptedException {
int requests = Integer.parseInt(args[0]) ;
int connections = args.length > 1 ? Integer.parseInt(args[1]) : 1;
int threads = args.length > 2 ? Integer.parseInt(args[2]) : 1;
Config config = new Config();
NioEventLoopGroup group = new NioEventLoopGroup(threads);
config.setTransportMode(TransportMode.NIO); // 默认是NIO的方式
config.useSingleServer()
//可以用"rediss://"来启用SSL连接,前缀必须是redis:// or rediss://
.setAddress("redis://192.168.253.176:6379")
// .setTcpNoDelay(false)
// .setConnectionPoolSize(connections)
// .setConnectionMinimumIdleSize(connections)
// .setPassword("123456")
;
config.setThreads(threads);
config.setNettyThreads(threads);
config.setEventLoopGroup(group);
RedissonClient redissonClient = Redisson.create(config);
RAtomicLong key = redissonClient.getAtomicLong("count");
key.set(0);
Result result = getResult(requests, key, redissonClient);
long delta = result.end - result.start;
Thread.sleep(1000);
System.out.println( "总计: " + requests + "次" );
System.out.println( "成功: " + result.countSuccess[0] + "次" );
System.out.println( "总计: " + delta + "毫秒" );
System.out.println( "QPS: " + requests * 1000L / delta + "次/秒" );
System.exit(0);
}
private static Result getResult(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(requests);
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
//背压计数器
// AtomicInteger backpressure = new AtomicInteger(20000);
for(int i = 0; i < requests; i++)
{
CompletableFutureWrapper<Long> future = ( CompletableFutureWrapper<Long> ) key.incrementAndGetAsync();
// backpressure.decrementAndGet();
worker.execute( () -> {
try {
Long result = future.get();
latch.countDown();
// backpressure.incrementAndGet();
countSuccess[0]++;
if( countSuccess[0] % 10000 == 0 ){
printer.execute( () -> {
System.out.println( "第" + result + "次请求") ;
});}
} catch (InterruptedException | ExecutionException e) {
latch.countDown();
// backpressure.incrementAndGet();
printer.execute( () -> {
System.out.println( "异常:" + e.getLocalizedMessage()) ;
});
}
});
// while(backpressure.get() <= 0)
// {
// Thread.yield();
// }
}
latch.await();
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static Result getResultSync(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
for(int i = 0; i < requests; i++)
{
countSuccess[0] = (int) key.incrementAndGet();
printer.execute( () -> {
System.out.println( "第" + countSuccess[0] + "次请求") ;
});
}
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static class Result {
public final int[] countSuccess;
public final long start;
public final long end;
public Result(int[] countSuccess, long start, long end) {
this.countSuccess = countSuccess;
this.start = start;
this.end = end;
}
}
}
log4j日志配置log4j.properties
log4j.rootLogger=info, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=logs/app.log
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=5
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
pom.xml 文件配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>RedisTest</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.33.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-reload4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-reload4j</artifactId>
<version>2.0.13</version>
<!-- <scope>test</scope>-->
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 主动声明插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<!-- 绑定生命周期 -->
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<!-- 设置依赖的存放路径 -->
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
版本2 - 连接数调优
package org.example;
import io.netty.channel.nio.NioEventLoopGroup;
import org.redisson.Redisson;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;
import org.redisson.misc.CompletableFutureWrapper;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
// 按两次 Shift 打开“随处搜索”对话框并输入 `show whitespaces`,
// 然后按 Enter 键。现在,您可以在代码中看到空格字符。
public class Main {
private static final ExecutorService worker = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("worker-thread-" + thread.getId());
return thread;
}));
private static final ExecutorService printer = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("printer-thread-" + thread.getId());
return thread;
}));
public static void main(String[] args) throws InterruptedException {
int requests = Integer.parseInt(args[0]) ;
int connections = args.length > 1 ? Integer.parseInt(args[1]) : 1;
int threads = args.length > 2 ? Integer.parseInt(args[2]) : 1;
Config config = new Config();
NioEventLoopGroup group = new NioEventLoopGroup(threads);
config.setTransportMode(TransportMode.NIO); // 默认是NIO的方式
config.useSingleServer()
//可以用"rediss://"来启用SSL连接,前缀必须是redis:// or rediss://
.setAddress("redis://192.168.253.176:6379")
// .setTcpNoDelay(false)
.setConnectionPoolSize(connections)
.setConnectionMinimumIdleSize(connections)
// .setPassword("123456")
;
config.setThreads(threads);
config.setNettyThreads(threads);
config.setEventLoopGroup(group);
RedissonClient redissonClient = Redisson.create(config);
RAtomicLong key = redissonClient.getAtomicLong("count");
key.set(0);
Result result = getResult(requests, key, redissonClient);
long delta = result.end - result.start;
Thread.sleep(1000);
System.out.println( "总计: " + requests + "次" );
System.out.println( "成功: " + result.countSuccess[0] + "次" );
System.out.println( "总计: " + delta + "毫秒" );
System.out.println( "QPS: " + requests * 1000L / delta + "次/秒" );
System.exit(0);
}
private static Result getResult(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(requests);
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
//背压计数器
// AtomicInteger backpressure = new AtomicInteger(20000);
for(int i = 0; i < requests; i++)
{
CompletableFutureWrapper<Long> future = ( CompletableFutureWrapper<Long> ) key.incrementAndGetAsync();
// backpressure.decrementAndGet();
worker.execute( () -> {
try {
Long result = future.get();
latch.countDown();
// backpressure.incrementAndGet();
countSuccess[0]++;
if( countSuccess[0] % 10000 == 0 ){
printer.execute( () -> {
System.out.println( "第" + result + "次请求") ;
});}
} catch (InterruptedException | ExecutionException e) {
latch.countDown();
// backpressure.incrementAndGet();
printer.execute( () -> {
System.out.println( "异常:" + e.getLocalizedMessage()) ;
});
}
});
// while(backpressure.get() <= 0)
// {
// Thread.yield();
// }
}
latch.await();
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static Result getResultSync(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
for(int i = 0; i < requests; i++)
{
countSuccess[0] = (int) key.incrementAndGet();
printer.execute( () -> {
System.out.println( "第" + countSuccess[0] + "次请求") ;
});
}
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static class Result {
public final int[] countSuccess;
public final long start;
public final long end;
public Result(int[] countSuccess, long start, long end) {
this.countSuccess = countSuccess;
this.start = start;
this.end = end;
}
}
}
版本3 - TcpNoDelay调优
package org.example;
import io.netty.channel.nio.NioEventLoopGroup;
import org.redisson.Redisson;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;
import org.redisson.misc.CompletableFutureWrapper;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
// 按两次 Shift 打开“随处搜索”对话框并输入 `show whitespaces`,
// 然后按 Enter 键。现在,您可以在代码中看到空格字符。
public class Main {
private static final ExecutorService worker = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("worker-thread-" + thread.getId());
return thread;
}));
private static final ExecutorService printer = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("printer-thread-" + thread.getId());
return thread;
}));
public static void main(String[] args) throws InterruptedException {
int requests = Integer.parseInt(args[0]) ;
int connections = args.length > 1 ? Integer.parseInt(args[1]) : 1;
int threads = args.length > 2 ? Integer.parseInt(args[2]) : 1;
Config config = new Config();
NioEventLoopGroup group = new NioEventLoopGroup(threads);
config.setTransportMode(TransportMode.NIO); // 默认是NIO的方式
config.useSingleServer()
//可以用"rediss://"来启用SSL连接,前缀必须是redis:// or rediss://
.setAddress("redis://192.168.253.176:6379")
.setTcpNoDelay(false)
.setConnectionPoolSize(connections)
.setConnectionMinimumIdleSize(connections)
// .setPassword("123456")
;
config.setThreads(threads);
config.setNettyThreads(threads);
config.setEventLoopGroup(group);
RedissonClient redissonClient = Redisson.create(config);
RAtomicLong key = redissonClient.getAtomicLong("count");
key.set(0);
Result result = getResult(requests, key, redissonClient);
long delta = result.end - result.start;
Thread.sleep(1000);
System.out.println( "总计: " + requests + "次" );
System.out.println( "成功: " + result.countSuccess[0] + "次" );
System.out.println( "总计: " + delta + "毫秒" );
System.out.println( "QPS: " + requests * 1000L / delta + "次/秒" );
System.exit(0);
}
private static Result getResult(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(requests);
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
//背压计数器
// AtomicInteger backpressure = new AtomicInteger(20000);
for(int i = 0; i < requests; i++)
{
CompletableFutureWrapper<Long> future = ( CompletableFutureWrapper<Long> ) key.incrementAndGetAsync();
// backpressure.decrementAndGet();
worker.execute( () -> {
try {
Long result = future.get();
latch.countDown();
// backpressure.incrementAndGet();
countSuccess[0]++;
if( countSuccess[0] % 10000 == 0 ){
printer.execute( () -> {
System.out.println( "第" + result + "次请求") ;
});}
} catch (InterruptedException | ExecutionException e) {
latch.countDown();
// backpressure.incrementAndGet();
printer.execute( () -> {
System.out.println( "异常:" + e.getLocalizedMessage()) ;
});
}
});
// while(backpressure.get() <= 0)
// {
// Thread.yield();
// }
}
latch.await();
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static Result getResultSync(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
for(int i = 0; i < requests; i++)
{
countSuccess[0] = (int) key.incrementAndGet();
printer.execute( () -> {
System.out.println( "第" + countSuccess[0] + "次请求") ;
});
}
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static class Result {
public final int[] countSuccess;
public final long start;
public final long end;
public Result(int[] countSuccess, long start, long end) {
this.countSuccess = countSuccess;
this.start = start;
this.end = end;
}
}
}
版本4 - 虚拟机低配置的背压调优
package org.example;
import io.netty.channel.nio.NioEventLoopGroup;
import org.redisson.Redisson;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;
import org.redisson.misc.CompletableFutureWrapper;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
// 按两次 Shift 打开“随处搜索”对话框并输入 `show whitespaces`,
// 然后按 Enter 键。现在,您可以在代码中看到空格字符。
public class Main {
private static final ExecutorService worker = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("worker-thread-" + thread.getId());
return thread;
}));
private static final ExecutorService printer = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("printer-thread-" + thread.getId());
return thread;
}));
public static void main(String[] args) throws InterruptedException {
int requests = Integer.parseInt(args[0]) ;
int connections = args.length > 1 ? Integer.parseInt(args[1]) : 1;
int threads = args.length > 2 ? Integer.parseInt(args[2]) : 1;
Config config = new Config();
NioEventLoopGroup group = new NioEventLoopGroup(threads);
config.setTransportMode(TransportMode.NIO); // 默认是NIO的方式
config.useSingleServer()
//可以用"rediss://"来启用SSL连接,前缀必须是redis:// or rediss://
.setAddress("redis://192.168.253.176:6379")
.setTcpNoDelay(false)
.setConnectionPoolSize(connections)
.setConnectionMinimumIdleSize(connections)
// .setPassword("123456")
;
config.setThreads(threads);
config.setNettyThreads(threads);
config.setEventLoopGroup(group);
RedissonClient redissonClient = Redisson.create(config);
RAtomicLong key = redissonClient.getAtomicLong("count");
key.set(0);
Result result = getResult(requests, key, redissonClient);
long delta = result.end - result.start;
Thread.sleep(1000);
System.out.println( "总计: " + requests + "次" );
System.out.println( "成功: " + result.countSuccess[0] + "次" );
System.out.println( "总计: " + delta + "毫秒" );
System.out.println( "QPS: " + requests * 1000L / delta + "次/秒" );
System.exit(0);
}
private static Result getResult(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(requests);
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
//背压计数器
AtomicInteger backpressure = new AtomicInteger(20000);
for(int i = 0; i < requests; i++)
{
CompletableFutureWrapper<Long> future = ( CompletableFutureWrapper<Long> ) key.incrementAndGetAsync();
backpressure.decrementAndGet();
worker.execute( () -> {
try {
Long result = future.get();
latch.countDown();
// backpressure.incrementAndGet();
countSuccess[0]++;
if( countSuccess[0] % 10000 == 0 ){
printer.execute( () -> {
System.out.println( "第" + result + "次请求") ;
});}
} catch (InterruptedException | ExecutionException e) {
latch.countDown();
backpressure.incrementAndGet();
printer.execute( () -> {
System.out.println( "异常:" + e.getLocalizedMessage()) ;
});
}
});
//自旋式背压阻塞
while(backpressure.get() <= 0)
{
Thread.yield();
}
}
latch.await();
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static Result getResultSync(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
for(int i = 0; i < requests; i++)
{
countSuccess[0] = (int) key.incrementAndGet();
printer.execute( () -> {
System.out.println( "第" + countSuccess[0] + "次请求") ;
});
}
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static class Result {
public final int[] countSuccess;
public final long start;
public final long end;
public Result(int[] countSuccess, long start, long end) {
this.countSuccess = countSuccess;
this.start = start;
this.end = end;
}
}
}
版本5- 多连接乱序测试
运行配置
版本6 - 本机背压调优
package org.example;
import io.netty.channel.nio.NioEventLoopGroup;
import org.redisson.Redisson;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;
import org.redisson.misc.CompletableFutureWrapper;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
// 按两次 Shift 打开“随处搜索”对话框并输入 `show whitespaces`,
// 然后按 Enter 键。现在,您可以在代码中看到空格字符。
public class Main {
private static final ExecutorService worker = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("worker-thread-" + thread.getId());
return thread;
}));
private static final ExecutorService printer = Executors.newFixedThreadPool(1, (r -> {
Thread thread = new Thread(r);
thread.setName("printer-thread-" + thread.getId());
return thread;
}));
public static void main(String[] args) throws InterruptedException {
int requests = Integer.parseInt(args[0]) ;
int connections = args.length > 1 ? Integer.parseInt(args[1]) : 1;
int threads = args.length > 2 ? Integer.parseInt(args[2]) : 1;
Config config = new Config();
NioEventLoopGroup group = new NioEventLoopGroup(threads);
config.setTransportMode(TransportMode.NIO); // 默认是NIO的方式
config.useSingleServer()
//可以用"rediss://"来启用SSL连接,前缀必须是redis:// or rediss://
.setAddress("redis://192.168.253.176:6379")
.setTcpNoDelay(false)
.setConnectionPoolSize(connections)
.setConnectionMinimumIdleSize(connections)
// .setPassword("123456")
;
config.setThreads(threads);
config.setNettyThreads(threads);
config.setEventLoopGroup(group);
RedissonClient redissonClient = Redisson.create(config);
RAtomicLong key = redissonClient.getAtomicLong("count");
key.set(0);
Result result = getResult(requests, key, redissonClient);
long delta = result.end - result.start;
Thread.sleep(1000);
System.out.println( "总计: " + requests + "次" );
System.out.println( "成功: " + result.countSuccess[0] + "次" );
System.out.println( "总计: " + delta + "毫秒" );
System.out.println( "QPS: " + requests * 1000L / delta + "次/秒" );
System.exit(0);
}
private static Result getResult(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(requests);
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
//背压计数器
AtomicInteger backpressure = new AtomicInteger(30000);
for(int i = 0; i < requests; i++)
{
CompletableFutureWrapper<Long> future = ( CompletableFutureWrapper<Long> ) key.incrementAndGetAsync();
backpressure.decrementAndGet();
worker.execute( () -> {
try {
Long result = future.get();
latch.countDown();
backpressure.incrementAndGet();
countSuccess[0]++;
if( countSuccess[0] % 10000 == 0 ){
printer.execute( () -> {
System.out.println( "第" + result + "次请求") ;
});}
} catch (InterruptedException | ExecutionException e) {
latch.countDown();
backpressure.incrementAndGet();
printer.execute( () -> {
System.out.println( "异常:" + e.getLocalizedMessage()) ;
});
}
});
//自旋式背压阻塞
while(backpressure.get() <= 0)
{
Thread.yield();
}
}
latch.await();
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static Result getResultSync(int requests, RAtomicLong key, RedissonClient redissonClient) throws InterruptedException {
int[] countSuccess = new int[1];
long start = System.currentTimeMillis();
for(int i = 0; i < requests; i++)
{
countSuccess[0] = (int) key.incrementAndGet();
printer.execute( () -> {
System.out.println( "第" + countSuccess[0] + "次请求") ;
});
}
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
return new Result(countSuccess, start, end);
}
private static class Result {
public final int[] countSuccess;
public final long start;
public final long end;
public Result(int[] countSuccess, long start, long end) {
this.countSuccess = countSuccess;
this.start = start;
this.end = end;
}
}
}
redis服务端准备
虚拟机规格
redis状态
redis计数器准备
压测结果
压测运行配置
jvm内存:4G
请求并发数:1000000
线程数:1
连接数:1
版本1 - 没有调优
总计: 1000000次
成功: 245320次
总计: 33180毫秒
QPS: 30138次/秒连接数:默认24 最大64
版本2 - 连接数调优
总计: 1000000次
成功: 1000000次
总计: 5203毫秒
QPS: 192196次/秒连接数:默认1 最大1
版本3 - TcpNoDelay调优
总计: 1000000次
成功: 1000000次
总计: 5203毫秒
QPS: 235349次/秒连接数:默认1 最大1
TcpNoDelay = false
版本4 - 虚拟机低配置的背压调优
将客户端程序放在虚拟机(2C4G)里跑
压测命令
java -Djava.net.preferIPv4Stack=true -Dio.netty.leakDetectionLevel=advanced -Xmx2048m -Xms2048m -cp RedisTest-1.0-SNAPSHOT.jar:lib/* org.example.Main 1000000 1 1
背压调优前
出现OOM
响应超时
程序进入恶性循环
背压调优后
总计: 1000000次
成功: 1000000次
总计: 13545毫秒
QPS: 73827次/秒
版本5- 多连接乱序测试
版本6 - 本机背压调优
总计: 1000000次
成功: 1000000次
总计: 3623毫秒
QPS: 276014次/秒
原因分析
版本1 - 没有调优
1. 当连接池大小 小于 10,发送消息后会立即释放连接到连接池,快速复用模式
2. 当连接池大小 大于等于 10 ,要等到发送成功IO回调后,再释放,高并发场景下,很快进入连接不够用状态,而且发送失败会重试3次,一直不释放连接,变成践踏雪崩现象
版本2 - 连接数调优
1. 当连接池大小 小于 10,发送消息后会立即释放连接到连接池,快速复用模式
2. CPU性能强劲(14个内核),jvm堆内存4G充足
版本3 - TcpNoDelay调优
1. tcp层不再采用立即发送响应的串行低时延模式,而是采用并行发送的高吞量模式
左侧是:串行低时延模式,右侧是:并行发送的高吞量模式
版本4 - 虚拟机低配置的背压调优
1. CPU性能弱(2个内核),总体内存4G,jvm堆内存2G不足
2. 背压调优化,解决了内存不足的问题,CPU处理慢只是降低了QPS,不会导致雪崩
CPU很快打爆,但是内存正常,如下截图
版本5 - 多连接乱序测试
1. Redisson发送命令后,会将命令压入缓冲区中,操作系统调度发送命令,调度过程中,二个连接是没有次序依赖关系的,等待服务端响应后,也就破坏了次序。