当前位置: 新豪天地登录网址 > 技术资讯 > 正文

分布式架构高可用与高并发那些在工作中常用到

时间:2019-09-08 11:06来源:技术资讯
原标题:架构干货:来听听架构大师 Martin Abbott 怎么说 本篇通过阅读《高扩展性网站的50条原则》,总结出以下内容。 一方面博主没有实际的架构经验,另一方面知识面也不够宽阔,所

原标题:架构干货:来听听架构大师 Martin Abbott 怎么说

本篇通过阅读《高扩展性网站的50条原则》,总结出以下内容。

一方面博主没有实际的架构经验,另一方面知识面也不够宽阔,所以只能系统的总结书中的要点,并根据自己的理解做些归纳。

典型 Web App 架构

本文首先介绍微服务架构存在的风险,然后针对如何避免微服务架构的故障,提出了多种有效的微服务架构中的方法和技术,其中例如服务降级、变更管理、健康检查和修复、断路器、限流器等。

架构扩展性的13条最佳实践

主要内容

  本书从多个方面围绕高扩展性提出了50条建议,一个高扩展性的网站会随着业务的发展、用户的增加,自由的扩展架构,从而轻松的应付网站的快速发展。下面看看本书的具体内容:

图片 1

图片 2

目录

以下内容节选自:世界级软件架构大师 Martin Abbott 亲研架构秘籍《突破技术领导力》

化简方程

  1 不要过度的设计

  过度的设计相当于给系统增加了复杂度与维护的成本。而这些过度的设计,在正常的使用中,却没有太大的作用。往往是设计者自己认为很重要或者锦上添花的功能,实际用处不大。

  2 设计时考虑到扩展性

  在设计时要遵循一下的设计原则:设计时考虑20倍的容量,实现时考虑3倍的容量,部署时考虑1.5的容量。一面项目扩大时,临时扩展造成的困难。

  3 把方案一简再简

  应该遵循帕累托法则,20%的设计做了80%的工作,所以80%的时间,都应该放在这20%的设计上。

  一个产品主要的功能其实都集中在几个点上,把这几个点设计好了,其他的都是些附加的功能而已。所以这核心的业务一定要保证足够的简洁易用。

  4 减少DNS查询

  每个不同的域下的文件,加载时都需要查询DNS。比如cnblogs.com与i.cnblogs.com就属于不同的域。那么在查询DNS的时候,就会查询两次。当业务量很大时,就会造成一定的影响。

  5 尽可能减少对象

  由于对象在浏览器访问时,需要加载。所以可以考虑减少请求文件的数量(数量与浏览器并发加载数有关),把一些对象尽量的合并。比如图标类的文件,可以合并成一个大的图片。合理的文件数量,会加速浏览器的访问加载。

  6 使用同一品牌的网络设备

  由于一个http请求,可能通过很多物理设备。比如负载均衡器,交换机,路由器。所以尽量使用同一品牌的设备,会避免一些意外的情况。

反向代理服务

1、微服务架构的风险

1. 尽可能多地使用异步的通信方式

分布工作

图片 3

  7 X轴,横向复制

  这种事最简单的服务扩充,通过克隆或者复制实现,比如你的应用放在多个服务器上进行服务。常见的比如集群,负载均衡等等,数据库的读写分离。

  8 Y轴,拆分不同的东西

  大型系统中,拆分不同的功能,比如注册、购买、查询、云盘。等等

  9 Z轴,拆分不同的相似的东西

  比如按照用户的级别,或者用户的地理位置等等拆分。

以下是一个典型的高负载 web 应用示例:上图展示了一个典型的,三层架构的高性能 Web 应用。这种成熟的架构多年以来已被广泛部署于包括 Google、Yahoo、Facebook、Twitter、Wikipedia 在内的诸多大型 Web 应用中。

2、优雅的服务降级

同步调用会同时将两种不同服务的可用性捆绑在一起。如果其中一者产生错误或是堵塞,另一者也会受到影响。

横向扩展设计

  10 设计横向的扩展方案

  扩展包括横向、纵向。横向就是通过复制克隆应用,利用小型机集群扩展。纵向就是提高服务器的硬件以及网络设施。

  通过很多的案例都可以发现,单纯的升级硬件实现的纵向扩展,仅仅能解决一点点现实压力。而通过横向的集群扩展,却能够自由的实现伸缩。

  11 采用经济型系统

  与上面的原则类似,采用高价格的服务器,并不能保证日后的良好性能。应该使用普通的小型机集群扩展。

  12 横向扩展数据中心

  数据中心有很多的设计方案,比如

  热冷站配置:使用热站提供服务,当热站崩溃时,使用冷站继续服务。

图片 4

  推荐使用多个实时站点,成本更低,动态调用。缺点是增加了运维的难度。

  13 利用云技术进行设计

  云计算的有点就是虚拟化,可以在业务峰值时,弹性的扩充设备。并且在日常处理用,归还该扩展。

  缺点是提高了应用于虚拟环境的耦合。后面提到利用物理设备,隔离业务,在虚拟化的云计算中,可能会对业务隔离错误排查造成一定的干扰。

位于三层构架中最外层的反向代理服务器负责接受用户的接入请求,在实际应用中,代理服务器通常至少还要完成以下列表中的一部分任务:

3、变更管理

2. 使用用户泳道来隔离错误

使用正确的工具

  14 合理使用数据库

  目前有许多的数据库版本,比如传统的关系型数据库Oracle、MySQl,还有比较新的非关系型数据库NoSql,比如MongoDB,以及内存数据库FastDB,还有专门针对SSD固态硬盘的Aerospike等等。

  但是到了选型的时候,还是要一句个人的业务需求来定。看你的数据库要求的是速度,还是安全性等等。

  15 防火墙,到处都是防火墙

  防火墙可以对一些无效的访问进行拦截过滤。通常把一些CSS,静态文件,图片,JS等不采用防火墙,而关键的业务涉及到个人信息时采用。合理的设计防火墙,也会对网站的性能产生一定的影响。

  16 积极的利用日志文件

  利用各种日志以及工具,实时的监控业务。不仅仅是监控服务器的内存CPU,还应该监控业务上的数据。比如splunk(提供日志的搜集,存储,搜索,图形化展示)。

连接管理:分别维护客户端和应用服务器的连接池,管理并关闭已超时的长连接。

4、健康检查和负载均衡

根据用户划分来创建硬件隔离的“泳道 Swim Lanes”。这可以防止因为某个客户所产生的问题而影响其他客户,同时有助于诊断问题和代码发布。

不要做重复的工作

  17 不要立即检查刚做过的工作

  比如刚刚写如了数据,不要立即读取。虽然有些客户需要保证数据的完整,不能丢失。但是可以通过日志等记录,写完查这种做法,还是不推荐。

  18 停止重定向

  重定向会消耗一定的延迟,计算资源。应该尽量避免

  19 放松时序约束

  大多数的关系型数据库讲究ACID属性,扩展时就造成一定的困扰。因此某些业务适当的放松时序约束,可以提高网站的性能。

  比如某站在预定酒店时,用户预定后,会等待酒店的审核。比如某宝,在提款时,进行范围时间的确认。这种就是扩大了时序约束,进而提高网站性能以及事务安全。

攻击检测和安全隔离:由于反向代理服务无需完成任何动态页面生成任务,所有与业务逻辑相关的请求都转发至后端应用服务器处理。因此反向代理服务几乎不会被应用程序设计或后端数据漏洞所影响。反向代理的安全性和可靠性通常仅取决于产品本身。在应用服务的前端部署反向代理服务器可以有效地在后端应用和远程用户间建立起一套可靠的安全隔离和攻击检测机制。

5、自我修复

图片 5

积极利用缓存

  20 利用CDN

  可以利用CDN保存客户的数据和内容。大概的过程是,用户在进行网站访问时,转到CDN的服务器,CDN执行DNS查询,把用户请求分摊到不同的服务器。有很多的CDN服务商提供这种服务。

  21 使用过期头

  针对不同的对象类型,使用过期头,减少对象请求。常见的HTTP对应属性为:public no-cahe max-age等等

  22 缓存Ajax调用

  正确修改Http头Last-Modified Cache-Control Expires等属性。

  23 利用页面缓存

  缓存响应之前的冬天请求,降低web服务器的负载。

  24 利用应用缓存

  比如针对某些特殊的用户,缓存其请求数据。

  25 利用对象缓存

  适用于反复查询使用的数据对象。比如一个购物网站,缓存器热销产品数据。

  26 把对象缓存放在自己的层上

  使用单独的缓层,易于扩展和维护。

如果需要的话,还可以通过在外网、反向代理、后端应用和数据库等边界位置添加额外的硬件防火墙等网络隔离设备来实现更高的安全性保证。

6、故障转移缓存(Failover Caching)

3. 使用多层次的缓存

从错误中吸取教训

  27 积极的学习

  一个公司有学习的氛围,才会衍生出更好的产品。学习的内容一方面包括客户的业务知识,一方面来自技术和运维领域。

  28 不要依靠QA发现失误

  雇佣测试或者质量保证人员,最大的目的是为了检测产品的正确性。它能减少成本,提高开发人员的开发速度,因为开发人员不需要时刻关注代码的正确性,可以交给QA来测试。

  但是QA只负责发现问题,如何避免为题还是得依靠开发人员。

  29 没有回退的设计是失败的设计

  这里的回退,指的是产品发布的回退。如果碰上某些版本的BUG,可能需要交付之前可运行的版本,此时没有回退,就无法交付产品了。

  这里推荐学习持续集成的相关内容。

  30 讨论失败并从中吸取教训

  不应该在同一个问题上失败两次,每次失败多进行总结是不可缺少的。

负载均衡:通常使用轮转(Round Robin)或最少连接数优先等策略完成基于客户请求的负载均衡;也可以使用 SSI 等技术将一个客户请求拆分成若干并行计算部分分别提交到多个应用服务器。

7、重试逻辑(Retry Logic)

在多个层上尽可能地使用缓存,如数据库前的对象缓存(例如:memcached),或是页面内容缓存(例如:squid),边缘缓存(例如:Akamai)。

数据库原则

  关系型数据库的ACID属性:

  原子性:一个事务要么全执行,要么都不执行,

  一致性:事务开始和结束时,所有数据状态要一致,

  隔离性:事务的表现,是事务对数据库唯一的操作,

  持久性:事务完成,操作不能更改。

  31 注意代价高的关系

  应该在设计阶段完善的设计表的结构,等开发开始时,在增加某些列,可能会花费很高的代价。

  32 使用正确的数据库锁

  数据库有很多锁的概念,比如隐式锁、显式锁、行锁、页锁、范围锁、表锁、数据库锁等等。

  不合理的使用锁,会影响网站的吞吐量。

  33 不要使用多阶段提交

  比如两阶段提交:先表决,在提交。这回降低扩展性,因为在其提交事务完成前,是不能作其他操作的。

  34 不要使用select for update

  因为FOR UPDATE从句会导致锁定行,降低事务处理的速度。

  35 不要选择所有的数据

  比如select * from xxx;

  这种做法第一是不开与数据的扩展,比如本来有四列数据,业务处理代码直接写死。当增加了一列数据时,就会导致出错;另外就是会查询出不必要的数据。

  或者inset into xxx values(xxxx);

  这是当列信息不匹配时,也会出错。

分布式的 cache 加速:可以将反向代理分组部署在距离热点地区地理位置较近的网络边界上。通过在位于客户较近的位置提供缓冲服务来加速网络应用。这实际上就构成了 CDN 网络。

8、限流器和负载开关(Rate Limiters and Load Shedders)

4. 监控应用程序性能

容错设计与故障控制

  36 采用隔离故障的”泳道“

  服务与数据的划分有很多种,比如容器,集群,池,分片,泳道。泳道意味着每个业务有自己的领域,不能跨泳道调用。

  37 不要信任单点故障

  有很多系统设计成单点模式,当整个系统只是用该模块时,当出现单点故障,整个系统也就崩溃了。

  38 避免系统串联

  比如一个系统有很多的组件组成,每个组件99.9%的安全性,当串联3个组件时,整个系统的可用性就变成了99.7%。

  39 确保能够启用/禁用功能

  对于某些共享库,第三方服务,应该提供开启或者关闭的功能。

静态文件伺服:当收到静态文件请求时,直接返回该文件而无需将该请求提交至后端应用服务器。

9、快速且单独失效(Fail Fast and Independently)

首先要站在客户的角度去分析你的程序性能。从外部网络进行监控测试,可以模拟真实的用户体验。同时,还可以根据查询和事务操作上执行次数和耗时数据,来监控程序的内部工作情况。

避免或分发状态

  40 努力实现无状态

  实现状态会限制扩展性,增大成本

  41 尽可能在浏览器端维护会话

  一方面降低服务器压力,另一方面任何的请求可以发送给任何的服务器。

  42 利用分布式缓存存放状态

  使用独立的缓存层,利于扩展。有很多分布式的缓存方案,比如memcached。

动态响应缓存:对一段时间内不会发生改变的动态生成响应进行缓存,避免后端应用服务器频繁执行重复查询和计算。

10、舱壁模式(Bulkheads)

5. 复制数据库

异步通信和消息总线

  43 尽可能使用异步通信

  异步通信,可以确保每个服务和层之间的独立性,这样易于早呢更加系统的扩展性和减小耦合度。

  44 确保消息总线能够扩展

  尽量采用Y轴或者Z轴扩展,即按业务需求和功能扩展。因为单纯的复制或者克隆,反而会增加各个消息订阅者的监听数目。按照业务隔离,可以分离业务压力。

  45 避免让消息总线过度拥挤

  衡量价值与消息的成本。

图片 6

数据压缩传输:为返回的数据启用 GZIP/ZLIB 压缩算法以节约带宽。

11、断路器(Circuit Breakers)

复制数据库可以帮助还原数据,同时把读取的负载分配到多个实例。

其他原则

  46 慎用第三方解决方案扩展

  企业如果出现问题,那么寻找第三方能够解决燃眉之急。但是却不是长久之计,因为解决方案的提供商有很多客户,你的危机并不是他们的危机,所以不可能在关键时刻,尽职尽责。因此企业还是应该有一定的掌控力(这个词真是高大上!)。

  47 清除、归档和成本合理的存储

  有一些不必要的数据,就应该定期的删除。一些略有价值的数据进行定期的归档直接删除。一些很有价值的数据,应该进行备份以及快速访问。

  48 删除事务处理中的商业智能

  应该把产品系统与业务系统分离,提高产品的扩展性。

  避免业务扩展时,受到系统架构的限制。

  49 设计能够监控的应用

  应该设计全局的监控策略,保证回答

  ”发生了 问题了吗?“

  ”哪里发生了问题?“

  ”发生了什么问题?“

  ”会发生问题吗?“

  ”能自动修复吗?“

图片 7

  50 要能胜任

  应该在每个设计中涉及到最优秀的架构,不能完全依赖第三方的解决方案。

  一个简单优秀的架构,都是小而精的,如果单纯的依靠开源解决架构,虽然解决了问题,却会导致应用的臃肿。

数据加密保护(SSL Offloading):为与客户端的通信启用 SSL/TLS 加密保护。

12、故障测试(Testing for Failures)

6. 使用切片(Sharding)技术

参考

  【1】《高扩展性网站的50条原则》

容错:跟踪后端应用服务器的健康状况,避免将请求调度到发生故障的服务器。

13、总结

根据不同服务或(和)用户使用的量级来分割应用和数据库。虽然它会给程序带来一些轻量的复杂度,但在规模上这样做更易于扩展。

用户鉴权:完成用户登陆和会话建立等工作。

14、要点

7. 尽可能少的使用关系型数据库RDBMS特性

URL别名:对外建立统一的url别名信息,屏蔽真实位置。

微服务架构通过定义明确的服务边界,能有效地隔离故障。 和其他分布式系统一样,微服务在网络、硬件和应用层上都会存在更多的问题。由于服务之间是互相依赖,因此任何组件都可能出错导致用户不能访问。为尽可能减少部分中断带来的影响,我们需要构建容错能力强的服务,以从容应对发生的某些中断。

尽可能使用OLTP(on-line transaction processing,联机事务处理 )数据库作为存储设备。因为要确保ACID属性,关系型数据库在扩展型方面会有很多挑战。你的交易越依赖关系型数据库系统(RDBMS)提供的功能,那么系统在扩展时你投入的负载就越大。建议从数据库中将主要的业务逻辑(例如存储过程)都移到应用程序或服务内。当系统需要做大规模扩展时,应该通过应用或服务来扩展, 而不是通过SQL。

应用混搭:通过SSI和URL映射技术将不同的web应用混搭在一起。

本文介绍了构建和运维高可用的微服务架构系统中最常用的技术和架构模式。如果读者不熟悉上述的模式,那并没什么大碍。构建可靠的系统不是一踞而就的。

图片 8

协议转换:为使用 SCGI 和 FastCGI 等协议的后端应用提供协议转换服务。

微服务架构将应用逻辑拆分成服务,服务之间通过网络交互。由于是通过网络调用,而不是在进程中调用,因此这给需要在多个物理和逻辑组件间进行协作的系统带来了潜在的问题和复杂性。分布式系统变得越来越复杂,也导致网络特定故障发生的可能性增大。

8. 在服务器上小批量地部署新代码

目前比较有名的反向代理服务包括:Apache

相比传统应用庞大的结构,微服务架构最大的一个优点是团队能独立地设计、开发和部署各自的服务。团队能掌控各自服务的整个生命周期。这也意味者团队无法控制服务的依赖关系,因为这些依赖的服务可能是由其他团队管理。在微服务架构体系下,我们要牢记提供的服务由于是其他人控制,因此可能会由于发布、配置、和其他变更等原因,从而导致服务暂时不可用,而且组件之间互相独立。

尽可能小批量地在服务器上部署新代码,而不要让整个站点关闭。这要求所有代码都要向后兼容,因为在部署时你会有两个版本的代码同时运行。这种方法可以帮助我们方便地找到应用质量或者L&P测试(负载性能测试)所遗漏的问题,同时最小化对客户的影响。

httpd mod_proxy / IIS ARR / Squid / Apache Traffic Server / Nginx /

微服务架构最大的优点之一就是当组件出现故障时,能隔离这些故障并且能做到优雅地服务降级。比如,在图片分享应用中,当出现故障时,用户可能无法上传图片,但他们依然能浏览、编辑和分享已上传的图片。

9. 在部署前执行负载与性能测试

Cherokee / Lighttpd / HAProxy 以及 Varnish 等等。

图片 9微服务故障独立

一定要在产品部署前,执行负载与性能测试。虽然这不会发现所有问题(这也是为什么我们需要回滚 Rollback的能力),但它很值得做。

应用服务

在大多数情况下,是很难实现上图这种优雅地服务降级的,因为在分布式环境下,应用都是互相依赖的,开发者需要实现若干错误处理的逻辑(该部分在本文稍后部分讨论)去应对短暂的故障和中断。

10. 不能回滚注定失败

应用服务层位于数据库等后端通用服务层与反向代理层之间,向上接收由反向代理服务转发而来的客户端访问请求,向下访问由数据库层提供的结构化存储与数据查询服务。

图片 10服务互相依赖,如果无故障转移的逻辑,则会同时失效

确保所有版本的代码都有回滚能力,在准生产或者QA环境演练,必要时在生产环境中用它来解决客户的问题。如果没有经历过无法回滚代码的痛,还继续冒险地“修改-发布”代码,那么你会在未来某个时刻体会到这种痛苦。

应用层实现了 Web 应用的所有业务逻辑,通常要完成大量的计算和数据动态生成任务。应用层内的各个节点不一定是完全对等的,还可能以 SOA、μSOA 等架构拆分为不同服务集群。

Google的网站可靠性团队发现大概70%的故障都是由于变更而引起的。当对服务进行修改时……例如发布代码的新版本或者改变一些配置,则总会有可能引起故障或者引入新的错误。

11. 容量规划 / 扩展峰值

图片 11

在微服务架构中,服务是互相依赖的。这就是为什么你需要减少故障并且尽可能降低它们的负面影响。为了应对变更带来的问题,你可以实施变更策略管理并且实现其自动回滚。

对于每一层、每一个服务,都要清楚它有多大容量。使用 扩展峰值(Scalability Summits) 来规划容量的增长需求。

上图给出了一个典型的高并发、高性能应用层节点工作模型。每个 Web 应用节点(在图 5中由标有"App"字样的方框表示)通常都会工作在自己的服务器(物理服务器或VPS)之上,多个应用节点可以有效地并行工作,以方便地实现横向扩展。

比如,当部署新的代码或者修改配置时,应该分步将这些变更部署到服务实例群中的部分实例中,并且进行监控,如果发现关键指标出现问题则能自动进行回滚。

12. 问题根源分析

在上图所示的例子中,Web 应用节点由 IO 回调线程池、Web 请求队列以及后台工作线程池等三个重要部分组成,其伺服流程如下:

图片 12变更管理-回滚部署

确保有强大的学习文化,当问题出现时,一定要确保找到问题根源, 才能最终修复问题。

当一个

另一个解决方案是运行两套生产环境。部署的时候只部署变更的应用到其中一套环境中,并且在验证了新发布的版本符合预期后,才将负责均衡的流量指向新的应用,这种方法称为“蓝-绿发布”或者“红-黑发布”。

  1. 从一开始就重视质量工作

Web 请求到达后,底层操作系统通过 IOCP、epoll、kqueue、event ports、real time signal

回退代码并不是坏事情。你不应该在生产环境中部署有问题的代码,并且应该琢磨哪里出错了。当必要时候应该果断回退代码,这越早越好。

架构质量从一开始设计就要考虑进去,质量不能靠测试来解决。测试只能发现研发过程中带来的问题。

(posix aio)、/dev/poll、pollset 等各类与具体平台紧密相关的 IO 完成回调机制通知

因为故障或部署、自动扩展等原因,服务实例会不停启动,重新启动及停止。这使得服务暂时或一直停用。为了避免发生这些问题,在负载均衡中应该在路由中设置忽略这些实例,因为它们无法为子系统或用户提供服务。

转自:北京尚学堂IT学院(一点号)返回搜狐,查看更多

AIO(Asynchronous IO)回调线程,对这个已到达的 Web 请求进行处理。

我们可以通过外部观察去判断应用实例是否健康。你可以多次调用Get/health的端点或者通过自身服务的报告获得相关信息。现在的服务发现解决方案会持续从实例中收集健康信息,并且设置负载均衡的路由,让其只指向健康的实例组件。

责任编辑:

在 AIO

自我修复能帮助恢复应用。我们讨论下当应用遇到崩溃状态后,如何通过相关的步骤去自我修复。在大多数情况下,是通过外部系统监控实例的状态,当服务出现故障一段时间后则会重启服务。在大多数情况下,自我修复的功能是相当有用的,然而,在某些情况下由于不断地重启服务会带来相关的问题。例如当服务过载或者数据库连接超时,则会导致应用不能反馈正确的服务健康状态。

回调池中的工作线程接收到一个已到达的 Web

对于一些场景-比如数据库链接丢失,这个时候实现高级的自我修复功能是颇为棘手的。在这种情况下,需要为应用添加额外的逻辑去处理这些特例,并且让外部系统知道服务的实例不需要立即重新启动。

请求后,首先尝试对该请求进行预处理。在预处理过程中,将会使用位于本地的高速缓存来避免成本较高的数据库查询。如果本地缓存命中,则直接将缓存中的结果(仍然以异步

因为网络问题和系统中的变更,服务通常会出现故障。然而,这些故障中断大多是暂时的,这要归功于自我修复和高级负载平衡的功能,我们应该找到一个解决方案,能使服务即使在出现故障的时候也能工作。这就是故障转移缓存(Failover Caching),它能帮助为我们的应用提供必需的数据。

IO 的方式)返回客户端,并结束本次请求。

失效转移缓存通常使用两个不同的过期日期:其中更短的日期指示在正常情况下能使用缓存的时间,而更长的一个日期则指示在故障失效的时候,能使用缓存中的数据时长。

如果指定的 Web 请求要求查询的数据无法被本地缓存命中,或者这个 Web 请求需要数据库写入操作,则该请求将被 AIO 回调线程追加到指定的队列中,等待后台工作线程池中的某个空闲线程对其进行进一步处理。

图片 13故障转移缓存

后台工作线程池中的每个线程都分别维护着两条长连接:一条与底层到数据库服务相连,另一条则连接到分布式缓存(memcached)网络。通过让每个工作线程维护属于自己的长连接,后台工作线程池实现了数据库和分布式缓存连接池机制。长连接(Keep-Alive)通过为不同的请求重复使用同一条网络连接大大提高了应用程序处理效率和网络利用率。

特别需要提醒的是,只有当提供过时的数据比没有数据更好的情况下,才能使用故障转移缓存。

后台工作线程在 Web 请求队列上等待新的请求到达。在从队列中取出一个新的请求后,后台工作线程首先尝试使用分布式缓存服务命中该请求中的查询操作,如果网络缓存未命中或该请求需要数据库写入等进一步处理,则直接通过数据库操作来完成这个 Web 请求。

要设置缓存和故障转移缓存,可以在HTTP中使用标准响应头。

当一个 Web 请求被处理完成后,后台工作线程会将处理结果作为 Web 响应以异步 IO 的方式返回到指定客户端。

例如,使用max-age头可以指定某个资源为新资源的最大时间(译者注:意即设定max-age后,浏览器不再发送请求到服务器)。可以使用stale-if-error 头去确定在出现故障的情况下,从缓存获取资源的时间长短。

上述步骤粗略描述了一个典型 Web 应用节点的工作方式。值得注意的是,由于设计思想和具体功能的差异,不同的 Web 应用间,无论在工作模式或架构上都可能存在很大的差异。

现在的CDN和负载均衡器提供了各种缓存和故障转移的解决方案,但是你也可以在你的公司中建立一个共享库,其中包括这些标准的可靠性解决方案。

需要说明的是,与

在某些情况下,我们可能无法缓存数据,或者想对数据进行变更,但是操作最终失败了。在这种情况下,我们就可以选择重试操作,因为我们可以预期资源将在一段时间后恢复,或者负载均衡会将请求发送到健康的实例上。

epoll/kqueue/event ports 等相位触发的通知机制不同,对于 Windows IOCP 和 POSIX AIO

你应该小心地为应用程序和客户端添加重试逻辑,因为更大量的重试操作可能会使事情变得更糟,甚至阻止应用程序恢复。

Realtime Signal 这类边缘触发的 AIO 完成事件通知机制,为了避免操作系统底层 IO

在分布式系统中,微服务系统重试可能会触发多个其他请求或重试操作,并导致级联效应。为减少重试带来的影响,你应该减少重试的数量,并使用指数退避算法(exponential backoff algorithm)来持续增加重试之间的延迟时间,直到达到最大限制。

完成队列过长或溢出导致的内存缓冲区被长时间锁定在非分页内存池,在上述系统内的 AIO 回调方式实际上是由两个独立的线程池和一个

由于重试是由客户端(浏览器,其他微服务等)发起的,并且客户端在处理请求前后是不知道草走失败的,你应该为你的应用程序提供幂等处理能力。例如,当你重试购买操作时,不应该向客户收两次钱。给每个事务使用唯一的幂等键(idempotency-key)是解决重试问题的方法。

AIO 完成事件队列组成的:一个线程池专门负责不间断地等待系统 AIO 完成队列中到达的事件,并将其提交到一个内部的 AIO

限流是指在一段时间内,定义某个客户或应用可以接收或处理多少个请求的技术。例如,通过限流,你可以过滤掉产生流量峰值的客户和微服务,或者可以确保你的应用程序在自动扩展(Auto Scaling)失效前都不会出现过载的情况。

完成队列中(该队列工作在用户模式,具有用户可控的弹性尺寸,并且不会锁定内存);与此同时另一个线程池等待在这个内部 AIO

你还可以阻止较低优先级的流量,以便为关键事务提供足够的资源。

完成队列上,并且处理不断到达该队列的 AIO

图片 14限流器可以阻止流量峰值

完成事件。这样的设计降低了操作系统的工作负担,避免了在极端情况下可能出现的消息丢失、内存泄露以及内存耗尽等问题,同时也可以帮助操作系统更好地使用和管理非分页内存池。

另外有一种限流器,称为 “并发请求限流器(concurrent request limiter)”。当你有一些比较昂贵和重要的端点,希望它不应该被调用超过指定的次数,但仍然想要提供流量服务时,这个限流器就十分有用了。

作为典型案例:包括搜索引擎、Gmail

使用负载开关可以确保对于关键的事务总能提供足够的资源保障。它为高优先级的请求保留一些资源,并且不允许低优先级的事务去占用这些资源。负载开关会根据系统的整体状态做出决定,而不是基于单个用户的请求桶(request bucket)大小。负载设备有助于你的系统恢复,因为它们在持续发生故障事件时,依然能保持核心功能正常工作。

邮件服务在内的大部分 Google Web 应用均是使用 C/C 实现的。得益于 C/C 语言的高效和强大,Google 在为全球

在微服务体系架构中,我们希望服务可以快速、单独地失效。为了在服务层面隔离故障,我们可以使用隔板模式(bulkhead pattern)。可以在本文稍后看到相关介绍。

Internet 用户提供最佳 Web 应用体验的同时,也实现了在其遍及全球的上百万台分布式服务器上完成一次 Web 搜索,总能耗仅需

我们也希望我们的组件能够快速失效(fail fast),因为我们不希望等到断开的实例直到超时。没有什么比挂起的请求和无响应的界面更令人失望。这不仅浪费资源,而且还会让用户体验变得更差。我们的服务是互相调用的,所以在这些延迟叠加前,应该特别注意防止那些超时的操作。

0.0003 kW·h 的优异表现。关于 Google Web

你想到的第一个办法,可能是对每个服务的调用都定义超时的级别。这种做法的问题是,你不能真正知道到底什么是恰当的超时值,因为当网络故障和其他问题发生时,某些情况下只会影响一两次操作。在这种情况下,如果只有其中一些发生超时,你可能不想拒绝所有这些请求。

应用架构以及硬件规模等进一步讨论,你可以加这个群获取:交流学习群:744642380里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

我们可以说,通过使用超时来实现微服务中的快速失败是一种反模式,这是应该避免的。可以使用基于操作的成功/失败统计次数的熔断模式,而不是使用超时。

数据库和memcached服务

在工业领域中,常使用舱壁将划分为几个部分,以便在有某部分船体发生破裂时,其他部分依然能密封安然无恙。

数据库服务为上层

舱壁的概念也可以在软件开发中用于隔离资源。

Web 应用提供关系式或结构化的数据存储与查询支持。取决于具体用例,Web

通过使用舱壁模式,我们可以保护有限的资源不被用尽。例如,如果我们有两种类型的操作的话,它们都是和同一个数据库实例进行通信,并且数据据库限制连接数,这时我们可以使用两个连接池而不是使用一个共享的连接池。由于这种客户端和资源分离,超时或过度使用池的操作不会令所有其他操作失效。

应用可以使用数据库连接器之类的插件机制来提供对不同数据库服务的访问支持。在这种架构下,用户可以灵活地选择或变更最适合企业现阶段情况的不同数据库产品。例如:用户可以在原型阶段使用

泰坦尼克号沉没的主要原因之一是其舱壁设计失败,水可以通过上面的甲板倒在舱壁的顶部,最后整个船淹没。

SQLite 之类的嵌入式引擎完成快速部署和功能验证;而在应用的初期阶段切换到廉价的 MySql

图片 15泰坦尼克号故障的舱壁

数据库解决方案;等到业务需求不断上升,数据库负载不断加重时再向 Clustrix、MongoDB、Cassandra、MySql

为了限制操作的持续时间,我们可以使用超时。超时可以防止挂起操作并保证系统可以响应。然而,在微服务架构通信中使用静态、微调的超时是一种反模式,因为我们处于高度动态的环境中,几乎不可能确定在每种情况下都能正常工作的准确的时间限制。

Cluster、ORACLE 等更昂贵和复杂的解决方案进行迁移。

我们可以使用断路器来处理错误,而不是使用小型和特定基于事务的静态超时机制。断路器以现实世界的电子元件命名,因为它们的行为是都是相同的。你可以保护资源,并通过使用断路器协助它们进行恢。断路器在分布式系统中非常有用,因为重复的故障可能会导致雪球效应,并使整个系统崩溃。

Memcached 服务作为一个完全基于内存和

当在短时间内多次发生指定类型的错误,断路器会开启。开启的断路器可以拒绝接下来更多的请求 – 就像防止真实的电子流动一样。断路器通常在一定时间后关闭,以便为底层服务提供足够的空间来恢复。

Value> 对的分布式数据对象缓冲服务,拥有令人难以置信的查询效率以及一个优雅的,无需服务器间通信的大型分布式架构。对于高负载 Web

请记住,并不是所有的错误都应该触发断路器。例如,你可能希望忽略客户端问题,比如4xx响应代码的请求,但要包括5xx服务器端故障。一些断路器还可以有半开关状态。在这种状态下,服务发送第一个请求以检查系统的可用性,同时让其他请求失败。如果这个第一个请求成功,则将断路器恢复到关闭状态并继续接受流量。否则,保持打开状态。

应用来说,Memcached

图片 16断路器

常被用作一种重要的数据库访问加速服务,因此它不是一个必选组件。用户完全可以等到现实环境下的数据库服务出现了性能瓶颈时在部署它。值得强调的是,虽然

你应该持续地测试系统的常见问题,以确保你的服务可各类故障环境下运行。你应经常测试故障,以让你的团队对可能发生的事故有所准备。

memcached 并不是一个必选组件,但通过其在

关于测试,你可以使用外部服务来识别服务实例组,并随机终止运行组中的一个实例。通过使用这个方法,可以针对单个实例故障进行测试,你甚至可以关闭整个服务组来模拟云提供商层面的故障中断。

YouTube、Wikipedia、Amazon.com、SourceForge、Facebook、Twitter 等大型 Web

实施和运维可靠的服务并不容易。这需要你付出很多努力,还要花费公司更多的成本。

应用上的多年部署可以证明:memcached 不但能够在高负载环境下长期稳定地工作,而且可以戏剧性地提升数据查询的整体效率。有关

说到这里顺便给大家推荐一个Java架构方面的交流学习社群:650385180,里面不仅可以交流讨论,还有面试经验分享以及免费的资料下载,包括Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。相信对于已经工作和遇到技术瓶颈的码友,在这个群里会有你需要的内容。

memcached

可靠性有很多层次和方面,因此针对你的团队找出合适的解决方案是相当重要的。你应该将可靠性成为业务决策流程中的一个因素,并为此分配足够的预算和时间。

的进一步讨论,你可以加这个群获取:交流学习群:744642380里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

①、动态环境和分布式系统-如微服务将导致更高的故障机会。

当然,我们也应该注意到:以

②、服务应单独失效,实现优雅的服务降级以提升用户体验。

memcached

③、70%的问题是由变更引起的,恢复可用代码并不总是坏事。

为代表的分布式缓存系统,其本质上是一种以牺牲一致性为代价来提升平均访问效率的妥协方案——缓存服务为数据库中的部分记录增加了分布式副本。对于同一数据的多个分布式副本来说,除非使用

④、快速,单独地失败。团队无法控制其服务依赖关系。

Paxos、Raft 等一致性算法,不然无法实现强一致性保证。

⑤、架构模式和技术,如缓存、隔离技术、断路器和限流器有助于构建可靠的微服务

矛盾的是,memory cache

本身就是用来提升效率的,这使得为了它使用上述开销高昂的分布式强一致性算法变得非常不切实际:目前的分布式强一致性算法均要求每次访问请求都需要同时访问包括后台数据库主从节点在内的多数派副本——显然,这还不如干脆不使用缓存来的有效率。

另外,即使是 Paxos、Raft 之类的分布式一致性算法也只能在单个记录的级别上保证强一致。意即:即使应用了此类算法,也无法凭此提供事务级的强一致性保证。

除此之外,分布式缓存也增加了程序设计的复杂度(需要在访问数据库的同时尝试命中或更新缓存),并且还增加了较差情形下的访问延迟(如:未命中时的 RTT 等待延迟,以及节点下线、网络通信故障时的延迟等)。

与此同时,可以看到:从二十年前开始,各主流数据库产品其实均早已实现了成熟、高命中率的多层(磁盘块、数据页、结果集等)缓存机制。既然分布式缓存有如此多的缺陷,而数据库产品又自带了优秀的缓存机制,它为何又能够成为现代高负载 Web App 中的重要基石呢?

其根本原因在于:对于十年前的技术环境来说,当时十分缺乏横向扩展能力的

RDBMS系统已成为了严重制约 Web App 等网络应用扩大规模的瓶颈。为此,以 Google BigTable、Facebook

Cassandra、MongoDB 为代表的 NoSQL 数据库产品,以及以 memcached、redis

为代表的分布式缓存产品纷纷粉墨登场,并各自扮演了重要作用。

与 MySQL、ORACLE、DB2、MS SQL Server、PostgreSQL 等当时的 "传统" SQL数据库产品相比,无论 NoSQL 数据库还是分布式缓存产品,其本质上都是以牺牲前者的强一致性为代价,来换取更优的横向扩展能力。

应当看到,这种取舍是在当时技术条件下做出的无奈、痛苦的抉择,系统因此而变得复杂——在需要事务和强一致性保障,并且数据量较少的地方,使用无缓存层的传统

RDBMS;在一致性方面有一定妥协余地,并且读多写少的地方尽量使用分布式缓存来加速;在对一致性要求更低的大数据上使用

NoSQL;如果数据量较大,同时对一致性要求也较高,就只能尝试通过对 RDMBS

分库分表等方法来尽量解决,为此还要开发各种中间件来实现数据访问的请求分发和结果集聚合等复杂操作……各种情形不一而足,而它们的相互组合和交织则再次加剧了复杂性。

回顾起来,这是一个旧秩序被打破,新秩序又尚未建立起来的混乱时代——老旧 RMDBS 缺乏横向扩展能力,无法满足新时代的大数据处理需求,又没有一种能够替代老系统地位,可同时满足大部分用户需求的普适级结构化数据管理方案。

这是一个青黄不接的时代,而

BigTable、Cassandra、memcached 等产品则分别是 Google、Facebook 以及 LiveJournal

等厂商在那个时代进行 "自救" 的结果。这样以:"花费最小代价,满足自身业务需求即可" 为目标的产物自然不太容易具备很好的普适性。

然而今天,我们终于就快要走出这个窘境。随着

Google F1、MySQL Cluster、Clustrix、VoltDB、MemSQL、NuoDB 等众多 NewSQL

解决方案的逐步成熟以及技术的不断进步,横向扩展能力逐渐不再成为 RDBMS

的瓶颈。今天的架构设计师完全可以在确保系统拥有足够横向扩展能力的同时,实现分布式的事务级强一致性保证:

图片 17

如上图所示,在

NewSQL 具备了良好的横向扩展能力后,架构中不再迫切需要分布式缓存和 NoSQL

产品来弥补这方面的短板,这使得设计和开发工作再次回归到了最初的简洁和清晰。而对象存储(Object

Storage)服务则提供了对音频、视频、图片、文件包等海量非结构化BLOB数据的存储和访问支持。

这样简洁、清晰、朴素的架构使一切看起来仿佛回归到了多年以前,对象存储服务就像

FAT、NTFS、Ext3 等磁盘文件系统,NewSQL 服务则好像当年 MySQL、SQL Server 等 "单机版"

数据库。但一切却又已不同,业务逻辑、数据库和文件存储均已演进成为支持横向扩展的高可用集群,在性能、容量、可用性、可靠性、可伸缩性等方面有了巨大的飞跃:人类总是以螺旋上升的方式不断进步——在每一次看似回归的变迁中,实包含了本质的升华。

随着

GlusterFS、Ceph、Lustre 等可 mount 且支持 Native File API

的分布式文件系统越来越成熟和完善,也有望于大部分场合下逐渐替换现有的对象存储服务。至此 Web App

架构的演进才能算是完成了一次重生——这还算不上是涅槃,当我们能够在真正意义上实现出高效、高可用的多虚一(Single System

Image)系统时,涅槃才真正降临。那时的我们编写分布式应用与如今编写一个单机版的多线程应用将不会有任何区别——进程天然就是分布式、高可用的!

三层架构的可伸缩性

小到集中部署于单台物理服务器或 VPS 内,大到 Google 遍及全球的上百万台物理服务器所组成的分布式应用。前文描述的三层 Web 应用架构体现出了难以置信的可伸缩性。

具体来说,在项目验证、应用部署和服务运营的初期阶段,可以将以上三层服务组件集中部署于同一台物理服务器或 VPS 内。与此同时,可通过取消 memcached 服务,以及使用资源开销小并且易于部署的嵌入式数据库产品来进一步降低部署难度和系统整体资源开销。

随着项目运营的扩大和负载的持续加重,当单服务器方案和简单的纵向扩展已无法满足项目运营负荷时,用户即可通过将各组件分布式地运行在多台服务器内来达到横向扩展的目的。例如:反向代理可通过

DNS CNAME 记录轮转或 3/4

层转发(LVS、HAProxy等)的方式实现分布式负载均衡。应用服务则可由反向代理使用基于轮转或最小负载优先等策略来实现分布式和负载均衡。此外,使用基于共享

IP 的服务器集群方案也能够实现负载均衡和容错机制。

与此类似,memcached

和数据库产品也都有自己的分布式运算、负载均衡以及容错方案。此外,数据库访问性能瓶颈可通过更换非关系式的数据库产品,或使用主-从数据库加复制等方式来提升。而数据库查询性能则可通过部署

memcached 或类似服务来极大程度地改善。

编辑:技术资讯 本文来源:分布式架构高可用与高并发那些在工作中常用到

关键词: