Netty的TCP_NODELAY选项
来自社群小伙伴的交流
总目录 博客园版 为您奉上更多の珍贵的学习资源
有关本文的 脚本 和 代码,可以来 尼恩 发起的Java 高并发 疯狂创客圈 社群 交流和获取。
Netty的TCP选项的配置
DefaultSocketChannelConfig
@SuppressWarnings("unchecked")
@Override
public <T> T getOption(ChannelOption<T> option) {
if (option == SO_RCVBUF) {
return (T) Integer.valueOf(getReceiveBufferSize());
}
if (option == SO_SNDBUF) {
return (T) Integer.valueOf(getSendBufferSize());
}
if (option == TCP_NODELAY) {
return (T) Boolean.valueOf(isTcpNoDelay());
}
if (option == SO_KEEPALIVE) {
return (T) Boolean.valueOf(isKeepAlive());
}
if (option == SO_REUSEADDR) {
return (T) Boolean.valueOf(isReuseAddress());
}
if (option == SO_LINGER) {
return (T) Integer.valueOf(getSoLinger());
}
if (option == IP_TOS) {
return (T) Integer.valueOf(getTrafficClass());
}
if (option == ALLOW_HALF_CLOSURE) {
return (T) Boolean.valueOf(isAllowHalfClosure());
}
return super.getOption(option);
}
@Override
public <T> boolean setOption(ChannelOption<T> option, T value) {
validate(option, value);
if (option == SO_RCVBUF) {
setReceiveBufferSize((Integer) value);
} else if (option == SO_SNDBUF) {
setSendBufferSize((Integer) value);
} else if (option == TCP_NODELAY) {
setTcpNoDelay((Boolean) value);
} else if (option == SO_KEEPALIVE) {
setKeepAlive((Boolean) value);
} else if (option == SO_REUSEADDR) {
setReuseAddress((Boolean) value);
} else if (option == SO_LINGER) {
setSoLinger((Integer) value);
} else if (option == IP_TOS) {
setTrafficClass((Integer) value);
} else if (option == ALLOW_HALF_CLOSURE) {
setAllowHalfClosure((Boolean) value);
} else {
return super.setOption(option, value);
}
return true;
}
Netty的TCP选项
关于 本文的技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,
Option.TCP_NODELAY
TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。
为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。
TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,
同时,对方接收到数据,也需要发送ACK表示确认。
为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。(一个连接会设置MSS参数,因此,TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据)。
MTU:一个网络包的最大长度,以太网中一般为 1500 字节;
MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度; 最大 1460
Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。
Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。
所谓“小段”,指的是小于MSS尺寸的数据块,
所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。
举个例子,比如之前的blog中的实验,
一开始client端调用socket的write操作将一个int型数据(称为A块)写入到网络中,由于此时连接是空闲的(也就是说还没有未被确认的小段),因此这个int型数据会被马上发送到server端,
接着,client端又调用write操作写入‘/r/n’(简称B块),这个时候,A块的ACK没有返回,
所以可以认为已经存在了一个未被确认的小段,所以B块没有立即被发送,一直等待A块的ACK收到(大概40ms之后),B块才被发送。
整个过程如图所示:
这里还隐藏了一个问题,就是A块数据的ACK为什么40ms之后才收到?
这是因为TCP/IP中不仅仅有nagle算法,还有一个 ACK延迟机制 。
当Server端收到数据之后,它并不会马上向client端发送ACK,而是会将ACK的发送延迟一段时间(假设为t),
它希望在t时间内server端会向client端发送应答数据,这样ACK就能够和应答数据一起发送,就像是应答数据捎带着ACK过去。
在我之前的时间中,t大概就是40ms。这就解释了为什么’/r/n’(B块)总是在A块之后40ms才发出。
如果你觉着nagle算法太捣乱了,那么可以通过设置TCP_NODELAY将其禁用 。
当然,更合理的方案还是应该使用一次大数据的写操作,而不是多次小数据的写操作。
关于 本文的技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,
Option.SO_RCVBUF
Option(Option.SO_RCVBUF, XX),
recv_buf 不会预先分配内存, 只是个接收缓冲区size的最大限制,
不建议设置rcv_buf, linux内核会对每一个连接做动态的 调整, 一般情况下足够智能, 如果设置死了, 就失去了这个特性, 尤其是大量长连接的应用,
Option.SO_SNDBUF
Option(Option.SO_SNDBUF, XX),
send_buf 不会预先分配内存, 只是个发送缓冲区size的最大限制,
不建议设置send_buf , linux内核会对每一个连接做动态的 调整, 一般情况下足够智能, 如果设置死了, 就失去了这个特性, 尤其是大量长连接的应用,
Option.RCVBUF_ALLOCATOR
不是TCP协议选项
关于 本文的技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,
参考文献
https://blog.csdn.net/qq_22310551/article/details/124431034
https://blog.csdn.net/u013887008/article/details/104361380
https://article.itxueyuan.com/e196bP
https://blog.csdn.net/xiaolifeidaofirst/article/details/125218511
https://blog.csdn.net/small_engineer/article/details/124190620
https://learnku.com/articles/47193
https://www.cnblogs.com/xiaolincoding/p/12995358.html
https://blog.csdn.net/sinat_20184565/article/details/104828782
https://www.cnblogs.com/qhdziyan/p/16044974.html
http://blog.csdn.net/historyasamirror/article/details/6423235