【问题标题】:Netty Data Validation IssuesNetty 数据验证问题
【发布时间】:2021-07-14 08:59:52
【问题描述】:

我已将 Netty 提供的 echo 示例修改为乒乓基准测试,该测试计算客户端发送和接收的不同消息大小的平均延迟。我遇到的问题是数据验证。

问题如下,

  1. 客户端通道接收缓冲区的默认容量默认为 2048。这是一个问题,因为我想测试更大的消息大小 (4 MB)。
  2. 经过大量迭代,接收缓冲区的容量会变小。当消息大于 64 字节时,这会导致验证错误。

任何解决这些问题的建议或帮助都会很棒。谢谢。

这是我的代码,EchoClient:

public final class EchoClient {

static final boolean SSL = System.getProperty("ssl") != null;
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));

static final String TRANSPORT = System.getProperty("transport", "Nio"); 
static final int ITER = Integer.parseInt(System.getProperty("iter", "1000"));
static final int WARMUP_ITERS = Integer.parseInt(System.getProperty("warmupIters", Integer.toString(ITER/10)));

public static void main(String[] args) throws Exception {
    // Configure SSL.git
    final SslContext sslCtx;
    if (SSL) {
        sslCtx = SslContextBuilder.forClient()
            .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
    } else {
        sslCtx = null;
    }

    // Configure the transport channel
    Class transportChannel = (TRANSPORT.equals("Nio")) ? NioSocketChannel.class : OioSocketChannel.class;  

    // Configure the client.
    EventLoopGroup group = (TRANSPORT.equals("Nio")) ? new NioEventLoopGroup() : new OioEventLoopGroup();
    try {
        Bootstrap b = new Bootstrap();
        b.group(group)
         .channel(transportChannel)
         .option(ChannelOption.TCP_NODELAY, true)
         .handler(new ChannelInitializer<SocketChannel>() {
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
                 ChannelPipeline p = ch.pipeline();
                 if (sslCtx != null) {
                     p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
                 }
                 //p.addLast(new LoggingHandler(LogLevel.INFO));
                 p.addLast(new EchoClientHandler());
             }
         });

        // Start the client.
        ChannelFuture f = b.connect(HOST, PORT).sync();

        // Wait until the connection is closed.
        f.channel().closeFuture().sync();
    } finally {
        // Shut down the event loop to terminate all threads.
        group.shutdownGracefully();
    }
}
}

回声服务器:

public final class EchoServer {

static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
static final String TRANSPORT = System.getProperty("transport", "Nio"); 

public static void main(String[] args) throws Exception {
    // Configure SSL.
    final SslContext sslCtx;
    if (SSL) {
        SelfSignedCertificate ssc = new SelfSignedCertificate();
        sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
    } else {
        sslCtx = null;
    }

    // Configure the transport channel
    Class transportChannel = (TRANSPORT.equals("Nio")) ? NioServerSocketChannel.class : OioServerSocketChannel.class;  

    // Configure the server
    EventLoopGroup bossGroup = (TRANSPORT.equals("Nio")) ? new NioEventLoopGroup(1) : new OioEventLoopGroup(1);
    EventLoopGroup workerGroup = (TRANSPORT.equals("Nio")) ? new NioEventLoopGroup() : new OioEventLoopGroup(); 

    final EchoServerHandler serverHandler = new EchoServerHandler();
    try {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
         .channel(transportChannel)
         .option(ChannelOption.SO_BACKLOG, 100)
         .handler(new LoggingHandler(LogLevel.INFO))
         .childHandler(new ChannelInitializer<SocketChannel>() {
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
                 ChannelPipeline p = ch.pipeline();
                 if (sslCtx != null) {
                     p.addLast(sslCtx.newHandler(ch.alloc()));
                 }
                 //p.addLast(new LoggingHandler(LogLevel.INFO));
                 p.addLast(serverHandler);
             }
         });

        // Start the server.
        ChannelFuture f = b.bind(PORT).sync();

        // Wait until the server socket is closed.
        f.channel().closeFuture().sync();
    } finally {
        // Shut down all event loops to terminate all threads.
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}
}

EchoClientHandler:

public class EchoClientHandler extends ChannelInboundHandlerAdapter {

long startTime; 
long endTime;
long durations = 0;

private ChannelHandlerContext ctx;

int iter = EchoClient.ITER;
int skip = EchoClient.WARMUP_ITERS;
int j = iter;
int size = 1;

/**
 * Creates a client-side handler.
 */
public EchoClientHandler() {
}

private ByteBuf createMsg(int size) {
    ByteBuf message = Unpooled.directBuffer(size);
    for (int i = 0; i < message.capacity(); i ++) {
        message.writeByte((byte) i);
    }

    return message;
}

private void checkDataValidity(ByteBuf msgRecvByClient) {
    ByteBuf msgSentByClient = createMsg(size);

    if (!msgRecvByClient.equals(msgSentByClient)) {
        System.err.println("ERROR: Message was corrupted");
        ctx.close();
    }
}

@Override
public void channelActive(ChannelHandlerContext ctx) {
    this.ctx = ctx;
    ByteBuf msg = createMsg(size);
    startTime = System.nanoTime();
    ctx.writeAndFlush(msg);
    j--;
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    checkDataValidity((ByteBuf)msg);

    if (j < iter - skip) {
        endTime = System.nanoTime();
        durations += (endTime - startTime)/(double)1000;

        if (j == 0) {
            System.out.printf("%d bytes has latency avg: %10.2f us\n", size, durations/((double)(iter-skip)));
            durations = 0;
            j = iter;
            size *= 2;
            if (size > EchoClient.SIZE) ctx.close();
        }
    }

    ByteBuf newMsg = createMsg(size);

    startTime = System.nanoTime();
    ctx.write(newMsg);
    j--;   
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
    //ByteBuf newMsg = createMsg(size);

    //startTime = System.nanoTime();
    //ctx.writeAndFlush(newMsg);
    //j--;

    ctx.flush();
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    // Close the connection when an exception is raised.
    cause.printStackTrace();
    ctx.close();
}
}

EchoServerHandler:

public class EchoServerHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ctx.write(msg);
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
    ctx.flush();
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    // Close the connection when an exception is raised.
    cause.printStackTrace();
    ctx.close();
}
}

【问题讨论】:

    标签: netty


    【解决方案1】:

    您的代码没有任何消息边界,因此 netty 将根据内部缓冲区大小以及任何时候套接字上可用的数据量从套接字读取数据。如果您想读取特定长度的消息,那么您应该定义该应用程序级别的消息边界。可以在此处找到如何设置此类消息边界的一个非常简单的示例:

    https://github.com/netty/netty/blob/4.1/example/src/main/java/io/netty/example/factorial/FactorialServerInitializer.java#L49

    通常,人们会使用更标准的消息编码格式,例如 google 协议缓冲区。

    【讨论】:

      猜你喜欢
      • 2021-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多