都说web2.0的魅力在于从静态资源到交互资源的转变,而web3.0的魅力在于其去中心化的资源,人人都可以参与其中享受收益时代。但是,无论上层的概念多么花哨,最底层的通信仍然是基于web1.0形成的技术。我们的最终目标其实是在去中心化的名义下实现真正的中心化。当流量增加到一定程度,网络编程就会出现各种奇葩的场景。下面将通过十几个实际案例来说明xjjdog在工作中经常遇到的与网络相关的高频问题,希望对大家有所帮助。1.大量客户端在线时注意避免。不管你的服务器有多强大,当大量的连接到来并执行业务服务时,瞬间就会出现问题。例如,如果您的MQTT服务器连接到数十万台设备。当你的MQTT服务器宕机重启时,它要承受几十万的并发量,这几乎是任何服务都经不起的。在xjjdog以往的经历中,因服务器重启问题导致的阻塞事故不计其数。这个场景其实和缓存击穿的概念非常相似。当缓存中的热点数据集中失效时,所有的请求都会渗透到数据库层面,造成问题。如上图所示,解决缓存击穿问题就是给每个key加上一个随机值的过期时间,让它们不会同时过期。同样,我们可以在客户端重新连接到服务器时添加一个随机时间。随机数是个好东西,它可以让我们的海量连接在一个随机的时间窗口内保持线性增长。2、多网卡队列openstack等虚拟平台上假想的虚拟机,经常会因为网卡能力弱导致流量达到一定程度后服务卡死。这是因为单个CPU在处理中断时会造成瓶颈。通过dstat或iftop命令,可以看到当前的网络流量。比如新的Kafka机器上线后,会进行大规模的数据拷贝。这时候你去ping一下相关的机器,你会发现ping值变的很大了。同时,Recv-Q和Send-Q的值也会增加。这时候就需要开启网卡的多队列模式。使用ethtool查看网卡的队列信息。ethtool-leth0|grep'Combined'Combined:1当然可以通过以下命令增加网卡的队列。ethtool-Leth0combined2建议同时开启中断均衡服务。systemctlstartirqbalance3。不时切断长连接。如果客户端和服务端连接,并且保持连接,对方没有关闭,那么就是长连接。长连接可以避免频繁创建连接的开销。从HTTP1到HTTP2再到HTTP3,一直致力于减少连接和重用连接。通常,长连接是首选。但是在一些特殊情况下,我们希望长连接不要一直保持在那里,需要给它加上TTL。这种情况通常发生在负载均衡场景中。比如LVS、HAProxy等,如果后端有A、B、C三台机器,在LVS加载后,90个连接分配给这三台机器。但是在某个时刻,A宕机了,它持有的30个连接会重新加载给B和C,此时它们都持有45个连接。当A重新启动时,它无法再获得新的连接。如果LVS操作rebalance一次,影响会比较大。因此,我们希望创建的长连接能够具有lifetime属性,在一定的时间间隔内实现渐进的再平衡。4.k8s端口范围是为了避免k8s和其他程序冲突。默认端口范围是30000-32767。如果你使用的是k8s平台,配置了nodeport却无法访问,注意是不是设置的端口号太小了。5.TIME_WAITTIME_WAIT是主动关闭连接的一方维护的状态。像nginx和爬虫服务器,经常会出现大量time_wait状态的连接。TCP一般在主动关闭连接后等待2MS,然后彻底关闭连接。由于HTTP使用的是TCP协议,在这些频繁切换连接的服务器上积压了很多TIME_WAIT状态的连接。有些系统可以通过dmesg看到如下信息。__ratelimit:2170callbackssuppressedTCP:timewaitbuckettableoverflowTCP:timewaitbuckettableoverflowTCP:timewaitbuckettableoverflowTCP:timewaitbuckettableoverflowsysctl命令可以设置这些参数。如果要重启生效,在/etc/sysctl.conf文件中添加即可。#修改阈值net.ipv4.tcp_max_tw_buckets=50000#表示开启TCP连接中TIME-WAITsockets的快速回收net.ipv4.tcp_tw_reuse=1#开启timewait快速回收。这必须启用,默认情况下禁用。net.ipv4.tcp_tw_recycle=1#修改系统默认TIMEOUT时间,默认60s。net.ipv4.tcp_fin_timeout=10测试参数,可以使用命令sysctl-wnet.ipv4.tcp_tw_reuse=1,如果写入文件,使用sysctl-p生效。6、CLOSE_WAITCLOSE_WAIT一般是peer主动关闭导致的,但是我们没有正确处理。说白了就是程序写的有问题,属于比较害人的类型。大家都知道TCP连接是三次握手四次挥手,因为TCP连接允许单向关闭。如图所示,当一个连接被主动关闭时,会进入fin_wait_1状态。同时,收到fin报文的被动关闭方进入close_wait状态,主动关闭方回复ack后进入fin_wait_2状态。这是一种单向闭包。此时如果被动关闭方因为某种原因没有给主动关闭方发送fin消息,就会一直处于close_wait状态。例如,收到EOF但没有启动关闭操作。显然,这主要是一个编程错误,只能通过代码审查来解决。7.可由进程打开的网络连接。即使释放了一个端口,Linux也可以接受大量的连接。这些连接的上限受限于单进程文件句柄数和操作系统文件句柄数,即ulimit和file-max。为了能够持久化参数更改,我们倾向于将更改写入文件。进程的文件句柄限制可以放在/etc/security/limits.conf中,其上限由fs.nr_open限制;操作系统的文件句柄限制可以放在/etc/sysctl.conf文件中。最后别忘了在/proc/$id/limits文件中确认修改是否对进程生效。/etc/security/limits.conf配置示例:rootsoftnofile1000000roothardnofile1000000*softnofile1000000*hardnofile1000000es-nofile655358。SO_KEEPALIVE如果启用该Socket选项,客户端Socket将使用空闲连接每隔一段时间(大约两小时)向服务器发送一个数据包。这个数据包没有其他目的,只是为了检查服务器是否仍然处于活动状态。如果服务器没有响应这个数据包,客户端Socket会在大约11分钟后发送另一个数据包。如果服务器在12分钟内没有响应,客户端Socket将被关闭。如果关闭Socket选项,当服务器无效时,客户端Socket可能会长时间不关闭。9、SO_REUSEADDR解决什么问题?我们在进行网络开发时,经常会遇到地址已被使用的异常。这是由于应用关闭时对应端口的网络连接处于TIME_WAIT状态造成的。TIME_WAIT状态通常会持续一段时间(2ML)。设置SO_REUSEADDR可以支持快速端口重用和应用程序的快速重启。10、应用心跳tcp本身的keepalived机制,对于健康检查来说是很鸡肋的。它安静地运行在底层,不能产生应用层的语义。在我们的想象中,连接应该是一条线。但实际上只是2个点,而且每次的路径可能都不一样。一点,需要发送心跳包,收到回复,才能知道对方是否活着。tcp自带的心跳机制只能知道对方是否存活,而不知道服务是否可用或健康状态,而且超时配置经常和超时重传机制冲突。因此,需要一个具有明确含义的应用层心跳。11.SO_LINGERSocket选项可以影响关闭方法的行为。默认情况下,当close方法被调用时,会立即返回;如果此时还有未发送的数据包,这些数据包将被丢弃。如果linger参数设置为正整数n(n的最大值为65、535),调用close方法后最多阻塞n秒。在这n秒内,系统会尝试发送未发送的数据包;如果超过n秒,如果有未发送的数据包,这些数据包将被丢弃;close方法将立即返回。如果linger设置为0,则与关闭SO_LINGER选项的效果相同。12.SO_TIMEOUT可以使用这个选项来设置读取数据的超时时间。当输入流的read方法被阻塞时,如果设置了timeout(超时的单位是毫秒),那么系统会在等待timeout毫秒后抛出InterruptedIOException异常。抛出异常后,输入流并没有关闭,可以通过read方法继续读取数据。13.SO_SNDBUF、SO_RCVBUF默认情况下,输出流的发送缓冲区为8096字节(8K)。该值是Java推荐的输出缓冲区大小。如果这个默认值不满足要求,可以使用setSendBufferSize方法重新设置缓冲区的大小。但最好不要将输出缓冲区设置得太小,否则会导致数据传输过于频繁,从而降低网络传输效率。14.SO_OOBINLINE如果开启这个Socket选项,可以通过Socket类的sendUrgentData方法向服务器发送一个单字节数据。此单字节数据不通过输出缓冲区,而是立即发送。虽然客户端没有使用OutputStream向服务器端发送数据,但是这个单字节数据在服务器程序中是和其他普通数据混合在一起的。因此服务器程序并不知道客户端发送的数据是OutputStream发送的还是sendUrgentData发送的。最后我很惊讶的发现有些网络环境还是千兆网卡,包括一些比较专业的测试环境。在对这些环境进行实际压力测试时,当流量超过网卡的限制时,应用响应会变得异常缓慢。计算机系统是一个整体,CPU、内存、网络、IO,任何一个环节出现瓶颈都会出现问题。网络是分布式系统中一个非常重要的因素。但是因为它比较底层,所以大部分开发者都不是很了解。再加上各种云原生组件的流行,访问这些底层设施的机会越来越少。但如果系统真的出现了问题,在排除了其他可能最有可能出现问题的组件之后,别忘了——还有一团糟的网络在等着你。作者简介:品味小姐姐(xjjdog),一个不允许程序员走弯路的公众号。专注于基础架构和Linux。十年架构,每天百亿流量,与你探讨高并发世界,给你不一样的滋味。
