簡體   English   中英

在Netty中設置低延遲客戶端/服務器示例

[英]Setting up low-latency client/server example in Netty

我有一個使用Netty編寫的簡單ECHO服務器和客戶端。 服務器和客戶端在同一台計算機上。 我原本期望的平均延遲大約是幾毫秒,但是無論我如何嘗試,我都無法將延遲降低到毫秒以下的持續時間。 任何幫助將不勝感激。

更新:即使使用System.nanoTime,我也看到大約25-30ms的延遲。

回聲客戶端

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();
    }

}

回聲服務器

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;
    }

您是否在不同的JVM中運行客戶端和服務器? 如果是這樣,跨JVM邊界衡量時間就不會像您想的那樣簡單。 例如,根據oracle java doc,使用System.nanoTime()不一定能正常工作:

僅當計算在Java虛擬機的同一實例中獲得的兩個此類值之間的差時,此方法返回的值才有意義。

假設您可以找到一種可靠的方法來衡量JVM之間的時間,並且您的目標是確定Netty客戶端發送至Netty服務器所花費的時間,然后簡化您的用例以盡可能地隔離該時間。 例如,在上面的代碼中,您正在計算發送/接收65536字節數組的時間。 從時序實驗中刪除此內容,以幫助隔離瓶頸所在。

您從多少次計時中收集時間? 您是否排除Netty本身的初始化時間(在進行計時之前在客戶端/服務器之間運行一些消息)?

調整配置又如何影響性能? 有很多旋鈕可以調整(線程池大小,發送/接收buff大小等)。

您正在使用哪個版本的Netty,並且在編寫后是否可以選擇強制刷新?

我看不到EchoClient的代碼。 看起來您EchoClientHandler EchoClient的代碼復制/粘貼到EchoClient的代碼中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM