此系列文章为极客时间上从0开始学架构学习后感悟总结,虽然隔了一段时间了,那么就再看一遍而且进行感悟升华,排版格式上有问题,后期再复习时也会进行更新web
一. 高性能数据库集群:读写分离算法
读写分离的基本原理是将数据库读写操做分散到不一样的节点上。数据库
数据库服务器搭建主从集群,一主一从、一主多从均可以编程
数据库主机负责读写操做,从机只负责读操做后端
数据库主机经过复制将数据同步到从机,每台数据库服务器都存储了全部的业务数据浏览器
业务服务器将写操做发给数据库主机,将读操做发给数据库从机缓存
从代码层面与运维层面实现读写分离很简单并不复杂,复杂来源于读写分离实现后引出的两个问题。复制延迟、分配机制。安全
复制延迟体现于,从主机写入数据后因为复制须要时间,此时马上查询从机没法获取刚才新写加的数据,对应的解决方式有3种:服务器
分配机制通常则为两种选型,程序代码封装与中间件封装。网络
二. 高性能数据库集群:分库分表
业务分库指的是按照业务模块将数据分散到不一样的数据库服务器。例如,一个简单的电商网站,包括用户、商品、订单三个业务模块,咱们能够将用户数据、商品数据、订单数据分开放到三台不一样的数据库服务器上,而不是将全部数据都放在一台数据库服务器上。相应的也会带来一些问题
既然有问题,那么咱们对应的解决方式呢,咱们回想架构设计三原则:合适、简单、演化,对应的问题一会儿就豁然开朗了
将不一样业务数据分散存储到不一样的数据库服务器,可以支撑百万甚至千万用户规模的业务,但若是业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈,此时就须要对单表数据进行拆分。
垂直分表适合将表中某些不经常使用且占了大量空间的列拆分出去。垂直分表引入的复杂性主要体如今表操做的数量要增长。所以要区分好经常使用与不经常使用字段
水平分表适合表行数特别大的表,有的公司要求单表行数超过 5000 万就必须进行分表,这个数字能够做为参考,但并非绝对标准,关键仍是要看表的访问性能。对于一些比较复杂的表,可能超过 1000 万就要分表了;而对于一些简单的表,即便存储数据超过 1 亿行,也能够不分表。但无论怎样,当看到表的数据量达到千万级别时,做为架构师就要警觉起来,由于这极可能是架构的性能瓶颈或者隐患。引出的问题有:
a) 范围路由:范围路由的优势是能够随着数据的增长平滑地扩充新的表,一个比较隐含的缺点是分布不均匀
b) Hash 路由:Hash 路由的优缺点和范围路由基本相反,Hash 路由的优势是表分布比较均匀,缺点是扩充新的表很麻烦,全部数据都要重分布
c) 配置路由:配置路由设计简单,使用起来很是灵活,尤为是在扩充表的时候,只须要迁移指定的数据,而后修改路由表就能够了,缺点就是必须多查询一次,会影响总体性能;并且路由表自己若是太大(例如,几亿条数据),性能一样可能成为瓶颈,若是咱们再次将路由表分库分表,则又面临一个死循环式的路由算法选择问题。
2.Join操做。水平分表后,数据分散在多个表中,若是须要与其余表进行 join 查询,须要在业务代码或者数据库中间件中进行屡次 join 查询,而后将结果合并。
3.Count操做。水平分表后,虽然物理上数据分散到多个表中,但某些业务逻辑上仍是会将这些表看成一个表来处理
a) count相加:具体作法是在业务代码或者数据库中间件中对每一个表进行 count() 操做,而后将结果相加。这种方式实现简单,缺点就是性能比较低。
b) 记录数表:具体作法是新建一张表,假如表名为“记录数表”,包含 table_name、row_count 两个字段,每次插入或者删除子表数据成功后,都更新“记录数表”。
4.order by操做。水平分表后,数据分散到多个子表中,排序操做没法在数据库中完成,只能由业务代码或者数据库中间件分别查询每一个子表中的数据,而后汇总进行排序
三. NoSql
关系数据库通过几十年的发展后已经很是成熟,强大的 SQL 功能和 ACID 的属性,使得关系数据库普遍应用于各式各样的系统中,但这并不意味着关系数据库是完美的,关系数据库存在以下缺点:
针对上述问题,分别诞生了不一样的 NoSQL 解决方案,这些方案与关系数据库相比,在某些应用场景下表现更好。但世上没有免费的午饭,NoSQL 方案带来的优点,本质上是牺牲 ACID 中的某个或者某几个特性,所以咱们不能盲目地迷信 NoSQL 是银弹,而应该将 NoSQL 做为 SQL 的一个有力补充,NoSQL != No SQL,而是 NoSQL = Not Only SQL
K-V 存储
K-V 存储的全称是 Key-Value 存储,其中 Key 是数据的标识,和关系数据库中的主键含义同样,Value 就是具体的数据。
Redis 是 K-V 存储的典型表明,它是一款开源(基于 BSD 许可)的高性能 K-V 缓存和存储系统。Redis 的 Value 是具体的数据结构,包括 string、hash、list、set、sorted set、bitmap 和 hyperloglog,因此经常被称为数据结构服务器。
Redis支持的操做在关系型数据库中实现很麻烦,并且须要进行屡次 SQL 操做,性能很低。例如:删除第一个值并更新全部的id
Redis 的缺点主要体如今并不支持完整的 ACID 事务,Redis 虽然提供事务功能,但 Redis 的事务和关系数据库的事务不可同日而语,Redis 的事务只能保证隔离性和一致性(I 和 C),没法保证原子性和持久性(A 和 D)。虽然 Redis 并无严格遵循 ACID 原则,但实际上大部分业务也不须要严格遵循 ACID 原则。
文档数据库
为了解决关系数据库 schema 带来的问题,文档数据库应运而生。文档数据库最大的特色就是 no-schema,能够存储和读取任意的数据。目前绝大部分文档数据库存储的数据格式是 JSON(或者 BSON),由于 JSON 数据是自描述的,无须在使用前定义字段,读取一个 JSON 中不存在的字段也不会致使 SQL 那样的语法错误。
优势有
缺点是
列式数据库
顾名思义,列式数据库就是按照列来存储数据的数据库,与之对应的传统关系数据库被称为“行式数据库”,由于关系数据库是按照行来存储数据的。
优势是
缺点在于必须在特定场景下才能发挥它的优点
全文搜索引擎
全文搜索引擎的技术原理被称为“倒排索引”(Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,其基本原理是创建单词到文档的索引。之因此被称为“倒排”索引,是和“正排“索引相对的,“正排索引”的基本原理是创建文档到单词的索引。
正排索引示例:
倒排索引示例:
四. 高性能缓存架构
虽然咱们能够经过各类手段来提高存储系统的性能,但在某些复杂的业务场景下,单纯依靠存储系统的性能提高不够的,典型的场景有:
缓存就是为了弥补存储系统在这些复杂业务场景下的不足,其基本原理是将可能重复使用的数据放到内存中,一次生成、屡次使用,避免每次使用都去访问存储系统。缓存可以带来性能的大幅提高,以 Memcache 为例,单台 Memcache 服务器简单的 key-value 查询可以达到 TPS 50000 以上,其基本的架构是:
缓存虽然可以大大减轻存储系统的压力,但同时也给架构引入了更多复杂性。
缓存穿透
缓存穿透是指缓存没有发挥做用,业务系统虽然去缓存查询数据,但缓存中没有数据,业务系统须要再次去存储系统查询数据。一般状况下有两种状况:
第一种状况是被访问的数据确实不存在。通常状况下,若是存储系统中没有某个数据,则不会在缓存中存储相应的数据,这样就致使用户查询的时候,在缓存中找不到对应的数据,每次都要去存储系统中再查询一遍,而后返回数据不存在。缓存在这个场景中并无起到分担存储系统访问压力的做用。一般状况下,业务上读取不存在的数据的请求量并不会太大,但若是出现一些异常状况,例如被黑客攻击,故意大量访问某些读取不存在数据的业务,有可能会将存储系统拖垮。这种状况的解决办法比较简单,若是查询存储系统的数据没有找到,则直接设置一个默认值(能够是空值,也能够是具体的值)存到缓存中,这样第二次读取缓存时就会获取到默认值,而不会继续访问存储系统。
第二种状况是存储系统中存在数据,但生成缓存数据须要耗费较长时间或者耗费大量资源。若是恰好在业务访问的时候缓存失效了,那么也会出现缓存没有发挥做用,访问压力所有集中在存储系统上的状况。典型的就是电商的商品分页,假设咱们在某个电商平台上选择“手机”这个类别查看,因为数据巨大,不能把全部数据都缓存起来,只能按照分页来进行缓存,因为难以预测用户到底会访问哪些分页,所以业务上最简单的就是每次点击分页的时候按分页计算和生成缓存。一般状况下这样实现是基本知足要求的,可是若是被竞争对手用爬虫来遍历的时候,系统性能就可能出现问题。这种状况并无太好的解决方案,由于爬虫会遍历全部的数据,并且何时来爬取也是不肯定的,多是天天都来,也多是每周,也多是一个月来一次,咱们也不可能为了应对爬虫而将全部数据永久缓存。一般的应对方案要么就是识别爬虫而后禁止访问,但这可能会影响 SEO 和推广;要么就是作好监控,发现问题后及时处理,由于爬虫不是攻击,不会进行暴力破坏,对系统的影响是逐步的,监控发现问题后有时间进行处理。
缓存雪崩
缓存雪崩是指当缓存失效(过时)后引发系统性能急剧降低的状况。当缓存过时被清除后,业务系统须要从新生成缓存,所以须要再次访问存储系统,再次进行运算,这个处理步骤耗时几十毫秒甚至上百毫秒。而对于一个高并发的业务系统来讲,几百毫秒内可能会接到几百上千个请求。因为旧的缓存已经被清除,新的缓存还未生成,而且处理这些请求的线程都不知道另外有一个线程正在生成缓存,所以全部的请求都会去从新生成缓存,都会去访问存储系统,从而对存储系统形成巨大的性能压力。这些压力又会拖慢整个系统,严重的会形成数据库宕机,从而造成一系列连锁反应,形成整个系统崩溃。
缓存热点
缓存热点的解决方案就是复制多份缓存副本,将请求分散到多个缓存服务器上,减轻缓存热点致使的单台缓存服务器压力。
虽然缓存系统自己的性能比较高,但对于一些特别热点的数据,若是大部分甚至全部的业务请求都命中同一份缓存数据,则这份数据所在的缓存服务器的压力也很大。例如,某明星微博发布“咱们”来宣告恋爱了,短期内上千万的用户都会来围观。对于粉丝数超过 100 万的明星,每条微博均可以生成 100 份缓存,缓存的数据是同样的,经过在缓存的 key 里面加上编号进行区分,每次读缓存时都随机读取其中某份缓存。缓存副本设计有一个细节须要注意,就是不一样的缓存副本不要设置统一的过时时间,不然就会出现全部缓存副本同时生成同时失效的状况,从而引起缓存雪崩效应。正确的作法是设定一个过时时间范围,不一样的缓存副本的过时时间是指定范围内的随机值。
五. 单服务器高性能模式:PPC与TPC
单服务器高性能的关键之一就是服务器采起的并发模型,并发模型有以下两个关键设计点:
以上两个设计点最终都和操做系统的 I/O 模型及进程模型相关。
PPC模式
PPC 是 Process Per Connection 的缩写,其含义是指每次有新的链接就新建一个进程去专门处理这个链接的请求,这是传统的 UNIX 网络服务器所采用的模型。基本的流程图是:
注意,图中有一个小细节,父进程“fork”子进程后,直接调用了 close,看起来好像是关闭了链接,其实只是将链接的文件描述符引用计数减一,真正的关闭链接是等子进程也调用 close 后,链接对应的文件描述符引用计数变为 0 后,操做系统才会真正关闭链接。
PPC 模式实现简单,比较适合服务器的链接数没那么多的状况,例如数据库服务器。对于普通的业务服务器,在互联网兴起以前,因为服务器的访问量和并发量并无那么大,这种模式其实运做得也挺好,世界上第一个 web 服务器 CERN httpd 就采用了这种模式。互联网兴起后,服务器的并发和访问量从几十剧增到成千上万,这种模式的弊端就凸显出来了,主要体如今这几个方面:
Prefork模式
PPC 模式中,当链接进来时才 fork 新进程来处理链接请求,因为 fork 进程代价高,用户访问时可能感受比较慢,prefork 模式的出现就是为了解决这个问题。顾名思义,prefork 就是提早建立进程(pre-fork)。系统在启动的时候就预先建立好进程,而后才开始接受用户的请求,当有新的链接进来的时候,就能够省去 fork 进程的操做,让用户访问更快、体验更好。prefork 的基本示意图是:
prefork 的实现关键就是多个子进程都 accept 同一个 socket,当有新的链接进入时,操做系统保证只有一个进程能最后 accept 成功。但这里也存在一个小小的问题:“惊群”现象,就是指虽然只有一个子进程能 accept 成功,但全部阻塞在 accept 上的子进程都会被唤醒,这样就致使了没必要要的进程调度和上下文切换了。幸运的是,操做系统能够解决这个问题,例如 Linux 2.6 版本后内核已经解决了 accept 惊群问题。prefork 模式和 PPC 同样,仍是存在父子进程通讯复杂、支持的并发链接数量有限的问题,所以目前实际应用也很少。Apache 服务器提供了 MPM prefork 模式,推荐在须要可靠性或者与旧软件兼容的站点时采用这种模式,默认状况下最大支持 256 个并发链接。
TPC模式
TPC 是 Thread Per Connection 的缩写,其含义是指每次有新的链接就新建一个线程去专门处理这个链接的请求。与进程相比,线程更轻量级,建立线程的消耗比进程要少得多;同时多线程是共享进程内存空间的,线程通讯相比进程通讯更简单。所以,TPC 其实是解决或者弱化了 PPC fork 代价高的问题和父子进程通讯复杂的问题。
注意,和 PPC 相比,主进程不用“close”链接了。缘由是在于子线程是共享主进程的进程空间的,链接的文件描述符并无被复制,所以只须要一次 close 便可。TPC 虽然解决了 fork 代价高和进程通讯复杂的问题,可是也引入了新的问题,具体表如今:
除了引入了新的问题,TPC 仍是存在 CPU 线程调度和切换代价的问题。所以,TPC 方案本质上和 PPC 方案基本相似,在并发几百链接的场景下,反而更多地是采用 PPC 的方案,由于 PPC 方案不会有死锁的风险,也不会多进程互相影响,稳定性更高。
Prethread模式
好熟悉的名词,与上面那个貌似有殊途同归之妙。和 prefork 相似,prethread 模式会预先建立线程,而后才开始接受用户的请求,当有新的链接进来的时候,就能够省去建立线程的操做,让用户感受更快、体验更好。因为多线程之间数据共享和通讯比较方便,所以实际上 prethread 的实现方式相比 prefork 要灵活一些,常见的实现方式有下面几种:
Apache 服务器的 MPM worker 模式本质上就是一种 prethread 方案,但稍微作了改进。Apache 服务器会首先建立多个进程,每一个进程里面再建立多个线程,这样作主要是为了考虑稳定性,即:即便某个子进程里面的某个线程异常致使整个子进程退出,还会有其余子进程继续提供服务,不会致使整个服务器所有挂掉。prethread 理论上能够比 prefork 支持更多的并发链接,Apache 服务器 MPM worker 模式默认支持 16 × 25 = 400 个并发处理线程。
六. 单服务器高性能模式:Reactor与Proactor
上一节为单服务器高性能的 PPC 和 TPC 模式,它们的优势是实现简单,缺点是都没法支撑高并发的场景,尤为是互联网发展到如今,各类海量用户业务的出现,PPC 和 TPC 彻底无能为力。所以咱们引出应对高并发场景的单服务器高性能架构模式:Reactor 和 Proactor。
Reactor
PPC 模式最主要的问题就是每一个链接都要建立进程(,链接结束后进程就销毁了,这样作实际上是很大的浪费。为了解决这个问题,一个天然而然的想法就是资源复用,即再也不单独为每一个链接建立进程,而是建立一个进程池,将链接分配给进程,一个进程能够处理多个链接的业务。引入资源池的处理方式后,会引出一个新的问题:进程如何才能高效地处理多个链接的业务?当一个链接一个进程时,进程能够采用“read -> 业务处理 -> write”的处理流程,若是当前链接没有数据能够读,则进程就阻塞在 read 操做上。这种阻塞的方式在一个链接一个进程的场景下没有问题,但若是一个进程处理多个链接,进程阻塞在某个链接的 read 操做上,此时即便其余链接有数据可读,进程也没法去处理,很显然这样是没法作到高性能的。解决这个问题的最简单的方式是将 read 操做改成非阻塞,而后进程不断地轮询多个链接。这种方式可以解决阻塞的问题,但解决的方式并不优雅。首先,轮询是要消耗 CPU 的;其次,若是一个进程处理几千上万的链接,则轮询的效率是很低的。为了可以更好地解决上述问题,很容易能够想到,只有当链接上有数据的时候进程才去处理,这就是 I/O 多路复用技术的来源。I/O 多路复用技术概括起来有两个关键实现点:
事件反应的意思,能够通俗地理解为“来了一个事件我就有相应的反应”,这里的“我”就是 Reactor,具体的反应就是咱们写的代码,Reactor 会根据事件类型来调用相应的代码进行处理。Reactor 模式也叫 Dispatcher 模式(在不少开源的系统里面会看到这个名称的类,其实就是实现 Reactor 模式的),更加贴近模式自己的含义,即 I/O 多路复用统一监听事件,收到事件后分配(Dispatch)给某个进程。
Reactor 模式的核心组成部分包括 Reactor 和处理资源池(进程池或线程池),其中 Reactor 负责监听和分配事件,处理资源池负责处理事件。初看 Reactor 的实现是比较简单的,但实际上结合不一样的业务场景,Reactor 模式的具体实现方案灵活多变,主要体如今:
将上面两个因素排列组合一下,理论上能够有 4 种选择,但因为“多 Reactor 单进程”实现方案相比“单 Reactor 单进程”方案,既复杂又没有性能优点,所以“多 Reactor 单进程”方案仅仅是一个理论上的方案,实际没有应用。最终 Reactor 模式有这三种典型的实现方案:
以上方案具体选择进程仍是线程,更多地是和编程语言及平台相关。例如,Java 语言通常使用线程(例如,Netty),C 语言使用进程和线程均可以。例如,Nginx 使用进程,Memcache 使用线程。
单 Reactor 单进程 / 线程
单 Reactor 单进程的模式优势就是很简单,没有进程间通讯,没有进程竞争,所有都在同一个进程内完成。但其缺点也是很是明显,具体表现有:
所以,单 Reactor 单进程的方案在实践中应用场景很少,只适用于业务处理很是快速的场景,目前比较著名的开源软件中使用单 Reactor 单进程的是 Redis。
单 Reactor 多线程
单 Reator 多线程方案可以充分利用多核多 CPU 的处理能力,但同时也存在下面的问题:
你可能会发现,我只列出了“单 Reactor 多线程”方案,没有列出“单 Reactor 多进程”方案,这是什么缘由呢?主要缘由在于若是采用多进程,子进程完成业务处理后,将结果返回给父进程,并通知父进程发送给哪一个 client,这是很麻烦的事情。由于父进程只是经过 Reactor 监听各个链接上的事件而后进行分配,子进程与父进程通讯时并非一个链接。若是要将父进程和子进程之间的通讯模拟为一个链接,并加入 Reactor 进行监听,则是比较复杂的。而采用多线程时,由于多线程是共享数据的,所以线程间通讯是很是方便的。虽然要额外考虑线程间共享数据时的同步问题,但这个复杂度比进程间通讯的复杂度要低不少。
多 Reactor 多进程 / 线程
多 Reactor 多进程 / 线程的方案看起来比单 Reactor 多线程要复杂,但实际实现时反而更加简单,主要缘由是:
目前著名的开源系统 Nginx 采用的是多 Reactor 多进程,采用多 Reactor 多线程的实现有 Memcache 和 Netty。
Proactor
Reactor 是非阻塞同步网络模型,由于真正的 read 和 send 操做都须要用户进程同步操做。这里的“同步”指用户进程在执行 read 和 send 这类 I/O 操做的时候是同步的,若是把 I/O 操做改成异步就可以进一步提高性能,这就是异步网络模型 Proactor。Proactor 中文翻译为“前摄器”比较难理解,与其相似的单词是 proactive,含义为“主动的”,所以咱们照猫画虎翻译为“主动器”反而更好理解。Reactor 能够理解为“来了事件我通知你,你来处理”,而 Proactor 能够理解为“来了事件我来处理,处理完了我通知你”。这里的“我”就是操做系统内核,“事件”就是有新链接、有数据可读、有数据可写的这些 I/O 事件,“你”就是咱们的程序代码。
理论上 Proactor 比 Reactor 效率要高一些,异步 I/O 可以充分利用 DMA 特性,让 I/O 操做与计算重叠,但要实现真正的异步 I/O,操做系统须要作大量的工做。目前 Windows 下经过 IOCP 实现了真正的异步 I/O,而在 Linux 系统下的 AIO 并不完善,所以在 Linux 下实现高并发网络编程时都是以 Reactor 模式为主。因此即便 Boost.Asio 号称实现了 Proactor 模型,其实它在 Windows 下采用 IOCP,而在 Linux 下是用 Reactor 模式(采用 epoll)模拟出来的异步模型。
七. 高性能负载均衡:分类及架构
单服务器不管如何优化,不管采用多好的硬件,总会有一个性能天花板,当单服务器的性能没法知足业务需求时,就须要设计高性能集群来提高系统总体的处理性能。高性能集群的本质很简单,经过增长更多的服务器来提高系统总体的计算能力。因为计算自己存在一个特色:一样的输入数据和逻辑,不管在哪台服务器上执行,都应该获得相同的输出。所以高性能集群设计的复杂度主要体如今任务分配这部分,须要设计合理的任务分配策略,将计算任务分配到多台服务器上执行。高性能集群的复杂性主要体如今须要增长一个任务分配器,以及为任务选择一个合适的任务分配算法。
DNS 负载均衡
DNS 是最简单也是最多见的负载均衡方式,通常用来实现地理级别的均衡。例如,北方的用户访问北京的机房,南方的用户访问深圳的机房。DNS 负载均衡的本质是 DNS 解析同一个域名能够返回不一样的 IP 地址。例如,一样是 www.baidu.com,北方用户解析后获取的地址是 61.135.165.224(这是北京机房的 IP),南方用户解析后获取的地址是 14.215.177.38(这是深圳机房的 IP)。
DNS 负载均衡实现简单、成本低,但也存在粒度太粗、负载均衡算法少等缺点。仔细分析一下优缺点,其优势有:
缺点有:
针对 DNS 负载均衡的一些缺点,对于时延和故障敏感的业务,有一些公司本身实现了 HTTP-DNS 的功能,即便用 HTTP 协议实现一个私有的 DNS 系统。这样的方案和通用的 DNS 优缺点正好相反。
硬件负载均衡
硬件负载均衡是经过单独的硬件设备来实现负载均衡功能,这类设备和路由器、交换机相似,能够理解为一个用于负载均衡的基础网络设备。目前业界典型的硬件负载均衡设备有两款:F5 和 A10。这类设备性能强劲、功能强大,但价格都不便宜,通常只有“土豪”公司才会考虑使用此类设备。普通业务量级的公司一是负担不起,二是业务量没那么大,用这些设备也是浪费。
硬件负载均衡的优势是:
硬件负载均衡的缺点是:
软件负载均衡
软件负载均衡经过负载均衡软件来实现负载均衡功能,常见的有 Nginx 和 LVS,其中 Nginx 是软件的 7 层负载均衡,LVS 是 Linux 内核的 4 层负载均衡。4 层和 7 层的区别就在于协议和灵活性,Nginx 支持 HTTP、E-mail 协议;而 LVS 是 4 层负载均衡,和协议无关,几乎全部应用均可以作,例如,聊天、数据库等。
软件和硬件的最主要区别就在于性能,硬件负载均衡性能远远高于软件负载均衡性能。Ngxin 的性能是万级,通常的 Linux 服务器上装一个 Nginx 大概能到 5 万 / 秒;LVS 的性能是十万级,听说可达到 80 万 / 秒;而 F5 性能是百万级,从 200 万 / 秒到 800 万 / 秒都有(数据来源网络,仅供参考,如需采用请根据实际业务场景进行性能测试)。固然,软件负载均衡的最大优点是便宜,一台普通的 Linux 服务器批发价大概就是 1 万元左右,相比 F5 的价格,那就是自行车和宝马的区别了。
除了使用开源的系统进行负载均衡,若是业务比较特殊,也可能基于开源系统进行定制(例如,Nginx 插件),甚至进行自研。
下面是 Nginx 的负载均衡架构示意图:
软件负载均衡的优势:
缺点:
负载均衡典型架构
前面咱们介绍了 3 种常见的负载均衡机制:DNS 负载均衡、硬件负载均衡、软件负载均衡,每种方式都有一些优缺点,但并不意味着在实际应用中只能基于它们的优缺点进行非此即彼的选择,反而是基于它们的优缺点进行组合使用。具体来讲,组合的基本原则为:DNS 负载均衡用于实现地理级别的负载均衡;硬件负载均衡用于实现集群级别的负载均衡;软件负载均衡用于实现机器级别的负载均衡。
须要注意的是,上图只是一个示例,通常在大型业务场景下才会这样用,若是业务量没这么大,则没有必要严格照搬这套架构。例如,一个大学的论坛,彻底能够不须要 DNS 负载均衡,也不须要 F5 设备,只须要用 Nginx 做为一个简单的负载均衡就足够了。
八. 高性能负载均衡:算法
负载均衡算法数量较多,并且能够根据一些业务特性进行定制开发,抛开细节上的差别,根据算法指望达到的目的,大致上能够分为下面几类。
轮询
负载均衡系统收到请求后,按照顺序轮流分配到服务器上。轮询是最简单的一个策略,无须关注服务器自己的状态,例如:
须要注意的是负载均衡系统无须关注“服务器自己状态”,这里的关键词是“自己”。也就是说,只要服务器在运行,运行状态是不关注的。但若是服务器直接宕机了,或者服务器和负载均衡系统断连了,这时负载均衡系统是可以感知的,也须要作出相应的处理。例如,将服务器从可分配服务器列表中删除,不然就会出现服务器都宕机了,任务还不断地分配给它,这明显是不合理的。
总而言之,“简单”是轮询算法的优势,也是它的缺点。
加权轮询
负载均衡系统根据服务器权重进行任务分配,这里的权重通常是根据硬件配置进行静态配置的,采用动态的方式计算会更加契合业务,但复杂度也会更高。
加权轮询是轮询的一种特殊形式,其主要目的就是为了解决不一样服务器处理能力有差别的问题。例如,集群中有新的机器是 32 核的,老的机器是 16 核的,那么理论上咱们能够假设新机器的处理能力是老机器的 2 倍,负载均衡系统就能够按照 2:1 的比例分配更多的任务给新机器,从而充分利用新机器的性能。
加权轮询解决了轮询算法中没法根据服务器的配置差别进行任务分配的问题,但一样存在没法根据服务器的状态差别进行任务分配的问题。
负载最低优先
负载均衡系统将任务分配给当前负载最低的服务器,这里的负载根据不一样的任务类型和业务场景,能够用不一样的指标来衡量。例如:
负载最低优先的算法解决了轮询算法中没法感知服务器状态的问题,由此带来的代价是复杂度要增长不少。例如:
负载最低优先算法基本上可以比较完美地解决轮询算法的缺点,由于采用这种算法后,负载均衡系统须要感知服务器当前的运行状态。固然,其代价是复杂度大幅上升。通俗来说,轮询多是 5 行代码就能实现的算法,而负载最低优先算法可能要 1000 行才能实现,甚至须要负载均衡系统和服务器都要开发代码。负载最低优先算法若是自己没有设计好,或者不适合业务的运行特色,算法自己就可能成为性能的瓶颈,或者引起不少莫名其妙的问题。因此负载最低优先算法虽然效果看起来很美好,但实际上真正应用的场景反而没有轮询(包括加权轮询)那么多。
性能最优类
负载最低优先类算法是站在服务器的角度来进行分配的,而性能最优优先类算法则是站在客户端的角度来进行分配的,优先将任务分配给处理速度最快的服务器,经过这种方式达到最快响应客户端的目的。
和负载最低优先类算法相似,性能最优优先类算法本质上也是感知了服务器的状态,只是经过响应时间这个外部标准来衡量服务器状态而已。所以性能最优优先类算法存在的问题和负载最低优先类算法相似,复杂度都很高,主要体如今:
Hash类
负载均衡系统根据任务中的某些关键信息进行 Hash 运算,将相同 Hash 值的请求分配到同一台服务器上,这样作的目的主要是为了知足特定的业务需求。例如: