简体   繁体   中英

Netty UDP Performance Issue

I've implemented three small UDP server. One with a plain Java DatagramSocket (threaded), one with Netty and the last one also with Netty but with a threaded message handling (because Netty doesn't support multiple threads with UDP).

After some measurements I got the following results for requests per second:

  • DatagramSocket ~30.000 requests/second
  • Netty ~1.500 requests/second
  • Netty (threaded): ~8.000 requests/second

The real application I have to implement must handle > 25.000 requests/second. So my question is if I make something wrong with Netty or if Netty is not designed to handle that much of connections per second?

Here are the implementations

DatagramSocket Main

public static void main(String... args) throws Exception {
    final int port = Integer.parseInt(args[0]);
    final int threads = Integer.parseInt(args[1]);
    final int work = Integer.parseInt(args[2]);

    DATAGRAM_SOCKET = new DatagramSocket(port);

    for (int i = 0; i < threads; i++) {
        new Thread(new Handler(work)).start();
    }
}

DatagramSocket Handler

private static final class Handler implements Runnable {
    private final int work;

    public Handler(int work) throws SocketException {
        this.work = work;
    }

    @Override 
    public void run() {
        try {
            while (!DATAGRAM_SOCKET.isClosed()) {
                final DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
                DATAGRAM_SOCKET.receive(receivePacket);
                final InetAddress ip = receivePacket.getAddress();
                final int port = receivePacket.getPort();
                final byte[] sendData = "Hey there".getBytes();
                Thread.sleep(RANDOM.nextInt(work));
                final DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, ip, port);
                DATAGRAM_SOCKET.send(sendPacket);
            }
        } catch (Exception e) {
            System.out.println("ERROR: " + e.getMessage());
        }
    }
}

Netty Main

public static void main(String[] args) throws Exception
{
    final int port = Integer.parseInt(args[0]);
    final int sleep = Integer.parseInt(args[1]);

    final Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(new NioEventLoopGroup());
    bootstrap.channel(NioDatagramChannel.class);
    bootstrap.handler(new MyNettyUdpHandler(sleep));
    bootstrap.bind(port).sync().channel().closeFuture().sync();
}

Netty Handler (threaded)

public class MyNettyUdpHandler extends MessageToMessageDecoder<DatagramPacket> {
    private final Random random = new Random(System.currentTimeMillis());
    private final int sleep;

    public MyNettyUdpHandler(int sleep) {
        this.sleep = sleep;
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket, List list) throws Exception {
        new Thread(() -> {
            try {
                Thread.sleep(random.nextInt(sleep));
            } catch (InterruptedException e) {
                System.out.println("ERROR while sleeping");
            }

            final ByteBuf buffer = Unpooled.buffer(64);
            buffer.writeBytes("Hey there".getBytes());
            channelHandlerContext.channel().writeAndFlush(new DatagramPacket(buffer, datagramPacket.sender()));
        }).start();
    }
}

The non threaded Netty Handler is the same but without the thread.

You can change your Netty decode() method like so to make it equivalent to the DatagramSocket code:

@Override
protected void decode(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket, List list) throws Exception {
  final Channel channel = channelHandlerContext.channel();
  channel.eventLoop().schedule(() -> {
    final ByteBuf buffer = Unpooled.buffer(64);
    buffer.writeBytes("Hey there".getBytes());
    channel.writeAndFlush(new DatagramPacket(buffer, datagramPacket.sender()));
  }, random.nextInt(sleep), TimeUnit.MILLISECONDS);
}

But I'm guessing the sleep() code is simulating business code you will later execute. If that is the case make sure you don't run blocking code inside the handler.

EDIT:

To answer your question below: You got a bit confused with the channels. You create a pipeline in the bootstrap, and you bind to some port. The returned channel is the server channel. The channel in the handlers method (your decode method in your case), is like the socket you get when you accept() in traditional socket programming. Note that port you extracted from the incoming DatagramPacket - it's roughly the same. So you send data to the client back on this channel.

The code I wrote that schedules the response is simply doing the same as what your DatagramSocket code, and the threaded netty code you wrote. I wasn't sure why you did that, and simply assumed you have a business requirement to delay the response. If this isn't the case, you can remove the schedule call, and your code will run much faster. If your business logic is non-blocking, and runs in a few millis, you're done. If it's blocking, you need to try to find a non-blocking alternative, or run it in an executor, ie not on the event loop.

Hope this helps, even though this wasn't part of your original question. Netty is awesome, and I hate seeing bad examples and bad vibes about it so it's worth my time I guess ;)

Creating a thread in every decode() is inefficient. You can submit the task to channel.eventLoop() as Eran said if the task is simple and won't block(In fact decode() in MesaggeToMessageDecoder s is executed by the channel's EventLoop ,so you need not submit it manually unless you want to shedule it). Or you can submit the task to a ThreadPoolExecutor or EventExecutorGroup . The latter is better because you can add listeners to the Future returned by EventExecutorGroup.submit() so you don't have to wait for the task to be completed. My English is poor,hope these can help you.

Edit: You can write as following,just executing the simple logic code in the EventLoop (ie.I/O thread):

@Override
protected void decode(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket, List list) throws Exception {
        //do something simple with datagramPacket
        ...

        final ByteBuf buffer = Unpooled.buffer(64);
        buffer.writeBytes("Hey there".getBytes());
        channelHandlerContext.channel().writeAndFlush(new DatagramPacket(buffer, datagramPacket.sender()));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM