背景简介
对于大型应用后台系统来说,稳定性至关重要。目前越来越多的大型应用系统采用微服务架构,更加需要关注稳定性的技术能力建设。稳定性是服务系统基础能力的体现。
基础知识
在介绍稳定性技术策略主题之前,我们首先梳理一些基础概念和知识。
针对我们业务后台系统建设,任何大型业务后台系统绝对不是一蹴而就。它是伴随着业务不同阶段,不断进行演进的过程。如果经历过从 0 到 1 建设一个业务后台系统的同学,都会有类似的体会。
启动阶段
启动阶段,业务模型相对简单,用户量少,这时候我们可以将所有的系统模块耦合在一个工程里面,进行单机部署。这时候可能仅仅需要将业务系统与数据库进行隔离。
探索阶段
探索阶段,业务模型不断演进,用户量增加,单机服务能力瓶颈凸显。这时候就需要由单机服务部署向集群服务部署来优化,利用负载均衡将请求合理分配,减少单机服务压力。另外一个方面,数据量不断的增加,也需要考虑针对数据来进行水平的扩展(主从部署,读写分离)。
在这一阶段,我们仅仅是做了集群扩展,但所有的业务代码都在同一个工程维护,所有的数据信息都在同一个数据库中存储。随着团队的扩充与业务交互不断复杂化,在工程维护上存在很大的风险,工程研发效率受到制约,业务代码之间的耦合也难以清晰,系统可靠性存在很大风险,一个 bug 可能会造成整个应用的崩溃不可用。
发展阶段
如果我们比较幸运,业务持续快速发展,对于业务角色模型理解越来越清晰,业务角色模型之间的交互越来越确定。这时候就需要我们基于对业务充分的理解前提下进行垂直拆分。不同的业务模型会部署到不同的系统工程中,工程之间通过接口来进行交互。这样工程内业务高度集中,工程间通过接口服务来进行解耦。这时候不管是系统维护,还是业务模块维护,都将大大的提高效率。数据部分同样垂直拆分,不同业务数据拆分到不同的数据库,从而提高单机数据库的能力。
拿电商系统的结构来说,如下图所示:
基于业务模型分为几个大的系统服务,系统服务之间通过内部 RPC 接口来进行交互。
成熟阶段
上面是基于大的业务模型进行划分,随着业务复杂度越来越高,我们必将对大业务模型,基于功能或者业务边界进行更细粒度的拆分。比如说,我们可以将产品中心划分为:基础信息中心,库存管理中心,SKU 中心等。
这时候就涉及到微服务的拆分与治理工作。
微服务的拆分原则我们应该注意:业务功能单一; 服务间业务边界清晰;拆分粒度合理; 分层划分合理。
从研发的角度来说,微服务带来的好处:研发效率提升;代码质量更优;能够独立部署;单模块复杂度降低。上面提到的产品中心我们可以拆分很多小的服务来提供,如下图所示:
细粒度的拆分,也会带来一定的挑战:
微服务划分单元越细,整体服务维护非常复杂;
微服务通过 RPC 接口交互,整个链路可能会不清晰;
单服务的修改或者优化会受到其他模块的牵制。
当然这些挑战都是我们需要思考与解决的问题,并不能抹杀微服务的优点。
大型业务后台系统,不断的微服务化,带来系统之间的接口交互与依赖管理也越来越重要。我们需要从整体上考虑我们如何保证这样一个流量并发高,业务模型复杂,服务依赖交互多的大型微服务后台应用系统的稳定性。不能由于某个不利因素来影响整个业务微服务系统的不可用。接下来我们将重点介绍稳定性相关的内容。
稳定性技术策略
什么是稳定性
对于大型微服务系统,在错综复杂的服务逻辑各种交互情景下,面对各种未知的条件变化,整体系统依旧能够正常平稳的提供服务,这便是稳定性。
影响稳定性的因素
系统稳定性影响因素非常多,举例来说:
1. 服务间的依赖:某个服务 BUG 造成其他依赖服务的不可用;
2. 业务逻辑变更:业务逻辑不断迭代演变,新老服务的不兼容;
3. 访问流量激增:流量突然增加,比如我们进行促销活动期间,导致服务压力过大 ,达到服务能力上限,从而导致服务崩溃;
4. 机器老化异常:任何机器和人一样,都有生老病死,随着长时间的运行,也会有磨损,因此某个机器故障,也是可能的异常。
还有其他各方面的因素,在这里就不进行穷尽了,大家可以思考一下,还有那些经典的影响因素。
稳定性的衡量标准
稳定性衡量标准一般用 N 个 9 来衡量。如表格所示:
名称级别年度停机时间描述2 个 999%87.6 小时基本可用3 个 999.9%8.8 小时较高可用性4 个 999.99%53 分钟技术容灾能力可用性5 个 999.999%5 分钟极高可用性
比如说某个系统网站全年稳定性达到 4 个 9 ,意味着全年服务不可用的时间小于等于 53 分钟。
稳定性技术策略
稳定性的技术策略,是我们本文介绍的重点,从大的方面来说,稳定性技术策略包含:监控,冗余,限流,降级,回滚,重试。
监控
监控是指对整个系统服务进行实时的监控操作,准确反馈系统运行状态,能够做到及时发现异常故障,记录详细日志与数据,提高故障发现,定位,解决的效率。从而提高系统服务整体稳定性。
监控是保证稳定性最基础工作。我们将重点介绍监控需要从几个方面考虑? 有哪些监控方向?
监控可以分为以下几类来进行:
流量监控
流量监控包括:PV、 UV、 IP,热门页面,用户响应时间。
这些基本的流量指标就不在这里向大家详细说明了,如果有不太清楚的同学可以自己搜索一下。
在流量监控这块,我们需要注意的是:流量毛刺。流量毛刺往往代表了系统某个风险点或者异常情况的发生。
一个正常业务的流量趋势具备周期一致性特征。比如说,一个业务每天的流量峰值一般在中午 12:00 和下午 18:00,那么这种峰值在没有特殊情况出现的前提下,应该会遵循该峰值时间规律。 那么流量毛刺是啥呢? 如下图所示:
从图中左侧部分可以看到,8 点钟有流量的突增,这时候我们需要确认为什么会有流量的突增。是业务的正常表现,还是有其他的异常流量进入。
从图中右侧部分可以看到,每条曲线代表了每一天的流量统计,红色曲线相对于其他几天的流量曲线在凌晨 3:00 和早上 8:00 的时候有明显的流量毛刺,这时候我们就需要确认是什么因素造成流量数据的突变。
通过对于流量毛刺的观察,能够让我们及时了解业务中的风险,及时做好预警与准备。
业务监控
业务监控是根据业务属性来定义监控指标,可以用于判定整体业务的运转是否正常。不同业务类型监控的指标肯定有所不同,比如电商场景、物流场景、游戏场景是完全不同的监控方向。
我们拿一个电商交易业务系统来举例,看看有哪些监控指标?大家可以参考着思考自己目前负责的业务指标。举例来说针对一个电商交易系统我们可以监控的业务指标有:
用户下单监控:秒/分/时 用户下单统计 ;
用户支付监控:秒/分/时 用户支付统计;
用户退款情况:秒/分/时 用户退款统计;
商品库存状态:库存消耗/剩余统计;
业务 GMV 监控:业务整体销售额统计;
商家在线状态:监控商家在线状态;
促销运营监控:监控促销金额与数量。
在业务监控中,还需要重点关注业务转化漏斗概念。流量漏斗可以看出业务转化率以及用户的访问深度。它是业务健康程度的监控,也是部分需求效果的衡量标准。
机器监控
机器监控需要关注的内容,应该是后台研发比较熟悉的部分。 主要监控方向包含下面几个部分:
当然在 linux 系统中,也有各种常用指令,来进行该类数据的收集:top、free、ping、iostat、netstat。
对于 Java 系统来说,需要进行 JVM 层面的监控内容,比如说:gc 的情况,线程创建销毁情况,full gc 情况,内存不同模块使用情况等。 JVM 同样也提供了指令集,方便我们进行信息的查找:jstat、jps、stack、jmap、jhat 等。
日志记录
在日志的打印过程中,我们需要注意日志的打印规范,日志打印内容应该包含对于问题排查有益的信息,并且日志格式清晰,可解析性高。日志一般是打印到服务器磁盘上面,但是由于单机磁盘能力有限,并且对于大型分布式系统来说需要整体收集不同服务器模块的日志,进行统一分析,提高问题排查效率。这时候就需要一个集中式的日志中心。
日志中心的核心能力一般包含:获取日志、存储日志、展示日志、分析日志、报警服务。
相对比较简单的实现方式可以采用ELK快速搭建自己的日志中心。
我们利用 kafka 将日志信息,进行异步广播,然后进行日志的解析,存储到 ES 检索系统中,利用 kibana 来进行日志的检索、查看等。进一步提升日志的有效管理。
冗余
冗余的对立面是单点。冗余可以有效的减少单点问题造成的影响。大家可以思考一下,如果一个系统服务只部署到一台机器,机器服务挂掉之后,意味着服务不可用,依赖于它的服务也会出现异常,最坏的情况可能会造成雪崩。
为了简化冗余的思考,我们将整个应用后台架构简化为四层架构,如下图所示:
最上层是用户访问,然后到反向代理,Nginx 为流量入口; 然后到站点应用,比如说咱们的 Tomcat 或者 Jetty 应用服务器; 最后是数据层 db;为了性能优化增加了 Cache 服务。
大家思考一下,如果这几层服务,全部的都是单点,单点就是我们只有一个机器去部署这些服务。Nginx 只有一台机器,Server 应用也只有一台机器。如果机器宕机会造成什么样的后果?会直接导致整个系统服务不可用,这个就是单点的风险。
完全依赖一个单点 没有任何冗余备份,导致服务的稳定性非常脆弱。
怎么去做冗余呢?
对于 Nginx 层与 Server 层,我们可以从下面几个方向考虑冗余:
多机部署:同一个机房内,部署多个服务节点,其中一台挂掉,还有同机房的冗余备份;
跨机房部署:不同机房内,部署多个服务几点,其中一个机房断电或者其他异常情况,还有其他机房来备份提供支持;
异地多活:如果所有机房都在同一个城市,就会造成地域单点,比如说城市光纤异常。这时候异地多活部署就会减少这部分影响。
对于数据层面比如 MySQL、Redis 都有自身提供的冗余方案。 MySQL 自身提供了主从部署,主从同步的机制,系统服务可以进行读主写从操作。
Redis 也类似,提供了集群冗余方案:主从配置,哨兵机制,集群模式。
Redis 集群模式能够实现数据分区冗余备份,主从同步,多主容灾,故障自动转移能力。
冗余的思路,在业务实现方向也可以落地考虑,比如说重要数据的多介质存储;重要组件的多版本选择等等。
限流
大型微服务架构中的任何服务节点,不管咱们怎么优化,怎么拓展,都会有能力上限。如果达到能力上线,系统服务奔溃的可能性增加,这种情况也很容易造成整个微服务应用的雪崩效应。
作为一个面向用户的网站,有时候我们会面对流量激增的情形,如果这时候达到了我们某个或者多个服务的能力上限,我们应该怎么保证系统的稳定性?
限流在这种情形下就起到了作用。
限流的目的
就是当服务器的压力剧增的情况下,为了保证服务不被拖垮,对一些流量采取拒绝或者降级的策略,以此来保证核心服务的正常运转。这是一种有损的技术手段。
将部分流量进行限制速率,控制输入和输出,将超过限制速率部分的流量,进行拒绝服务、或者排队等措施。以此来达到系统的自我保护目的。
限流策略:
限流策略有三种常用方式:
1. 总量计数限流:并发量或者访问量超过设定的阈值,将拒绝提供服务;
2. 漏桶限流算法:一个固定容量漏斗,任意速率流入或者生成并发或者访问,但会以一个固定的速率执行这些并发或者访问。如果超过固定容量的部分将被拒绝;
3. 令牌桶限流算法:一个固定容量的令牌通,令牌按照固定速率放到桶中,请求到来时候先去令牌通获取令牌,如果获取到则可以进入业务逻辑部分,获取不到则不会执行。
三者的区别:
限流的维度
如下图所示,限流思考从三个维度去思考:
限流的实现
1. 基于 AtomicLong 来实现单机(接口)总量计数法限流;
2. 基于 Semaphore 信号量来实现单机(接口)总量计数法限流;
3. 利用 Guava limiter 来实现令牌通的算法;
4. 对于分布式限流,我们可以考虑,利用 Redis 的 Incr 来进行实现。
降级
降级的目的
1. 削弱非核心服务资源占用;
2. 保证业务核心服务稳定性。
举例来说:
一个交易平台,有用户下单,支付等功能,同时也有用户评论,商品推荐,广告推荐等模块。在促销活动期间,用户大批量的进入,那么这时候由于功能模块非常多,流量或者机器资源消耗非常大。造成系统整体负载过高。那么很可能出现一个可怕的情况,用户没办法进行正常交易。
面对这种情况,我们应该怎么做?这时候降级就体现了它的实用价值。
降级策略
降级策略有很多方面需要思考与落地,在这里总结一下经常用到降级策略。
页面降级:非核心页面模块,占用紧张资源,关停该页面,减少访问;
服务降级:将功能分类,分为核心服务与非核心服务,将非核心服务进行关停;
依赖降级:将依赖服务梳理分类,保证核心依赖的稳定,将非核心进行降级关停;
读写降级:将直接读写数据库切换为读写缓存; 对于缓存依旧可以进行备份冗余;
延迟降级:页面的异步加载策略; 数据写入异步消息队列等。
降级实现
降级就需要一个分布式开关,通过开关来确定降级策略的启动与否。 分布式开关的实现方式,我们也简述一下:
基于 Redis 实现;
基于 ZK 来实现;
基于内部数据库来实现。
每个具体的实现方式大家如果感兴趣可以查阅一下资料,基本上都是功能实现相对简单。
合理降级了解了降级方法之后, 我们还需要清楚不是所有服务都能降级,也不是等到故障发生了以后,才去选择或者确定哪些服务可以降级。故障的降级策略一定是要提前去规划与思考。
系统或者服务需要分为核心系统和非核心系统(核心服务和非核心服务)来区分。核心服务是我们力保不能有任何问题的主流程服务 P0; 非核心服务,又可以根据重要性进行再次分层。可以划分为 P1、P2、P3 服务。
P0 服务网为主服务,是力保稳定性的对象,他们挂了整个业务也就崩溃;
P1 服务为与紧密主服务相关,但可以后续异步补偿来操作的服务,比如说,结算流水,订单统计;
P2 服务与主服务有点相关,但关闭了对主服务任何业务逻辑没有影响,比如说,订单评价,商品推荐等;
P3 服务于主服务没有相关,关闭之后对主服务没有任何影响 比如说,广告推荐,用户喜好,评论浏览等。
在梳理服务等级的时候,需要注意 2-8 原则 也就是 20% 的系统为核心系统,80% 的系统为非核心服务。
回滚
根据经验,线上的大部分 BUG 都是由于新需求或者新的工程改动造成的。
那么当系统出现 BUG 或者不稳定的时候,考虑到寻找或者排查问题耗时会比较久,我们一般都会选择先回滚然后再去寻找问题具体原因。这种方式在一定程度上保证了系统的稳定性状态。
回滚定义:快速恢复到变化之前的状态,让程序或者服务恢复到改动之前的稳定状态。回滚目的:及时止损,减少线上问题排查付出的代价回滚影响:新改动的需求会延迟生效
那么我们回滚的方向又有哪些呢?
回滚方向:
1. 代码版本,也就是新旧代码的转换;
2. 系统服务,就是讲新上线部署的功能给予回滚操作,让服务恢复到新上线前的状态;
3. 数据内容,将修改的数据内容重新修改为历史数据版本。
怎么才能科学的回滚?
如果想把回滚的工作做好,需要处理好下面的主要内容:
发布信息规范:每次发布包,都有唯一的版本号;命名一定要规范。包含主要内容。 举例:工程名称 - 模块名称 - 代码版本 - 环境类型 - 日期版本 .jar(war)
代码管理科学:代码分支管理科学、 代码 review 机制、工程结构统一化。
代码 review 时机:
大版本(需求)代码必须 review;
线上 case 修复,代码必须 review;
测试前代码 review (保证测试质量);
周期固定时间,形成团队习惯。
数据管理规范:避免线上库直接操作、任何变更必须有回滚脚本、线下验证 。
注意事项:
尽量避免线上数据库手动操作;
若手动,需要执行详细规范;
不断设计减少手动操作频次。
工程上线规范:上线窗口避免流量高峰,灰度验证避免全量上线,及时验证回归测试,上线通告。
重试
重试目的
配合超时机制,正确的获取结果,同时保护系统资源服务;
利用多次访问策略,减少外部抖动对系统结果造成的影响;
借助幂等概念,保证信息提交成功,达到分布式一致性。
重试的场景可以有 异常重试,超时重试。
异常重试:我们访问某个依赖接口的时候,如果出现接口返回异常的情况,我们可以进行访问重试,从而获取正确结果。
超时重试:接口在规定的超时时间内没有得到相应的结果数据,进行重试操作。
全局思考重试策略
如何进行合理的超时重试策略设定,需要结合业务特点来进行详细的规划与测试。如果设定不好,很可能造成线上问题。
超时时间过长,可能导致服务阻塞; 超时时间太短,可能导致服务调用成功率降低。如果成功率降低,可能就会导致重试的概率加大。 重试必然会导致新的请求发生,增加一次访问时间,可能在用户体验上存在影响。
如果不断的重试,很可能导致不断的新建访问线程,重复请求,导致三方接口的压力。
所以超时时间 重试策略 都需要根绝我们业务特点进行验证与设计,避免上面介绍问题的出现。
峰值应对策略
当我们业务系统需要进行运营促销活动的时候,或者面临特殊日期将要给网站带来高于平时流量的时候,我们需要做好应对流量峰值的准备。我们需要系统的思考如何在系统峰值时刻保证我们大型微服务系统的稳定性。
我们将峰值应对按照时间维度进行划分:事前,事中,事后。
事前
前期准备
确定模块:确定本次峰值影响的工程或者服务,梳理一定要完整;
团队组建:根据影响模块,确定本次维稳参与同学(注意跨团队),确认分工;
合作约定:周期性的沟通,比如周会、约会、事前会议、事后总结会议。
数据预估
容量预估方向:
数据预估的时候不仅仅要考虑峰值的应对时候的容量冗余,同时也要考虑数据长期增量的准备。
系统压测
系统压测的维度由小到达来说:
单接口压测:接口维度的压测,人工根据接口模型进行压测数据;我们可以使用 Apach ab、Http load 来进行。
单机初级压测:针对机器维度来进行整体压测,可以人工构造数据也可以线上访问流量的复制来构造压测数据。我们可以使用 Jemeter、LoadRunner、tcp dump 等相关工具来进行。
单机负载均衡压测:也是针对单机维度来进行压测,与初级压测不同的是,根据负责均衡,将线上流量进行实时转发,将流量比例向压测机器倾斜,从而达到压测的目的。
全链路压测:全业务后台服务整体压测,复制线上真实流量,进行压测数据的改造,然后高并发的访问业务系统,提前相对真实的模拟峰值到来的情形。
全链路压测是最困难,涉及面最广的一种压测方式。同时也是最能发现系统瓶颈的一种方式,全链路压测的挑战有:
困难1:链路梳理复杂度;
困难2:多模块,多服务,多团队协同;
困难3:寻找短板,避免系统雪崩风险;
困难4:压测数据准备,脏数据处理;
困难5:压测统筹安排,数据采样对比。
全链路压测的流程如图所示:
容灾演练
目的
主动触发异常,熟悉异常处理流程;
验证故障处理规范,不断完善规范;
验证服务异常状态,验证报警机制。
步骤:
目前内容,评估影响,方案确认;
制定故障 SOP 手册,详细到每一步执行;
根据 SOP 进行问题解决执行操作;
记录故障数据与现象,监控报警确认;
故障分类:可以快速解决,需要外部支持。
容灾演练的 SOP 建设:
服务巡检
在峰值到达之前的一段时间里,我们需要对系统服务整体巡检,确保的我们的各项指标都能正常工作,及时发现可能存在的风向。
定事:
1. 对业务、流量、系统、机器、日志 进行数据指标的 check ;
2. 确保主要数据无遗漏,不重复,没错误。
定人: 专人专事,责任明确,分工合理,推进日常巡检工作 。
定时:
1. 根据业务特点周期性的进行服务巡检,比如每天一次,每周一次;
2. 根据业务特点,合理的安排巡检的时间,比如说中午、下午。
定方案: 根据巡检出现的异常数据或者不合理数据,进行解决方案的制定 方案必须可执行同时有完成时间点。
事中
峰值应对值班
在面对峰值期间,我们需要保证团队资源的稳定,需要安排核心人员进行值班操作。值班过程中需要做一下工作:
服务观察:业务数据,服务监控,日志报警,趋势检测; **熟悉 SOP **:容灾操作 SOP ,值班 SOP ,同步 SOP;组织形式:站会,日报,总结。
数据观察同步
为了让团队其他人或者合作团队了解当前的系统运行情况,我们需要在固定的时间里同步相关数据,及时检查数据走向与趋势。
每天固定时间进行数据同步(尽量选择峰值时间段);
数据同步对象包含:业务、PM、QA、RD 多个团队;
统计指标注意跨团队的理解程度(可理解的形式来描述);
对比属性,比如说目前峰值是上次活动峰值的多少倍。
线上紧急情况处理
处理准则:
这里特别重要的一点准则就是,快速止损是第一要务,问题排查以及解决是止损之后的动作。
这时候在快速恢复线上服务的时候,就能考察我们前期的容灾演练的效果了。
上面图形展示的操作规范都应该在容灾 SOP 建设中覆盖到。
事后
1. 所有报警异常的梳理与解决,所有不规范性的讨论与优化;
2. 根据真实的场景进行 SOP 的优化,这个 SOP 可能包含咱们的值班 SOP 以及容灾演练 SOP 的建设;
3. 复盘讨论,需要根据业务数据、流量数据、系统服务情况统一来进行复盘整理。复盘的边界,不仅仅是应用后台,还应该也包含前端研发、SRE、运营产品、中间件平台等。
复盘讨论的内容一般包含:
应用后台: 峰值期间业务数据、服务性能数据、整体稳定性数据;
前端研发:APP crash 率、性能表现情况、DAU、各页面转化率;
SRE 运维:机房整体情况、机器负载情况、网络宽带情况、资源利用率等;
运营产品:业务指标完成度、同比(环比)情况、未来规划;
中间件平台:中间件峰值稳定性情况、容量情况、服务能力情况。
性能优化策略
性能优化重要性:
用户角度:网站体验重要衡量标准;
系统角度:稳定性的基本要求保障;
研发角度:自身技术能力的竞争力。
在这里由于篇幅有限,我们不针对每一块优化的技术策略进行详细的讲解,我们重点介绍一下技术优化的整体方向与策略,以及如何选择方案。
我们在考虑网站性能优化方案选择的时候,从收益与投入两个方向综合考虑。
应用技术栈对于后台研发同学来说是相对比较熟悉的技术内容,所以投入会相对较少,收益却会很大。因为大部分性能优化的问题,还都在于代码与技术方案层面。
数据库方面也是需要重点投入的方向,能够避免业务数据获取以及存储方面的瓶颈。
其他的三个方向网络优化,容器组件,底层环境都不是业务后台研发同学经常学习的方向。所以这几块优化方向可以考虑和公司的运维同学进行共同思考与建设。
当然性能优化是一个长期重复执行的动作,需要进行不断的总结,梳理出来符合自己业务的性能优化策略。
总结
本文介绍了大型微服务架构后台应用系统稳定性技术策略的介绍。每个技术点都需要我们持续的思考与落地,全面整体的思考稳定性相关的建设动作。与此同时,还进行峰值应对过程中稳定性的保障工作如何开展,希望对大家有所帮助。