程序准备

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

总结

redisnetty
服务端IO线程11
服务端工作线程11
TCP连接数6565
客户端IO线程数22
请求次数2000020000
QPS7490次/秒7513次/秒
平均响应时间0.134毫秒0.133毫秒

netty的性能的确可以比肩redis

更多推荐