双语网站建设方案,wordpress国内课题组,分类信息网址导航,微信网站怎么建设对性能孜孜不倦的追求是整个人类技术不断发展的根本驱动力。例如计算机#xff0c;从电子管计算机到晶体管计算机再到集成电路计算机#xff0c;运算性能从每秒几次提升到每秒几亿次。但伴随性能越来越高#xff0c;相应的方法和系统复杂度也是越来越高。现代的计算机 CPU 集…对性能孜孜不倦的追求是整个人类技术不断发展的根本驱动力。例如计算机从电子管计算机到晶体管计算机再到集成电路计算机运算性能从每秒几次提升到每秒几亿次。但伴随性能越来越高相应的方法和系统复杂度也是越来越高。现代的计算机 CPU 集成了几亿颗晶体管逻辑复杂度和制造复杂度相比最初的晶体管计算机根本不可同日而语。
软件系统也存在同样的现象。最近几十年软件系统性能飞速发展从最初的计算机只能进行简单的科学计算到现在 Google 能够支撑每秒几万次的搜索。与此同时软件系统规模也从单台计算机扩展到上万台计算机从最初的单用户单工的字符界面 Dos 操作系统到现在的多用户多工的 Windows 10 图形操作系统。
当然技术发展带来了性能上的提升不一定带来复杂度的提升。例如硬件存储从纸带→磁带→磁盘→SSD并没有显著带来系统复杂度的增加。因为新技术会逐步淘汰旧技术这种情况下我们直接用新技术即可不用担心系统复杂度会随之提升。只有那些并不是用来取代旧技术而是开辟了一个全新领域的技术才会给软件系统带来复杂度因为软件系统在设计的时候就需要在这些技术之间进行判断选择或者组合。就像汽车的发明无法取代火车飞机的出现也并不能完全取代火车所以我们在出行的时候需要考虑选择汽车、火车还是飞机这个选择的过程就比较复杂了要考虑价格、时间、速度、舒适度等各种因素。
软件系统中高性能带来的复杂度主要体现在两方面一方面是单台计算机内部为了高性能带来的复杂度另一方面是多台计算机集群为了高性能带来的复杂度。
单机复杂度
计算机内部复杂度最关键的地方就是操作系统。计算机性能的发展本质上是由硬件发展驱动的尤其是 CPU 的性能发展。著名的“摩尔定律”表明了 CPU 的处理能力每隔 18 个月就翻一番而将硬件性能充分发挥出来的关键就是操作系统所以操作系统本身其实也是跟随硬件的发展而发展的操作系统是软件系统的运行环境操作系统的复杂度直接决定了软件系统的复杂度。
操作系统和性能最相关的就是进程和线程。最早的计算机其实是没有操作系统的只有输入、计算和输出功能用户输入一个指令计算机完成操作大部分时候计算机都在等待用户输入指令这样的处理性能很显然是很低效的因为人的输入速度是远远比不上计算机的运算速度的。
为了解决手工操作带来的低效批处理操作系统应运而生。批处理简单来说就是先把要执行的指令预先写下来写到纸带、磁带、磁盘等形成一个指令清单这个指令清单就是我们常说的“任务”然后将任务交给计算机去执行批处理操作系统负责读取“任务”中的指令清单并进行处理计算机执行的过程中无须等待人工手工操作这样性能就有了很大的提升。
批处理程序大大提升了处理性能但有一个很明显的缺点计算机一次只能执行一个任务如果某个任务需要从 I/O 设备例如磁带读取大量的数据在 I/O 操作的过程中CPU 其实是空闲的而这个空闲时间本来是可以进行其他计算的。
为了进一步提升性能人们发明了“进程”用进程来对应一个任务每个任务都有自己独立的内存空间进程间互不相关由操作系统来进行调度。此时的 CPU 还没有多核和多线程的概念为了达到多进程并行运行的目的采取了分时的方式即把 CPU 的时间分成很多片段每个片段只能执行某个进程中的指令。虽然从操作系统和 CPU 的角度来说还是串行处理的但是由于 CPU 的处理速度很快从用户的角度来看感觉是多进程在并行处理。
多进程虽然要求每个任务都有独立的内存空间进程间互不相关但从用户的角度来看两个任务之间能够在运行过程中就进行通信会让任务设计变得更加灵活高效。否则如果两个任务运行过程中不能通信只能是 A 任务将结果写到存储B 任务再从存储读取进行处理不仅效率低而且任务设计更加复杂。为了解决这个问题进程间通信的各种方式被设计出来了包括管道、消息队列、信号量、共享存储等。
多进程让多任务能够并行处理任务但本身还有缺点单个进程内部只能串行处理而实际上很多进程内部的子任务并不要求是严格按照时间顺序来执行的也需要并行处理。例如一个餐馆管理进程排位、点菜、买单、服务员调度等子任务必须能够并行处理否则就会出现某个客人买单时间比较长比如说信用卡刷不出来其他客人都不能点菜的情况。为了解决这个问题人们又发明了线程线程是进程内部的子任务但这些子任务都共享同一份进程数据。为了保证数据的正确性又发明了互斥锁机制。有了多线程后操作系统调度的最小单位就变成了线程而进程变成了操作系统分配资源的最小单位。
多进程多线程虽然让多任务并行处理的性能大大提升但本质上还是分时系统并不能做到时间上真正的并行。解决这个问题的方式显而易见就是让多个 CPU 能够同时执行计算任务从而实现真正意义上的多任务并行。目前这样的解决方案有 3 种SMPSymmetric Multi-Processor对称多处理器结构、NUMANon-Uniform Memory Access非一致存储访问结构、MPPMassive Parallel Processing海量并行处理结构。其中 SMP 是我们最常见的目前流行的多核处理器就是 SMP 方案。
操作系统发展到现在如果我们要完成一个高性能的软件系统需要考虑如多进程、多线程、进程间通信、多线程并发等技术点而且这些技术并不是最新的就是最好的也不是非此即彼的选择。在做架构设计的时候需要花费很大的精力来结合业务进行分析、判断、选择、组合这个过程同样很复杂。举一个最简单的例子Nginx 可以用多进程也可以用多线程JBoss 采用的是多线程Redis 采用的是单进程Memcache 采用的是多线程这些系统都实现了高性能但内部实现差异却很大。
集群的复杂度
虽然计算机硬件的性能快速发展但和业务的发展速度相比还是小巫见大巫了尤其是进入互联网时代后业务的发展速度远远超过了硬件的发展速度。例如 2016 年“双 11”支付宝每秒峰值达 12 万笔支付。 2017 年春节微信红包收发红包每秒达到 76 万个。
要支持支付和红包这种复杂的业务单机的性能无论如何是无法支撑的必须采用机器集群的方式来达到高性能。例如支付宝和微信这种规模的业务系统后台系统的机器数量都是万台级别的。
通过大量机器来提升性能并不仅仅是增加机器这么简单让多台机器配合起来达到高性能的目的是一个复杂的任务我针对常见的几种方式简单分析一下。
1. 任务分配
任务分配的意思是指每台机器都可以处理完整的业务任务不同的任务分配到不同的机器上执行。
我从最简单的一台服务器变两台服务器开始来讲任务分配带来的复杂性整体架构示意图如下。 从图中可以看到1 台服务器演变为 2 台服务器后架构上明显要复杂多了主要体现在 需要增加一个任务分配器这个分配器可能是硬件网络设备例如F5、交换机等可能是软件网络设备例如LVS也可能是负载均衡软件例如Nginx、HAProxy还可能是自己开发的系统。选择合适的任务分配器也是一件复杂的事情需要综合考虑性能、成本、可维护性、可用性等各方面的因素。 任务分配器和真正的业务服务器之间有连接和交互即图中任务分配器到业务服务器的连接线需要选择合适的连接方式并且对连接进行管理。例如连接建立、连接检测、连接中断后如何处理等。 任务分配器需要增加分配算法。例如是采用轮询算法还是按权重分配又或者按照负载进行分配。如果按照服务器的负载进行分配则业务服务器还要能够上报自己的状态给任务分配器。
这一大段描述即使你可能还看不懂但也应该感受到其中的复杂度了更何况还要真正去实践和实现。
上面这个架构只是最简单地增加 1 台业务机器我们假设单台业务服务器每秒能够处理 5000 次业务请求那么这个架构理论上能够支撑 10000 次请求实际上的性能一般按照 8 折计算大约是 8000 次左右。
如果我们的性能要求继续提高假设要求每秒提升到 10 万次上面这个架构会出现什么问题呢是不是将业务服务器增加到 25 台就可以了呢显然不是因为随着性能的增加任务分配器本身又会成为性能瓶颈当业务请求达到每秒 10 万次的时候单台任务分配器也不够用了任务分配器本身也需要扩展为多台机器这时的架构又会演变成这个样子。
这个架构比 2 台业务服务器的架构要复杂主要体现在 任务分配器从 1 台变成了多台对应图中的任务分配器 1 到任务分配器 M这个变化带来的复杂度就是需要将不同的用户分配到不同的任务分配器上即图中的虚线“用户分配”部分常见的方法包括 DNS 轮询、智能 DNS、CDNContent Delivery Network内容分发网络、GSLB 设备Global Server Load Balance全局负载均衡等。 任务分配器和业务服务器的连接从简单的“1 对多”1 台任务分配器连接多台业务服务器变成了“多对多”多台任务分配器连接多台业务服务器的网状结构。 机器数量从 3 台扩展到 30 台一般任务分配器数量比业务服务器要少这里我们假设业务服务器为 25 台任务分配器为 5 台状态管理、故障处理复杂度也大大增加。
上面这两个例子都是以业务处理为例实际上“任务”涵盖的范围很广可以指完整的业务处理也可以单指某个具体的任务。例如“存储”“运算”“缓存”等都可以作为一项任务因此存储系统、运算系统、缓存系统都可以按照任务分配的方式来搭建架构。此外“任务分配器”也并不一定只能是物理上存在的机器或者一个独立运行的程序也可以是嵌入在其他程序中的算法例如 Memcache 的集群架构。 2. 任务分解
通过任务分配的方式我们能够突破单台机器处理性能的瓶颈通过增加更多的机器来满足业务的性能需求但如果业务本身也越来越复杂单纯只通过任务分配的方式来扩展性能收益会越来越低。例如业务简单的时候 1 台机器扩展到 10 台机器性能能够提升 8 倍需要扣除机器群带来的部分性能损耗因此无法达到理论上的 10 倍那么高但如果业务越来越复杂1 台机器扩展到 10 台性能可能只能提升 5 倍。造成这种现象的主要原因是业务越来越复杂单台机器处理的性能会越来越低。为了能够继续提升性能我们需要采取第二种方式任务分解。
继续以上面“任务分配”中的架构为例“业务服务器”如果越来越复杂我们可以将其拆分为更多的组成部分我以微信的后台架构为例。
http://image.jiagoushuo.com/2016/qAnayi.jpg
通过上面的架构示意图可以看出微信后台架构从逻辑上将各个子业务进行了拆分包括接入、注册登录、消息、LBS、摇一摇、漂流瓶、其他业务聊天、视频、朋友圈等。
通过这种任务分解的方式能够把原来大一统但复杂的业务系统拆分成小而简单但需要多个系统配合的业务系统。从业务的角度来看任务分解既不会减少功能也不会减少代码量事实上代码量可能还会增加因为从代码内部调用改为通过服务器之间的接口调用那为何通过任务分解就能够提升性能呢
主要有几方面的因素
简单的系统更加容易做到高性能
系统的功能越简单影响性能的点就越少就更加容易进行有针对性的优化。而系统很复杂的情况下首先是比较难以找到关键性能点因为需要考虑和验证的点太多其次是即使花费很大力气找到了修改起来也不容易因为可能将 A 关键性能点提升了但却无意中将 B 点的性能降低了整个系统的性能不但没有提升还有可能会下降。
可以针对单个任务进行扩展
当各个逻辑任务分解到独立的子系统后整个系统的性能瓶颈更加容易发现而且发现后只需要针对有瓶颈的子系统进行性能优化或者提升不需要改动整个系统风险会小很多。以微信的后台架构为例如果用户数增长太快注册登录子系统性能出现瓶颈的时候只需要优化登录注册子系统的性能可以是代码优化也可以简单粗暴地加机器消息逻辑、LBS 逻辑等其他子系统完全不需要改动。
既然将一个大一统的系统分解为多个子系统能够提升性能那是不是划分得越细越好呢例如上面的微信后台目前是 7 个逻辑子系统如果我们把这 7 个逻辑子系统再细分划分为 100 个逻辑子系统性能是不是会更高呢
其实不然这样做性能不仅不会提升反而还会下降最主要的原因是如果系统拆分得太细为了完成某个业务系统间的调用次数会呈指数级别上升而系统间的调用通道目前都是通过网络传输的方式性能远比系统内的函数调用要低得多。我以一个简单的图示来说明。 从图中可以看到当系统拆分 2 个子系统的时候用户访问需要 1 次系统间的请求和 1 次响应当系统拆分为 4 个子系统的时候系统间的请求次数从 1 次增长到 3 次假如继续拆分下去为 100 个子系统为了完成某次用户访问系统间的请求次数变成了 99 次。
为了描述简单我抽象出来一个最简单的模型假设这些系统采用 IP 网络连接理想情况下一次请求和响应在网络上耗费为 1ms业务处理本身耗时为 50ms。我们也假设系统拆分对单个业务请求性能没有影响那么系统拆分为 2 个子系统的时候处理一次用户访问耗时为 51ms而系统拆分为 100 个子系统的时候处理一次用户访问耗时竟然达到了 149ms。
虽然系统拆分可能在某种程度上能提升业务处理性能但提升性能也是有限的不可能系统不拆分的时候业务处理耗时为 50ms系统拆分后业务处理耗时只要 1ms因为最终决定业务处理性能的还是业务逻辑本身业务逻辑本身没有发生大的变化下理论上的性能是有一个上限的系统拆分能够让性能逼近这个极限但无法突破这个极限。因此任务分解带来的性能收益是有一个度的并不是任务分解越细越好而对于架构设计来说如何把握这个粒度就非常关键了。
小结
今天我给你讲了软件系统中高性能带来的复杂度主要体现的两方面一是单台计算机内部为了高性能带来的复杂度二是是多台计算机集群为了高性能带来的复杂度希望对你有所帮助。