在Netty中设置低延迟客户端/服务器示例

时间:2014-09-24 20:59:05

标签: java netty

我有一个简单的ECHO服务器和使用Netty编写的客户端。服务器和客户端位于同一台计算机上。我期待的平均延迟大约为几毫秒,但是,无论我尝试什么,我都不能将延迟降低到亚毫秒的持续时间。任何帮助将不胜感激。

更新:即使使用System.nanoTime,我也会看到延迟大约25-30毫秒。

EchoClient

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

public class EchoClient {

    public static void main(String[] args) {

        if (args.length != 1) {
            System.err.println(String.format("usage: %s <num-msgs>", EchoClient.class.getCanonicalName()));
            System.exit(1);
        }

        final long NUM_MSGS = Integer.parseInt(args[0]);

        final EchoClientHandler echoClientHandler = new EchoClientHandler();

        final ExecutionHandler e =
                new ExecutionHandler(new OrderedMemoryAwareThreadPoolExecutor(4, 128 * 1024L, 128 * 1024L));
        ChannelFactory factory =
                new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
                                                  Executors.newCachedThreadPool());

        ClientBootstrap bootstrap = new ClientBootstrap(factory);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            @Override
            public ChannelPipeline getPipeline() throws Exception {
                return Channels.pipeline(new TestPayloadEncoder(),
                                         new TestPayloadDecoder(),
                                         e,
                                         echoClientHandler);
            }
        });
        bootstrap.setOption("tcpNoDelay", true);
        bootstrap.setOption("keepAlive", false);
        bootstrap.setOption("child.keepAlive", false);
        bootstrap.setOption("sendBufferSize", 128 * 1024L);
        bootstrap.setOption("receiveBufferSize", 128 * 1024L);

        for (int i = 0; i < NUM_MSGS; i++) {
            final InetSocketAddress serverAddr =
                    new InetSocketAddress("localhost", 8080);

            bootstrap.connect(serverAddr).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture f) throws Exception {
                    if (f.isSuccess()) {
                        f.getChannel().write(new TestPayload());
                    }
                }
            });
        }

        while (echoClientHandler.numMsgs.get() < NUM_MSGS);

        System.out.println(echoClientHandler.numMsgs);
        System.out.println(echoClientHandler.aggTime);
        System.out.println(String.format("mean transfer time: %.2fms",
                                         ((float) echoClientHandler.aggTime.get()) /
                                         echoClientHandler.numMsgs.get()));
        System.out.flush();

        e.releaseExternalResources();
        factory.releaseExternalResources();
    }

}

EchoClientHandler

import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

import java.util.concurrent.atomic.AtomicLong;

public class EchoClientHandler extends SimpleChannelHandler {

    public final AtomicLong numMsgs = new AtomicLong(0);
    public final AtomicLong aggTime = new AtomicLong(0);

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        long recvTime = System.currentTimeMillis();
        TestPayload m = (TestPayload) e.getMessage();
        aggTime.addAndGet(recvTime - m.getTime());
        numMsgs.incrementAndGet();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        e.getCause().printStackTrace();
        e.getChannel().close();
    }

}

EchoServer的

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

public class EchoServer {

    public static void main(String[] args) {
        ChannelFactory factory =
                new NioServerSocketChannelFactory(Executors.newFixedThreadPool(4),
                                                  Executors.newFixedThreadPool(32),
                                                  32);

        ServerBootstrap bootstrap = new ServerBootstrap(factory);
        final ExecutionHandler e =
                new ExecutionHandler(new OrderedMemoryAwareThreadPoolExecutor(4, 128 * 1024L, 128 * 1024L));
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            @Override
            public ChannelPipeline getPipeline() throws Exception {
                return Channels.pipeline(e, new EchoServerHandler());
            }
        });
        bootstrap.setOption("reuseAddr", true);
        bootstrap.setOption("keepAlive", false);
        bootstrap.setOption("child.reuseAddr", true);
        bootstrap.setOption("child.soLinger", 0);
        bootstrap.setOption("child.keepAlive", false);
        bootstrap.setOption("child.tcpNoDelay", true);
        bootstrap.setOption("child.sendBufferSize", 128 * 1024L);
        bootstrap.setOption("child.receiveBufferSize", 128 * 1024L);
        bootstrap.bind(new InetSocketAddress("localhost", 8080));
    }

}

EchoServerHandler

import org.jboss.netty.channel.*;

public class EchoServerHandler extends SimpleChannelHandler {

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        e.getChannel().write(e.getMessage());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        e.getCause().printStackTrace();
        e.getChannel().close();
    }

}

TestPayload

import org.jboss.netty.buffer.ChannelBuffer;

import java.util.Date;
import java.util.Random;

public class TestPayload {

    private static final int PREAMBLE_LEN = (Long.SIZE + Integer.SIZE) / 8;

    private static final Random RNG;
    static {
        RNG = new Random();
        RNG.setSeed(new Date().getTime());
    }

    private final int    paddingLen;
    private final byte[] padding;
    private final long   time;

    public TestPayload() {
        this(65536);
    }

    public TestPayload(int sizeInBytes) {
        this.paddingLen = sizeInBytes;
        this.padding = new byte[this.paddingLen];
        RNG.nextBytes(this.padding);
        this.time = System.currentTimeMillis();
    }

    private TestPayload(long time, int paddingLen, byte[] padding) {
        this.paddingLen = paddingLen;
        this.padding = padding;
        this.time = time;
    }

    public long getTime() {
        return this.time;
    }

    public void writeTo(ChannelBuffer buf) {
        buf.writeLong(this.time);
        buf.writeInt(this.paddingLen);
        buf.writeBytes(this.padding);
    }

    public static TestPayload readFrom(ChannelBuffer buf) {
        if (buf.readableBytes() < PREAMBLE_LEN) {
            return null;
        }

        buf.markReaderIndex();

        long time = buf.readLong();
        int paddingLen = buf.readInt();

        if (buf.readableBytes() < paddingLen) {
            buf.resetReaderIndex();
            return null;
        }

        byte[] padding = new byte[paddingLen];
        buf.readBytes(padding);

        return new TestPayload(time, paddingLen, padding);
    }

    public int getLength() {
        return PREAMBLE_LEN + this.paddingLen;
    }

1 个答案:

答案 0 :(得分:0)

您是否在不同的JVM中运行客户端和服务器?如果是这样,那么跨JVM边界的测量时间并不像你想象的那么直接。例如,使用System.nanoTime()不一定按照oracle java doc

工作
  

只有在计算在Java虚拟机的同一实例中获得的两个此类值之间的差异时,此方法返回的值才会有意义。

假设您可以找到一种可靠的方法来测量JVM的时间,并且您的目标是隔离Netty客户端发送到Netty服务器所需的时间,然后简化您的用例以尽可能地隔离它。例如,在上面的代码中,您计算​​发送/接收65536字节数组的时间。从计时实验中删除它,以帮助找出瓶颈所在。

你从多少次跑步中收集时间?你是否排除了Netty本身的初始化时间(在计时之前在客户端/服务器之间运行一些消息)?

另外,如何调整配置影响性能?有很多旋钮可以调整(线程池大小,发送/接收buff大小等等)。

您使用的是什么版本的Netty,并且在您写完后可以选择强制刷新吗?

我没有看到EchoClient的代码。您似乎复制/粘贴EchoClientHandler代码所在的EchoClient代码。

相关问题