当前位置: 首页 > 知识库问答 >
问题:

Netty ServerBootStrap选项或childOption及其缓冲区大小对速度的影响

周和志
2023-03-14

当缓冲区大小增加到超过默认值和/或在ServerBootStrap对象中选择了错误的覆盖位置(选项child Option)时,Netty似乎会降低上传/下载速度。当连接具有更大的延迟(约300毫秒)时,这变得更加明显

设置:

Netty客户端位于MacOS上,只有默认值。使用具有300ms DNS延迟的“网络链接调节器”。默认值:SendBuffer/ReceiveBuffer/LowWaterMark/HighWaterMark-128KB/128KB/32KB/64KB。

Netty 服务器位于 Windows 8.1 上,其默认值为“发送缓冲区”/“接收”“低”“低水位标记”/“高水位标记”-“64KB”/“64KB”。”

网络版本 4.1.6 最终版。

使用wireshark测量速度,使用具有设置的IO图:Y轴-

设备在本地网络上。

结果(速度值):

服务器-

Setting location\SO_SNDBUF   | 64KB   | 128KB  | 1024KB
-----------------------------------------------------------------
option                       | 3.6MB/s| 3.6MB/s| 3.6MB/s
childOption                  | 0.2MB/s| 0.5MB/s| 3.6MB/s

客户-

Setting location\SO_RCVBUF   | 64KB   | 128KB  | 1024KB
-----------------------------------------------------------------
option                       | 0.2MB/s| 0.5MB/s| 3.6MB/s
childOption                  | 3.6MB/s| 0.4MB/s| 3.6MB/s

服务器代码:

“参数”值:

“发送到客户端”/“发送到服务器”(隐式)用于传输方向。“选项”/“子选项”用于覆盖类型。“1”/“2”/“3” 表示缓冲区值。

ObjectEchoServer.java

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;

public final class ObjectEchoServer {

    static final int PORT = 8007;

    public static void main(String[] args) throws Exception {
        int rcvBuf, sndBuf, lowWaterMark, highWaterMark;
        rcvBuf = sndBuf = lowWaterMark = highWaterMark = 0;
        switch (args[2]){
            case "1":
                rcvBuf = 64;
                sndBuf = 64;
                lowWaterMark = 32;
                highWaterMark = 64;
                break;
            case "2":
                rcvBuf = 128;
                sndBuf = 128;
                lowWaterMark = 64;
                highWaterMark = 128;
                break;
            case "3":
                rcvBuf = 1024;
                sndBuf = 1024;
                lowWaterMark = 512;
                highWaterMark = 1024;
                break;

        }

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline p = ch.pipeline();
                    p.addLast(
                            new ObjectEncoder(),
                            new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                            new ObjectEchoServerHandler(args[0]));
                }
             });
            if(args[1].equalsIgnoreCase("childOption")) {
                b.childOption(ChannelOption.SO_RCVBUF, rcvBuf * 1024);
                b.childOption(ChannelOption.SO_SNDBUF, sndBuf * 1024);
                b.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(lowWaterMark * 1024, highWaterMark * 1024));
            } else if(args[1].equalsIgnoreCase("option")){
                b.option(ChannelOption.SO_RCVBUF, rcvBuf * 1024);
                b.option(ChannelOption.SO_SNDBUF, sndBuf * 1024);
                b.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(lowWaterMark * 1024, highWaterMark * 1024));
            }
            // Bind and start to accept incoming connections.
            b.bind(PORT).sync().channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

ObjectEchoServerHandler.java

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class ObjectEchoServerHandler extends ChannelInboundHandlerAdapter {
    private Object msg;
    ChannelHandlerContext ctx;
    String sendToClient;

    public ObjectEchoServerHandler(String sendToClient){
        this.sendToClient = sendToClient;

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx){
        this.ctx = ctx;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if(sendToClient.equalsIgnoreCase("sendToClient")) {//send a data stream to server
            this.msg = msg;
            ctx.writeAndFlush(msg).addListener(trafficGenerator);
        } //else receive a data stream from client
    }

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

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    private final ChannelFutureListener trafficGenerator = new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) {
            if (future.isSuccess()) {
                ctx.writeAndFlush(msg).addListener(trafficGenerator);
            } else {
                future.cause().printStackTrace();
                future.channel().close();
            }
        }
    };
}

客户代码:

“参数”值:

“sendToClient”/“sendToServer”(隐式)用于传输方向。

ObjectEchoClient.java

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

public final class ObjectEchoClient {

    static final String HOST = System.getProperty("host", "127.0.0.1");
    static final int PORT = 8007;
    static final int SIZE = 256;

    public static void main(String[] args) throws Exception {

        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline p = ch.pipeline();
                    p.addLast(
                            new ObjectEncoder(),
                            new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                            new ObjectEchoClientHandler(args[0]));
                    System.out.println("senbuf:"+ ch.config().getSendBufferSize());
                    System.out.println("waterhigh:"+ ch.config().getWriteBufferWaterMark().high());
                    System.out.println("waterlow:"+ ch.config().getWriteBufferWaterMark().low());
                    System.out.println("recbuf:"+ ch.config().getReceiveBufferSize());

                }
             });
            // Start the connection attempt.
            b.connect(HOST, PORT).sync().channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

ObjectEchoClientHandler.java

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.util.ArrayList;
import java.util.List;

public class ObjectEchoClientHandler extends ChannelInboundHandlerAdapter {

    private final List<String> firstMessage;
    private ChannelHandlerContext ctx;
    private String sendToClient;

    ObjectEchoClientHandler(String sendToClient) {
        this.sendToClient = sendToClient;

        firstMessage = new ArrayList<>(ObjectEchoClient.SIZE);
        for (int i = 0; i < ObjectEchoClient.SIZE; i++) {
            firstMessage.add(Integer.toString(i));
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        this.ctx = ctx;
      if(sendToClient.equalsIgnoreCase("sendToClient")) {//get a data stream from server
          this.ctx.writeAndFlush(firstMessage);
      } else {//send a stream of to server
          this.ctx.writeAndFlush(firstMessage).addListener(trafficGenerator);
      }
    }

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

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

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    private final ChannelFutureListener trafficGenerator = new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) {
            if (future.isSuccess()) {
                ctx.writeAndFlush(firstMessage).addListener(trafficGenerator);
            } else {
                future.cause().printStackTrace();
                future.channel().close();
            }
        }
    };
}

最大的问题是:

设置缓冲区的正确方法/位置是什么?我发现的信息(主要是代码示例)到处都是。

一个线程(netty 4. x中ServerBootstrap.option()和ServerBootstrap.childOption()有什么区别)说ServerBootStrap.childOption应该用于每个客户端信息,所以我认为两个缓冲区都应该在ServerBootStrap.childOption中。

共有1个答案

符风畔
2023-03-14

我认为这是对的b.option(ChannelOption.SO_RCVBUF,RCVBUF*1024);b、 选项(ChannelOptions.SO_SNDBUF,SNDBUF*1024)

 类似资料:
  • 我想在我的小libgdx游戏中使用框架缓冲区。 游戏使用了,我修改了s摄像头,使其使用50宽31高的视口。然后我将的投影矩阵设置为。这样做,我有一个分辨率独立的游戏,我可以使用我自己的“世界单位”,而不是使用像素。 但是现在,如果我创建一个,我必须给它一个大小。我必须给它摄像机视口的大小还是屏幕的大小(以像素为单位)? 而且,当我渲染的东西,我可以渲染他们在,这意味着渲染在几乎在游戏窗口的中间?

  • 问题内容: 根据文档,使用默认缓冲区大小,而第二个构造函数允许设置缓冲区大小。 public BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。 但是,文档没有提到默认的缓冲区大小是多少。 BufferedReader的默认缓冲区大小是多少? 问题答案: 默认缓冲区大小为8192个字符 http://developer.android.com/ref

  • 根据文档,使用默认缓冲区大小,而第二个构造函数,允许设置缓冲区大小。 公共缓冲区读取器(读取器输入) 创建使用默认大小的输入缓冲区的缓冲字符输入流。 但是,文档没有提到默认缓冲区大小。 BufferedReader的默认缓冲区大小是多少?

  • 我正在学习Java I/O。因此,使用缓冲流可以减少读取或写入所需的时间,因为如果使用普通的FileInputStream,每次调用读取时都会获取一个字节,但如果使用缓冲区,则会获取指定大小的数据并将其存储在内存中。所以我试着在实践中看到这一点。 我已将BufferedInputStream的缓冲区大小设置为512,8192,65536。每次需要87秒才能完成执行。所以我尝试使用FileInput

  • 我在游戏中使用延迟渲染器。因此,第一个渲染过程是使用多个渲染rarget创建反照率和法线缓冲区,并填充深度缓冲区。所有这些缓冲区实际上都是纹理。 现在我想从其他渲染通道访问深度缓冲区,从而访问其他帧缓冲区,而不改变深度纹理。我只想读取深度值。对于这些通道,我主要绘制全屏四边形,我不希望他们更新深度纹理并将其消隐为深度值0。 如何将一个给定的深度纹理附加到另一个帧缓冲区,并确保它是只写的?

  • 问题内容: 假设我想在syscall 上的Linux文件系统上写入1 GB的数据,这种情况发生在非常 繁忙的环境中 (许多相似的I / O并发)。什么是在区间的最佳缓冲区大小,说,这样做,当 不使用打开标志,或者 使用? 请没有“自己检查”的答案-我想从“文件系统”的家伙那里得到一些答案。 问题答案: 正如评论中所讨论的那样,我认为确切的大小并不重要,假设是: 文件系统大小的一小部分(请参阅Joa