TCP性能的提升

TCP三次握手的性能提升

调整SYN报文重传次数

  • echo 5>/proc/sys/net/ipv4/tcp_syn_retries
  • 每次重传的时间点是上次重传的两倍

调整SYN半连接队列长度

  • 如何查看半连接队列是否已满 netstat- -s | grep “SYNs to LISTEN” ,这是一个被丢弃连接的累计值,如果在上升说明半连接队列已经满了
  • 要想增大半连接队列,不能只单纯增大 tcp_max_syn_backlog 的值,还需一同增大 somaxconn 和 backlog,也就是增大 accept 队列。否则,只单纯增大 tcp_max_syn_backlog 是无效的。
  • 其实可以使用syncookies可以绕开半连接队列,但是可能要遭受syn攻击,一般选用他的1参数“表示仅当 SYN 半连接队列放不下时,再启用它” echo 1> /proc/sys/net/ipv4/tcp_syncookies

调整SYN+ACK报文重传次数

  • 调整 tcp_synack_retries 参数,默认重传5次,echo 1> /proc/sys/net/ipv4/tcp_synack_retries

调整accept队列长度

  • accept满了默认行为为丢弃数据包,但是我们也可以选择像客服端发送RST复位报文 echo 1> /proc/sys/net/ipv4/tcp_abort_on_overflow .通常情况下,应当把 tcp_abort_on_overflow 设置为 0,因为这样更有利于应对突发流量,因为ack会重发。设置为1只是方便我们观察客服端情况判断是否是队列已满;

  • 如何调整长度呢? ==min(somaxconn, backlog)

    • somaxconn 是 Linux 内核的参数,默认值是 128,可以通过 net.core.somaxconn 来设置其值
    • backlog 是 listen(int sockfd, int backlog) 函数中的 backlog 大小;
  • 如何查看长度呢?

    • ss -ltn进行查看

      • -l 代表listen,-n 不解析服务器 ,-t 只显示tcp
  • 如何查看由于 accept 连接队列已满,而被丢弃的连接?

    • netstat- -s | grep overflowed ,同上面查看半连接队列

绕过三次握手

  • TCP fast open技术,主要的依据就是cookie保存信息,以便下次快速发送;
  • 怎样打开呢? echo 3> /proc/sys/net/ipv4/tcp_fastopen ,3代表无论客服端和服务端都可以使用FAST OPEN功能。感兴趣去看看参数1,2有啥作用;

TCP四次挥手的性能提升

明白shutdown和colse的区别

  • close 函数意味着完全断开连接,完全断开不仅指无法传输数据,而且也不能发送数据。此时,调用了 close 函数的一方的连接叫做「孤儿连接」,如果你用 netstat -p 命令,会发现连接对应的进程名为空。

  • 而shutdown ,它可以控制只关闭一个方向的连接。它有以下参数:

    • SHUT_RD(0) 关闭读方向。如果接收缓冲区有已接收的数据,则将会被丢弃,并且后续再收到新的数据,会对数据进行 ACK,然后悄悄地丢弃。也就是说,对端还是会接收到 ACK,在这种情况下根本不知道数据已经被丢弃了。
    • SHUT_WR(1):关闭连接的「写」这个方向,这就是常被称为「半关闭」的连接。如果发送缓冲区还有未发送的数据,将被立即发送出去,并发送一个 FIN 报文给对端。
    • SHUT_RDWR(2):相当于 SHUT_RD 和 SHUT_WR 操作各一次,关闭套接字的读和写两个方向。

主动方的优化

  • FIN_WAIT1 的优化

    • 调整FIN报文重传次数

      • echo 5> /proc/sys/net/ipv4/tcp_orphan_retries 默认为8次
    • 调整孤儿链接的上限个数

      • 如果遇见恶意攻击,当我们调用close之后,FIN报文可能没有办法发出去(发动缓冲区还有数据or 接收窗口为0)
      • 那么就可以调整孤儿连接的最大数tcp_max_orphans
      • Linux 系统为了防止孤儿连接过多,导致系统资源长时间被占用,就提供了 tcp_max_orphans 参数。如果孤儿连接数量大于它,新增的孤儿连接将不再走四次挥手,而是直接发送 RST 复位报文强制关闭。
  • FIN_WAIT2 的优化

    • 调整FIN_WAIT2状态的时间

      • 如果连接是用 shutdown 函数关闭的,连接可以一直处于 FIN_WAIT2 状态
      • 对于 close 函数关闭的孤儿连接,由于无法在发送和接收数据,所以这个状态不可以持续太久,而 tcp_fin_timeout 控制了这个状态下连接的持续时长,默认值是 60 秒(2MSL)
      • tcp_fin_timeout
  • TIME_WAIT的优化

    • 调整time_wait状态是上限次数

      • time_wait的作用

        • 防止具有相同「四元组」的「旧」数据包被收到;(简单来说就是放置A给B的数据被接替C的客服端收了)
        • 保证「被动关闭连接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭
      • 为什么是2MSL?,maximum segment lifetime,定义了一个在网络中的生存时间 ;而2MSL是至少允许报文丢失一次(一个MSL为30s)

      • 为什么不是8MSL?

        • 因为连续两次丢包的概率太小了,比如一个丢包率达到百分之一的糟糕网络,连续两次丢包的概率只有万分之一
      • time_wait太多怎么办呢?

        • 虽然 TIME_WAIT 状态有存在的必要,但它毕竟会消耗系统资源。如果发起连接一方的 TIME_WAIT 状态过多,占满了所有端口资源,则会导致无法创建新连接。
        • 客户端受端口资源限制:如果客户端 TIME_WAIT 过多,就会导致端口资源被占用,因为端口就65536个,被占满就会导致无法创建新的连接
        • 服务端受系统资源限制:由于一个 四元组表示TCP连接,理论上服务端可以建立很多连接,服务端确实只监听一个端口 但是会把连接扔给处理线程,所以理论上监听的端口可以继续监听。但是线程池处理不了那么多一直不断的连接了。所以当服务端出现大量 TIME_WAIT 时,系统资源被占满时,会导致处理不过来新的连接;
      • 如何改?

        • echo 5000 > /proc/sys/net/ipv4/tcp_max_tw_buckets,当服务器并发数增多的时候,可以适当的增大这个值,但是也不是越大越好,因为内存和端口都是有限的;
    • 复用time_wait状态的连接

      • 有一种方式可以在建立新连接时,复用处于 TIME_WAIT 状态的连接,那就是打开 tcp_tw_reuse 参数。但是需要注意,该参数是只用于客户端(建立连接的发起方),因为是在调用 connect() 时起作用的,而对于服务端(被动连接方)是没有用的。

        • 其是协议角度安全可控的

          • 只适用于连接发起方,也就是 C/S 模型中的客户端
          • 对应的 TIME_WAIT 状态的连接创建时间超过 1 秒才可以被复用。
        • 使用前提还有双方都打开TCP时间戳

          • echo 1 >/proc/sys/net/ipv4/tcp_timestamps

          • 引入时间戳带来的好处

            • 我们在前面提到的 2MSL 问题就不复存在了,因为重复的数据包会因为时间戳过期被自然丢弃;
            • 同时,它还可以防止序列号绕回,也是因为重复的数据包会由于时间戳过期被自然丢弃;

被动方的优化

  • 大多数应用程序并不使用 shutdown 函数关闭连接。所以,当你用 netstat 命令发现大量 CLOSE_WAIT 状态。就需要排查你的应用程序,因为可能因为应用程序出现了 Bug,read 函数返回 0 时,没有调用 close 函数。
  • colse_wait状态重传FIN也是根据tcp_orphan_retries参数来进行设置的
  • 如果被动方迅速调用 close 函数,那么被动方的 ACK 和 FIN 有可能在一个报文中发送,这样看起来,四次挥手会变成三次挥手,这只是一种特殊情况,不用在意。(注意哦,是迅速)
  • 两方发送 FIN 报文时,都认为自己是主动方,所以都进入了 FIN_WAIT1 状态,FIN 报文的重发次数仍由 tcp_orphan_retries 参数控制。接下来,双方在等待 ACK 报文的过程中,都等来了 FIN 报文。这是一种新情况,所以连接会进入一种叫做 CLOSING 的新状态,它替代了 FIN_WAIT2 状态。接着,双方内核回复 ACK 确认对方发送通道的关闭后,进入 TIME_WAIT 状态,等待 2MSL 的时间后,连接自动关闭。

TCP 数据传输的性能提升

TCP连接

  • TCP连接时是由内核维护的,内核会为每个连接建立内存缓冲区

    • 如果连接的内存配置过小,就无法充分利用网络带宽,TCP传输效率就会降低
    • 如果连接的内存配置过大,很容易间服务器是资源耗光,这样就会导致新的来凝结无法建立

滑动窗口是怎样影响传输速度的?

  • TCP因为有超时重传机制,所以TCP报文发出去了之后,并不会立即从内存中删除,因为重传的时候还会利用到它;

  • 通过TCP报文格式,我们知道窗口大小为16位,也就是能够表达64KB的大小,而这个在当今的网速下明显是不够用的,所以在 TCP 选项字段定义了窗口扩大因子,用于扩大TCP通告窗口,使 TCP 的窗口大小从 2 个字节(16 位) 扩大为 30 位,所以此时窗口的最大值可以达到 1GB(2^30)

  • 扩大方式: ehco 1> /proc/sys/net/ipv4/tcp_window_scaling ,默认就是打开的

  • 那么是不是滑动窗口可以无限的扩大?

    • 这是不可能的,因为网络的传输能力是有限的,当发送方依据发送窗口,发送超过网络处理能力的报文时,路由器会直接丢弃这些报文。因此,缓冲区的内存并不是越大越好。
    • 内核缓冲区决定了滑动窗口的上限,缓冲区可分为:发送缓冲区 tcp_wmem 和接收缓冲区 tcp_rmem。
  • 所以TCP 的传输速度,受制于发送窗口与接收窗口,以及网络设备传输能力

如何确定最大的传输速度?

  • 首先理解:网络时延积=RTT*带宽,它决定网络中飞行报文的大小

    • 比如最大带宽是 100 MB/s,网络时延(RTT)是 10ms 时,意味着客户端到服务端的网络一共可以存放 100MB/s * 0.01s = 1MB 的字节。
  • 由于发送缓冲区大小决定了发送窗口的上限,而发送窗口又决定了「已发送未确认」的飞行报文的上限。因此,发送缓冲区不能超过「带宽时延积」。

  • 怎样调整发送缓冲区?

    • echo “4096 16384 4194304” >/proc/sys/net/ipv4/tcp_wmem 自动开启的
    • 第一个是动态范围的最小值(byte),而二个数值位初始化值,第三个为动态范围的最大值;
    • 发送缓冲区是自行调节的,当发送方发送的数据被确认后,并且没有新的数据要发送,就会把发送缓冲区的内存释放掉
  • 怎样调整接受缓冲区?

    • echo “4096 16384 4194304” >/proc/sys/net/ipv4/tcp_rmem
    • 如果空闲内存(tcp_mem)很多,就可以自动把缓冲区增大一些,这样传给对方的接收窗口也会变大,因而提升发送方发送的传输数据数量;
    • 反正,如果内存(tcp_mem)很紧张,就会减少缓冲区,这虽然会降低传输效率,可以保证更多的并发连接正常工作;
    • 接受缓冲区开启需要配置:配置 tcp_moderate_rcvbuf 为 1 来开启调节功能
  • 怎么调节TCP内存范围?

    • echo “88560 118080 177120” > /proc/sys/net/ipv4/tcp_mem

    • 上面三个数字单位不是字节,而是「页面大小」,1 页表示 4KB

      • 当 TCP 内存小于第 1 个值时,不需要进行自动调节;
      • 在第 1 和第 2 个值之间时,内核开始调节接收缓冲区的大小;
      • 大于第 3 个值时,内核不再为 TCP 分配新内存,此时新连接是无法建立的;
  • 高并发服务器

    • 在高并发服务器中,为了兼顾网速与大量的并发连接,我们应当保证接收缓冲区的动态调整的最大值达到带宽时延积,而接收缓冲区最小值保持默认的 4K 不变即可。而对于内存紧张的服务而言,调低默认值是提高并发的有效手段。
    • 如果这是网络 IO 型服务器,那么,调大 tcp_mem 的上限可以让 TCP 连接使用更多的系统内存,这有利于提升并发能力。需要注意的是,tcp_wmem 和 tcp_rmem 的单位是字节,而 tcp_mem 的单位是页面大小。而且,千万不要在 socket 上直接设置 SO_SNDBUF 或者 SO_RCVBUF,这样会关闭缓冲区的动态调整功能。

    以上内容总结于微信公众号:小林coding,一个很优秀的coder和订阅号up主;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值