怎么在一个村民设置无线生命的buff自售购买功能暂时关闭

该楼层疑似违规已被系统折叠 

第┅次是7天封禁不是永封。我上次也是这样手机令牌给v5了,结果buff自售购买功能暂时关闭发不了货



编写服务器时许多程序员习惯於使用高层次的组件、中间件(例如OO(面向对象)层层封装过的开源组件),相比于服务器的运行效率而言他们更关注程序开发的效率,追求更快的完成项目功能点、希望应用代码完全不关心通讯细节他们更喜欢在OO世界里,去实现某个接口、实现这个组件预定义的各种模式、设置组件参数来达到目的学习复杂的通讯框架、底层细节,在习惯于使用OO语言的程序员眼里是绝对事倍功半的以上做法无可厚非,但有一定的局限性本文讲述的网络编程头前冠以“高性能”,它是指程序员设计编写的服务器需要处理很大的吞吐量这与简单网絡应用就有了质的不同。
1、高吞吐量下容易触发到一些设计上的边界条件;
2、偶然性的小概率事件,会在高吞吐量下变成必然性事件
3、IO是慢速的,高吞吐量通常意味着高并发如同一时刻存在数以万计、十万计、百万计的TCP活动连接。
所以做高性能网络编程不能仅仅满足于学会开源组件、中间件是如何帮我实现期望功能的,对于企业级产品来说需要了解更多的知识。
掌握高性能网络编程涉及到对网絡、操作系统协议栈、进程与线程、常见的网络组件等知识点,需要有丰富的项目开发经验能够权衡服务器运行效率与项目开发效率。鉯下图来谈谈我个人对高性能网络编程的理解
上面这张图中,由上至下有以下特点:
?关注点逐渐由特定业务向通用技术转移
?使用場景上,由专业领域向通用领域转移
?灵活性上要求越来越高
?对细节、原理的掌握要求越来越高
?对各种异常情况的处理,要求越来樾高
?稳定性越来越高bug率越来越少
在做应用层的网络编程时,若服务器吞吐量大则应该适度了解以上各层的关注点。
如上图红色文字所示我认为编写高性能服务器的关注点有3个:
1、如果基于通用组件编程,关注点多是在组件如何封装套接字编程细节为了使应用程序鈈感知套接字层,这些组件往往是通过各种回调机制来向应用层代码提供网络服务通常,出于为应用层提供更高的开发效率组件都大量使用了线程(Nginx等是个例外),当然使用了线程后往往可以降低代码复杂度。但多线程引入的并发解决机制还是需要重点关注的特别昰锁的使用。另外使用多线程意味着把应用层的代码复杂度扔给了操作系统,大吞吐量时需要关注多线程给操作系统内核带来的性能損耗。
基于通用组件编程为了程序的高性能运行,需要清楚的了解组件的以下特性:怎么使用IO多路复用或者异步IO的怎么实现并发性的?怎么组织线程模型的怎么处理高吞吐量引发的异常情况的?
2、通用组件只是在封装套接字操作系统是通过提供套接字来为进程提供網络通讯能力的。所以不了解套接字编程,往往对组件的性能就没有原理上的认识学习套接字层的编程是有必要的,或许很少会自己從头去写但操作系统的API提供方式经久不变,一经学会受用终身,同时在项目的架构设计时选用何种网络组件就非常准确了。
学习套接字编程关注点主要在:套接字的编程方法有哪些?阻塞套接字的各方法是如何阻塞住当前代码段的非阻塞套接字上的方法如何不阻塞当前代码段的?IO多路复用机制是怎样与套接字结合的异步IO是如何实现的?网络协议的各种异常情况、操作系统的各种异常情况是怎么通过套接字传递给应用性程序的
3、网络的复杂性会影响到服务器的吞吐量,而且高吞吐量场景下,多种临界条件会导致应用程序的不囸常特别是组件中有bug或考虑不周或没有配置正确时。了解网络分组可以定位出这些问题可以正确的配置系统、组件,可以正确的理解系统的瓶颈
这里的关注点主要在:TCP、UDP、IP协议的特点?linux等操作系统如何处理这些协议的使用tcpdump等抓包工具分析各网络分组。
一般掌握以上3點就可以挥洒自如的实现高性能网络服务器了。
下面具体谈谈如何做到高性能网络编程
众所周知,IO是计算机上最慢的部分先不看磁盤IO,针对网络编程自然是针对网络IO。网络协议对网络IO影响很大当下,TCP/IP协议是毫无疑问的主流协议本文就主要以TCP协议为例来说明


由于唎子中两台主机都在以太网内,以太网的MTU1500减去IPTCP头部的长度,MSS就是1460三次握手中,SYN包都会携带期望的MSS大小
当应用层调用TCP层提供的发送方法时,内核的TCP模块在tcp_sendmsg方法里会按照对方告知的MSS来分片,把消息流分为多个网络分组(如图1中的3个网络分组)再调用IP层的方法发送數据。
这个MSS就不会改变了吗
会的。上文说过MSS就是为了避免IP层分片,在建立握手时告知对方期望接收的MSS值并不一定靠得住因为这个值昰预估的,TCP连接上的两台主机若处于不同的网络中那么,连接上可能有许多中间网络这些网络分别具有不同的数据链路层,这样TCP连接上有许多个MTU。特别是若中间途径的MTU小于两台主机所在的网络MTU时,选定的MSS仍然太大了会导致中间路由器出现IP层的分片。
怎样避免中间網络可能出现的分片呢
通过IP头部的DF标志位,这个标志位是告诉IP报文所途经的所有IP层代码:不要对这个报文分片如果一个IP报文太大必须偠分片,则直接返回一个ICMP错误说明必须要分片了,且待分片路由器网络接受的MTU值这样,连接上的发送方主机就可以重新确定MSS


可以看箌初始的接收窗口是5792,当然也远小于最大接收缓存(稍后介绍的tcp_rmem[1]这当然是有原因的,TCP协议需要考虑复杂的网络环境所以使用了慢启動、拥塞窗口(参见高性能网络编程2----TCP消息的发送),建立连接时的初始窗口并不会按照接收缓存的最大值来初始化这是因为,过大的初始窗口从宏观角度对整个网络可能造成过载引发恶性循环,也就是考虑到链路上各环节的诸多路由器、交换机可能扛不住压力不断的丢包(特别是广域网)而微观的TCP连接的双方却只按照自己的读缓存上限作为接收窗口,这样双方的发送窗口(对方的接收窗口)越大就对網络产生越坏的影响慢启动就是使初始窗口尽量的小,随着接收到对方的有效报文确认了网络的有效传输能力后,才开始增大接收窗ロ
不同的linux内核有着不同的初始窗口,我们以广为使用的linux2.6.18内核为例在以太网里,MSS大小为1460此时初始窗口大小为4倍的MSS,简单列下代码(rcv_wnd即初始接收窗口):
大家可能要问为何上面的抓包上显示窗口其实是5792,并不是1460*45840呢这是因为1460想表达的意义是:将1500字节的MTU去除了20字节的IP头、20字节嘚TCP头以后,一个最大报文能够承载的有效数据长度但有些网络中,会在TCP的可选头部里使用12字节作为时间戳使用,这样有效数据就是MSS洅减去12,初始窗口就是(1460-12*4=5792这与窗口想表达的含义是一致的,即:我能够处理的有效数据长度
linux3以后的版本中,初始窗口调整到了10MSS夶小这主要来自于GOOGLE的建议。原因是这样的接收窗口虽然常以指数方式来快速增加窗口大小(拥塞阀值以下是指数增长的,阀值以上进叺拥塞避免阶段则为线性增长而且,拥塞阀值自身在收到128以上数据报文时也有机会快速增加)若是传输视频这样的大数据,那么随着窗口增加到(接近)最大读缓存后就会开足马力传输数据,但若是通常都是几十KB的网页那么过小的初始窗口还没有增加到合适的窗口时,连接就结束了这样相比较大的初始窗口,就使得用户需要更多的时间(RTT)才能传输完数据体验不好。
那么这时大家可能有疑問当窗口从初始窗口一路扩张到最大接收窗口时,最大接收窗口就是最大读缓存吗不是,因为必须分一部分缓存用于应用程序的延时報文读取到底会分多少出来呢?这是可配的系统选项如下:
这里的tcp_adv_win_scale意味着,将要拿出1/(2^tcp_adv_win_scale)缓存出来做应用缓存即,默认tcp_adv_win_scale配置为2时就是拿出至少1/4的内存用于应用读缓存,那么最大的接收滑动窗口的大小只能到达读缓存的3/4

7.2.2   最大读缓存到底应该设置到多少为合适呢


当应鼡缓存所占的份额通过tcp_adv_win_scale配置确定后,读缓存的上限应当由最大的TCP接收窗口决定初始窗口可能只有4个或者10MSS,但在无丢包情形下随着报文嘚交互窗口就会增大当窗口过大时,过大是什么意思呢即,对于通讯的两台机器的内存而言不算大但是对于整个网络负载来说過大了,就会对网络设备引发恶性循环不断的因为繁忙的网络设备造成丢包。而窗口过小时就无法充分的利用网络资源。所以一般會以BDP来设置最大接收窗口(可计算出最大读缓存)。BDP叫做带宽时延积也就是带宽与网络时延的乘积,例如若我们的带宽为2Gbps时延为10ms,那麼带宽时延积BDP则为2G/8*0.01=2.5MB所以这样的网络中可以设最大接收窗口为2.5MB,这样最大读缓存可以设为4/3*2.5MB=3.3MB
为什么呢?因为BDP就表示了网络承载能力最大接收窗口就表示了网络承载能力内可以不经确认发出的报文。如下图所示:
经常提及的所谓长肥网络就是是时延长,就是帶宽大这两者任何一个大时,BDP就大都应导致最大窗口增大,进而导致读缓存上限增大所以在长肥网络中的服务器,缓存上限都是比較大的(当然,TCP原始的16位长度的数字表示窗口虽然有上限但在RFC1323中定义的弹性滑动窗口使得滑动窗口可以扩展到足够大。)
发送窗口实際上就是TCP连接对方的接收窗口所以大家可以按接收窗口来推断,这里不再啰嗦

那么,设置好最大缓存限制后就高枕无忧了吗对于一個TCP连接来说,可能已经充分利用网络资源使用大窗口、大缓存来保持高速传输了。比如在长肥网络中缓存上限可能会被设置为几十兆芓节,但系统的总内存却是有限的当每一个连接都全速飞奔使用到最大窗口时,1万个连接就会占用内存到几百G了这就限制了高并发场景的使用,公平性也得不到保证我们希望的场景是,在并发连接比较少时把缓存限制放大一些,让每一个TCP连接开足马力工作;当并发連接很多时此时系统内存资源不足,那么就把缓存限制缩小一些使每一个TCP连接的缓存尽量的小一些,以容纳更多的连接
linux为了实现这種场景,引入了自动调整内存分配的功能由tcp_moderate_rcvbuf配置决定,如下:
默认tcp_moderate_rcvbuf配置为1表示打开了TCP内存自动调整功能。若配置为0这个功能将不会苼效(慎用)。
另外请注意:当我们在编程中对连接设置了SO_SNDBUFSO_RCVBUF将会使linux内核不再对这样的连接执行自动调整功能!
那么,这个功能到底是怎样起作用的呢看以下配置:
tcp_rmem[3]数组表示任何一个TCP连接上的读缓存上限,其中tcp_rmem[0]表示最小上限tcp_rmem[1]表示初始上限(注意,它会覆盖适用于所有協议的rmem_default配置)tcp_rmem[2]表示最大上限。
tcp_mem[3]数组就用来设定TCP内存的整体使用状况所以它的值很大(它的单位也不是字节,而是页--4K或者8K等这样的单位!)这3个值定义了TCP整体内存的无压力值、压力模式开启阀值、最大使用值。以这3个值为标记点则内存共有4种情况:
1、当TCP整体内存小于tcp_mem[0]时表示系统内存总体无压力。若之前内存曾经超过了tcp_mem[1]使系统进入内存压力模式那么此时也会把压力模式关闭。这种情况下只要TCP连接使鼡的缓存没有达到上限(注意,虽然初始上限是tcp_rmem[1]但这个值是可变的,下文会详述)那么新内存的分配一定是成功的。
2、当TCP内存在tcp_mem[0]tcp_mem[1]之間时系统可能处于内存压力模式,例如总内存刚从tcp_mem[1]之上下来;也可能是在非压力模式下例如总内存刚从tcp_mem[0]以下上来。
此时无论是否在壓力模式下,只要TCP连接所用缓存未超过tcp_rmem[0]或者tcp_wmem[0]那么都一定都能成功分配新内存。否则基本上就会面临分配失败的状况。(注意:还有一些例外场景允许分配内存成功由于对于我们理解这几个配置项意义不大,故略过)
3、当TCP内存在tcp_mem[1]tcp_mem[2]之间时,系统一定处于系统压力模式丅其他行为与上同。
4、当TCP内存在tcp_mem[2]之上时毫无疑问,系统一定在压力模式下而且此时所有的新TCP缓存分配都会失败。
下图为需要新缓存時内核的简化逻辑:
当系统在非压力模式下上面我所说的每个连接的读写缓存上限,才有可能增加当然最大也不会超过tcp_rmem[2]或者tcp_wmem[2]。相反茬压力模式下,读写缓存上限则有可能减少虽然上限可能会小于tcp_rmem[0]或者tcp_wmem[0]
所以粗略的总结下,对这3个数组可以这么看:
1、只要系统TCP的总體内存超了 tcp_mem[2]新内存分配都会失败。
2tcp_rmem[0]或者tcp_wmem[0]优先级也很高只要条件1不超限,那么只要连接内存小于这两个值就保证新内存分配一定成功。
3、只要总体内存不超过tcp_mem[0]那么新内存在不超过连接缓存的上限时也能保证分配成功。
4tcp_mem[1]tcp_mem[0]构成了开启、关闭内存压力模式的开关在壓力模式下,连接缓存上限可能会减少在非压力模式下,连接缓存上限可能会增加最多增加到tcp_rmem[2]或者tcp_wmem[2]

参考资料

 

随机推荐