netty确实牛逼,性能比肩redis,实践压测
·
程序准备
redis部署

redis客户端
package org.example;
import io.netty.channel.SingleThreadEventLoop;
import io.netty.channel.nio.NioEventLoopGroup;
import org.redisson.Redisson;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.concurrent.*;
// 按两次 Shift 打开“随处搜索”对话框并输入 `show whitespaces`,
// 然后按 Enter 键。现在,您可以在代码中看到空格字符。
public class Main {
private static final ExecutorService work = Executors.newSingleThreadExecutor();
public static void main(String[] args) throws InterruptedException {
int requests = Integer.parseInt(args[0]) ;
int threads = args.length > 1 ? Integer.parseInt(args[1]) : 1;
Config config = new Config();
config.setTransportMode(TransportMode.NIO); // 默认是NIO的方式
config.useSingleServer()
//可以用"rediss://"来启用SSL连接,前缀必须是redis:// or rediss://
.setAddress("redis://192.168.253.148:6379")
// .setPassword("123456")
;
config.setThreads(threads);
config.setNettyThreads(threads);
config.setEventLoopGroup(new NioEventLoopGroup(threads));
RedissonClient redissonClient = Redisson.create(config);
RAtomicLong key_long = redissonClient.getAtomicLong("key_long");
key_long.set(0);
CountDownLatch latch = new CountDownLatch(requests);
long start = System.currentTimeMillis();
for(int i = 0; i < requests; i++)
{
RFuture<Long> future = key_long.incrementAndGetAsync();
work.submit(
() ->
{
Long tmp;
try {
tmp = future.get();
latch.countDown();
System.out.println("第" + tmp + "次请求");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
);
}
latch.await();
long end = System.currentTimeMillis();
redissonClient.getConfig().getEventLoopGroup().shutdownGracefully();
redissonClient.shutdown();
long delta = end - start;
Thread.sleep(3000);
System.out.println( "总计: " + delta + "毫秒" );
System.out.println( "QPS: " + requests * 1000L / delta + "次/秒" );
}
}
netty-服务端
package org.example;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 按两次 Shift 打开“随处搜索”对话框并输入 `show whitespaces`,
// 然后按 Enter 键。现在,您可以在代码中看到空格字符。
public class Main {
private static int count = 0;
//工作线程
private static ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
new ServerBootstrap()
.group(new NioEventLoopGroup(1))
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.childOption(ChannelOption.SO_BACKLOG, 1024000)
.childOption(ChannelOption.TCP_NODELAY, true)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
executorService.submit(
( ) ->
{
String res = "第" + (++count) + "次请求";
// 建议使用 ctx.alloc() 创建 ByteBuf
ByteBuf response = ctx.alloc().buffer();
response.writeCharSequence(res, Charset.defaultCharset());
ctx.writeAndFlush(response);
}
);
}
});
}
}).bind(8080);
}
}
部署命令
![]()
netty-客户端
package org.example;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import java.nio.charset.Charset;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;
// 按两次 Shift 打开“随处搜索”对话框并输入 `show whitespaces`,
// 然后按 Enter 键。现在,您可以在代码中看到空格字符。
public class Main {
private static final ExecutorService worker = Executors.newSingleThreadExecutor();
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]) : 2;
AtomicInteger count = new AtomicInteger(0);
CountDownLatch latch = new CountDownLatch(requests);
//一个读,一个写
NioEventLoopGroup group = new NioEventLoopGroup(threads);
Bootstrap bootstrap = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
worker.submit(
() ->
{
if( count.incrementAndGet() <= requests )
{
ctx.channel().writeAndFlush("request");
}
latch.countDown();
ByteBuf buffer = (ByteBuf) msg;
System.out.println (buffer.toString(Charset.defaultCharset()));
}
);
// 思考:需要释放 buffer 吗
}
});
}
});
Channel[] channels = new Channel[connections];
for (int i = 0; i < connections; i++) {
channels[i] = bootstrap.connect("192.168.253.148", 8080).sync().channel();
// channels[i] = bootstrap.connect("127.0.0.1", 8080).sync().channel();
}
long start = System.currentTimeMillis();
for (int i = 0; i < connections; i++) {
if( count.incrementAndGet() <= requests ) {
channels[i].writeAndFlush("request");
}
}
latch.await();
long end = System.currentTimeMillis();
for (int i = 0; i < connections; i++) {
channels[i].close().sync();
}
group.shutdownGracefully();
Thread.sleep(1000);
long delta = end - start;
System.out.println("总计" + requests + "次请求, 耗时" + delta + "毫秒");
System.out.println("QPS " + requests * 1000L / delta + "次/秒" );
}
}
虚拟机准备
192.168.253.148 是服务端,192.168.253.149是客户端,
两台虚拟机配置规格一致



压测命令
redis客户端

QPS 7490次/秒
netty客户端

QPS 7513次/秒
tcpdump分析
redis

netty

总结
| redis | netty | |
| 服务端IO线程 | 1 | 1 |
| 服务端工作线程 | 1 | 1 |
| TCP连接数 | 65 | 65 |
| 客户端IO线程数 | 2 | 2 |
| 请求次数 | 20000 | 20000 |
| QPS | 7490次/秒 | 7513次/秒 |
| 平均响应时间 | 0.134毫秒 | 0.133毫秒 |
netty的性能的确可以比肩redis
更多推荐


所有评论(0)