V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Moonkin
V2EX  ›  问与答

高并发 TCP 的 time_wait 问题。为什么服务器资源会被占满

  •  2
     
  •   Moonkin · 2020-06-10 09:36:51 +08:00 · 3978 次点击
    这是一个创建于 1659 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一个端口可以建立 2^16 个 TCP 链接吧,为什么讲解 time_wait 的博客大多都是说服务器端口被占满,难道一般情况下设计的服务器是为每一个 TCP 链接单独占用一个端口吗?

    所有空闲端口*每个端口 2^16 个链接,差不多可以有 2^30 个链接,这样的话,不会发生 time_wait 过多的问题吧?

    所以 time_wait 到底是占用了什么导致后续客户端访问被拒绝的?

    第 1 条附言  ·  2020-06-10 10:41:26 +08:00
    EDIT:比如这个面试题,“问若客户端和服务器之间,1s 会发生 5000 到 6000 次短链接,会发生什么问题”,如果只有持续了一两秒,是不是没什么问题?如果几分钟时间内每秒都是这么多链接,是不是才会有 fd 资源占用过多的问题?
    27 条回复    2020-06-13 19:25:58 +08:00
    soulzz
        1
    soulzz  
       2020-06-10 10:02:05 +08:00
    他们可能不知道 nio
    time_wait 基本都是卡在数据库那里了
    如果请求方做了超时处理,这时候就成了 close_wait 了
    zjj19950716
        2
    zjj19950716  
       2020-06-10 10:02:59 +08:00 via iPhone
    cpu fd 内核参数 ?
    hopingtop
        3
    hopingtop  
       2020-06-10 10:16:33 +08:00
    如果服务是短链接,例如 HTTP 这种,高并发情况下,会创建很多新的 TCP 新链接。当服务器主动发起 FIN 包关闭链接时,会出现 2 个 MSL 的等待时间(此时的 fd 资源是没有释放的),这都是为了安全关闭 TCP 链接所做的机制。可以修改系统参数,适当的缓解 time_wait 这种情况。
    fxxkgw
        4
    fxxkgw  
       2020-06-10 10:19:42 +08:00
    我记得看过的 time_wait 都是基本上说是占用系统资源,没有特别强调说把端口占满这种问题。。。正常端口范围是 1024-65535 之间

    不过 time_wait 状态最好的办法就是扩容机器,盲目把 tcp_tw_recycle=1,在 NAT 环境下因为服务器间系统时间不一致,导致包时间戳不一致而产生丢包这种问题能折腾死人。
    rrfeng
        5
    rrfeng  
       2020-06-10 10:22:26 +08:00
    fd 也是一种资源,一般是受限的( ulimit )
    @fxxkgw
    Moonkin
        6
    Moonkin  
    OP
       2020-06-10 10:45:47 +08:00
    @soulzz nio 对 time_wait 过多的问题有哪方面帮助啊? nio 是高并发的时候不需要每个为 socket new 一个处理线程吧。。
    Moonkin
        7
    Moonkin  
    OP
       2020-06-10 10:48:04 +08:00
    @zjj19950716
    @hopingtop
    @fxxkgw
    @rrfeng
    我看这个 StackOverflow 上面的回答也是说 TCP 链接数目没有限制,是 fd 限制。。。可能我之前看的博客比较水吧。。。
    https://stackoverflow.com/questions/2332741/what-is-the-theoretical-maximum-number-of-open-tcp-connections-that-a-modern-lin
    liuminghao233
        8
    liuminghao233  
       2020-06-10 10:57:46 +08:00 via iPhone
    你要是没用 reuseaddr
    timewait 的时候用那个端口是不可用的
    如果你几秒钟内把 2^16 个端口都用了
    timewait 还没到时间
    那么就没有端口用了
    Moonkin
        9
    Moonkin  
    OP
       2020-06-10 11:05:46 +08:00
    @liuminghao233 也就是说,没有 reuseaddr 的话,每个端口只能有一个 TCP 链接吗?
    raynor2011
        10
    raynor2011  
       2020-06-10 11:07:36 +08:00
    是发起短连接的客户端会有 time_wait 占用端口问题, 服务器不会有这个问题
    SAIKAII
        11
    SAIKAII  
       2020-06-10 11:17:28 +08:00 via iPhone
    1. 首先,每次来一个连接都对应一个套接字,套接字由(local_ip, local_port, remote_ip, remote_port)标志,然后每个新的连接套接字的本地 ip 和 port 不变,变的是 remote 的 ip 和 port 。
    2,每个套接字对应一个文件描述符 fd,fd 用完就无法再创建了。wait_time 导致的资源耗尽是 fd 的耗尽,这个资源大小可以修改。
    misaka19000
        12
    misaka19000  
       2020-06-10 11:17:28 +08:00
    time_wait 一般是短连接太多导致的吧,解决办法是尽量复用 TCP 连接
    misaka19000
        13
    misaka19000  
       2020-06-10 11:20:49 +08:00
    楼上的一些文字真是让我很诧异,time_wati 和 nio 没关系,和 fd 也没关系

    我估计楼主想问的是另一个问题:一个端口可以对应多个 TCP 链接吗?
    答案是可以的,因为 client_host:client_port -- server_host:server_port 为一个连接,可见服务器端是可以维护多个连接的,一个连接会占用一个 fd,fd 由操作系统限制并且可以修改
    sagaxu
        14
    sagaxu  
       2020-06-10 11:32:14 +08:00 via Android
    time_wait 的时候,fd 已经 close 掉了,怎么个占用法呢?
    Moonkin
        15
    Moonkin  
    OP
       2020-06-10 11:53:11 +08:00
    @misaka19000 那 time_wait 的危害是什么呢?为什么要减少 time_wait 。。。
    Moonkin
        16
    Moonkin  
    OP
       2020-06-10 11:56:24 +08:00
    @raynor2011 也就是说客户端一个端口只能发起一个 tcp 链接吗?我看 StackOverflow 说“一个端口可以有 64K 个 tcp 链接”,这是说端口在服务器被监听的情况下才可以吗?发起方不能发起 64K 个?
    gravitybox
        17
    gravitybox  
       2020-06-10 12:21:37 +08:00
    @Moonkin time_wait 会占着端口不放,如果有大量的连接处于 time_wait,那么可能后续无法分配端口了。
    misaka19000
        18
    misaka19000  
       2020-06-10 13:11:32 +08:00
    @Moonkin #14 一个 time_wait 代表了一个未被释放的 TCP 连接,总归是会占用一些内存的,如果出现了大量的 time_wait 需要考虑是不是系统被攻击了或者有什么可以优化的地方
    Moonkin
        19
    Moonkin  
    OP
       2020-06-10 16:32:22 +08:00
    @gravitybox 只有一个服务器而且只有一个客户端 才会出现端口占满的情况吧,因为这种情况只能客户端改变端口标识唯一 tcp
    liuminghao233
        20
    liuminghao233  
       2020-06-10 16:56:52 +08:00 via iPhone
    @Moonkin
    假设你有个 tcp
    10.10.10.10:6666—远程 8.8.8.8:80 timewait 了
    你就暂时不能用
    [10.10.10.10:6666—8.8.8.8:80]
    但是有 reuseport 的话
    虽然[10.10.10.10:6666—8.8.8.8:80]还是用不了
    但是你可以用[10.10.10.10:6666—8.8.8.8:83]
    或者是[10.10.10.10:6666—8.8.8.2:80]
    liuminghao233
        21
    liuminghao233  
       2020-06-10 16:58:37 +08:00 via iPhone
    打错了 是 reuseaddr
    Moonkin
        22
    Moonkin  
    OP
       2020-06-10 17:21:28 +08:00
    @liuminghao233 #20 嗯,懂啦,谢谢大佬。查了查 so_reuseaddr 和 so_reuseport,感觉我这个问题还是得事先说清楚是不是 reuse
    https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ
    akira
        23
    akira  
       2020-06-10 18:11:33 +08:00
    @fxxkgw 对的,之前我们有遇到过这个情况,他们查了一个多月都没查到问题
    catror
        24
    catror  
       2020-06-10 18:20:58 +08:00 via Android
    time_wait 是客户端的状态……占用的我是客户端的资源
    julyclyde
        25
    julyclyde  
       2020-06-11 10:36:08 +08:00
    “讲解 time_wait 的博客大多都是说服务器端口被占满”的主要原因是文盲
    wanguorui123
        26
    wanguorui123  
       2020-06-12 08:48:40 +08:00 via iPhone
    修改 Linux 的参数,time_wait 都杀掉
    gravitybox
        27
    gravitybox  
       2020-06-13 19:25:58 +08:00
    “一个端口可以建立 2^16 个 TCP 链接吧”
    答:一个端口可以建立不只 2^16 个 TCP 连接。标识一个 tcp 套接字是四元组,你现在只限定了源 ip 和源端口。
    你这里的语境应该是:
    1 、有一组固定的客户端 A 和服务端 B,即源 ip 和目的 ip 是固定的
    2 、有服务端端口 6379,例如 B 上运行着 Redis 服务,绑定端口 6379

    那么客户端 A 最多能和服务端 B 同时建立 65535-1024 个 tcp 连接。


    “为什么讲解 time_wait 的博客大多都是说服务器端口被占满”
    答:time_wait 是最先发 FIN 包的那一端才会有的状态,一般服务端不会主动断连,大多是客户端主动挥手,断开连接。


    “难道一般情况下设计的服务器是为每一个 TCP 链接单独占用一个端口吗?”
    答:服务端不会为每个 tcp 连接再分配端口。服务端为每个新的 tcp 连接会创建一个套接字,tcp 套接字是以四元组标识的,新的 tcp 连接,即对应不同的客户端 ip 或客户端端口。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3024 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 14:33 · PVG 22:33 · LAX 06:33 · JFK 09:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.