这篇文章是一个掠影,关于在负载增加时,你的精雕细琢的程序可能会遇到的所有坏事情:所有会突然导致损失的地狱。当然你可以扩展或扩大,但是你也可以选择更好的编程。使你的系统能处理更大的负载。这能节约经费,因为只需要更少的容器,而且这会使整个应用更加可靠并具有更好的响应时间。同时这对程序员也是一个极大的满足。
当对象的数量变多以后,我们通常会遇到伸缩性问题。随着对象数量增长,对所有类型资源的清晰的使用被强调突出出来。
在大量的网络故障场景,从来就没有足够的时间进行系统恢复。我们处于持续的压力状态。
例如,重选路由是一个高优先级的活动。如果存在大量的不能屏蔽或消除的重选路由工作,那么资源将会被持续的消耗,用来支持这些高优先级的工作。
随着媒体资产大小的增长,系统的负载也增加了。随着请求来源数量的增加,系统负载也增加了。
随着更多的特性被添加,系统中的漏洞要比系统曾经设想中的要多。
更多的客户端意味着更多的资源使用。线程是为客户端建立的,以便可以将事件推送给它们。内存被用来做客户端队列。网络带宽被用来通信。为了每个客户端,每个客户数据都需要被维护。
有许多设计上的问题能导致伸缩性的问题。
大多数你做出的估计都是无效的,有关于你正在使用内存数量、某些事情需要的时间、什么才是合理的超时、在一个时刻你能够消耗的数量,故障有多大几率发生,系统中不同的点潜伏的问题、你的队列需要有多大,等等。
内存使用的基线增长,以及内存使用的峰值增长。这些可能会引起内存溢出(Out of Memory)。
更多的对象占用了更多的内存。如果有人想支持1千万同步对象,你可能办不到,因为很简单你无法获得足够的内存。
大多数功能会将许多额外的资源用于每个以“原生”方式使用的资源。如果你在两个不同的列表中存储了一个对象,那么该对象数量的增长将会带来两倍的内存使用量增长。队列的大小也需要向上调整。磁盘的数量需要增长。将数据复制到二级存储所需要的时间有增长。将数据装载进一个应用所需要的时间有增长。为了所有这些,CPU的使用量有增长。引导时间有增长。
事件爆炸
许多系统中都新增了无数的工作流。网站服务器和应用程序服务器需要为巨型用户群提供服务,所以需求有多大,系统提供的服务就有多少。这样的工作永无止境。用户的请求每周7天每天24小时不停的来。所以服务器的CPU使用率很容易就打到100%。
一般都把100%的CPU使用率当成一个坏信号。为了避免这样,所以我们构建了非常复杂的架构以达到负载平衡,复制信号和服务器集群。
CPU是不会感觉到累的,所以你就想我们可以尽可能多的提高CPU的使用率。
另一方面,我们也通过最大限度的增加应届资源来提供效率。
在服务器端我们总是认为得保持CPU的低使用率以保证服务器能维持一个文档的响应速度。这样的做法是出于这样的考虑,如果没有可用的CPU资源,那么我们就不能在可用户可接受的响应时间内给出响应,或者去做其他的事情。
CPU 100%的使用率真的是个问题吗?使用空闲的CPU资源,与其使用任务的优先级这样简单的电脑程序的认知方法去构建系统,而不去深究使系统低量的工作负担,也不去搜集信息去做一些特别的决策,这真的不是一个问题?
除了根据负载平衡原则而构建笨拙的系统,猜测使用线程的数量和线程的优先级之外,我们束手无策。
要删除系统的功能的时候要从架构方面小心考虑。现在的应用构架很难决定程序是怎么跑的。
延迟增加
你遇到的延迟可能完全不同于规模的增长。CPU 资源不足是主要原因。
任务优先级被证明是错误的
可正常工作在轻负载情况下的任务优先级计划在重负载时会产生问题。尤其是,当有一个不好的流程控制机制时,高优先级的任务工作,低优先级的任务引起丢失和内存使用失效,因为低优先级的任务会获得比较少的执行机会。
队列的尺寸不够足够大
很多对象暗示更多并发操作将被产生,这意味着队列的大小需要增加。
启动时间更长
对象越多,需要越多的实践从磁盘加载到应用程序。
同步时间更长
对象越多,需要越长实践来同步各个对象之间的数据。
有不一样的测试在大配置中
因为搭建测试时间如此之多,所以实际测试搭时间少了。你不能在开发阶段访问一个大的系统,所以很可能你的设计将比一开始大。
如果某个操作被应用到每个对象,那么随着更多对象的增加,将会需要比以前更长的时间。表会更大,所以可能对一个数据量的数据很快的查询现在需要长得多的时间。
在普通的操作中你可能看不到某种特定的失效。在规模增大以后响应将下降,ARP请求将下降,文件系统将显现出特定的错误,消息可能丢失,回复可能丢失,等等。
上规模意味着更有可能会失败,因为一切都需要更长的时间。对很小的数据集,某种交换数据的协议可能进行得飞快,这意味着它少有机会看到重启或超时,但对于更大规模,时间窗口增长意味着新的问题可能会第一次出现。
在数据集变大时,很少数据集时的超时将不会发出请求。再加上CPU匮乏的问题,你的代码可能甚至没有机会运行,超时进一步的失效了。
在失败被声明前,应用没有办法选择重试的数量,因为它们没有支持做出决策的足够的信息。一秒钟4次重试好不好?为什么不20次呢?
大范围内的锁会存留更长时间,这给看到优先级继承问题创造了更好的机会。
在一个规模你能从生产者获得所有的数据。但在另一个规模你用完了队列的空间/内存。一个这样的例子就是某种轮询器,它被用来从远程的队列轮询所有的数据源,接下去再继续下一个队列。当队列上只有少量的项目时,这工作的很好。但可能某个属性变化会致使远程队列上的项目数量膨胀,轮询器会使某个节点耗尽内存。
100% CPU负荷的情形会导致看门狗(监视器)的超时。在较小规模的系统中,这鲜有发生,但是对较大规模的不良设计可能会使之发生。
在较小规模内存泄漏不会被人注意,但在较大规模它可能会变得非常显著。
一个应该在适当位置的锁但未在,在较小规模系统可能不引人注意,因为线程可能永远也不会恰好在能引起问题的指令前放弃处理器。在较大规模的系统中,将有更多的抢占,这意味着会有更多的机会看到不同的线程同时访问数据。
不同的调度模式使代码沿不同的路径运行,因此会有更大的遇到死锁的可能性。例如,当文件系统由于高CPU使用率而不能获得运行的机会,有时不知何故它会占用100%CPU而且不会再次运行。
时间同步不具有一个非常高的优先级,因此随着CPU和网络资源变得稀缺,不同节点的时钟将会漂移。
记录器会丢弃数据,是因为它的队列太小了,不能应对增长的负载,或者CPU太忙了,不能给记录器时间去分派日志数据。根据队列的大小和类型,这可能会引起OOM(内存不足Out Of Memory)。
一个繁忙的系统将无法在期望的时间触发定时器,这会在系统的其它部分级联的引起一个时间延迟。
在CPU高负荷或者网络高负载期间,机器之间的ARP会丢弃。这意味着包会被发送到错误的条目,直到表格被更新,也有可能永远也不会更新。
通常一个容器上文件描述符的数量有一个固定数值的极限。设计必须限制最大的数字在这个极限以下。如果阻塞的描述符从文件描述符池取出,那么具有大量连接数(ftp, com, 启动程序, 客户端,等等)的设计将导致问题。大规模条件下,描述符的数量可能有一个峰值。随着规模的增长,描述符泄漏会用光可用的描述符池。
每个socket 都有一个分配数量的缓冲空间。大量的socket能减少可获得内存的全部总量。随着规模的增长,丢弃增多,因为用来接受消息的缓存数量不足以跟上负载。这也与优先级有关,因为任务可能不具有从socket读出数据的足够优先级。在发送方,高优先级的任务可能用消息将低优先级的消息淹没。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务