10

超时类告警降低99%,携程微服务治理与优化实践 - 架构 - dbaplus社群:围绕Data、Bloc...

 1 year ago
source link: https://dbaplus.cn/news-141-5260-1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

超时类告警降低99%,携程微服务治理与优化实践

HongLiang 2023-05-06 10:02:01
作者介绍

HongLiang,携程高级技术专家,专注系统性能、稳定性、承载能力和交易质量,在技术架构演进、高并发等领域有丰富的实践经验。

微服务架构在中大型互联网公司中被广泛应用,随着业务的发展,应用数越来越多、调用关系也越来越复杂。中台化后,交易系统要支持业务线多,系统复杂性高,原系统虽然能支撑业务量的持续增长,但在稳定性、吞吐力和资源利用率上面,还存在优化空间。

分享的目的

本文站在业务开发角度介绍开发在微服务架构下遇到的相关问题(微服务架构的优缺点这里不再赘述),以门票活动预订流程查询引擎为例,分享微服务治理的实战经验,希望能给遇到同样问题的同学提供一些借鉴思路。

如下图所示,蓝色部分为本文的重点:

图片

图1 微服务架构关注点

在微服务治理之前,我们先简单了解一下微服务历史和陷阱。

二、微服务简史

微服务概念在2005年被提出,2011年用来代表架构风格。

定义:微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成,每个微服务仅关注于完成一件任务。

1、微服务与SOA的关系

大家在使用SOA (Service-Oriented Architecture)的时候往往分不清和微服务有什么关系,总结如下:

  • 微服务是SOA的实现方式

  • 微服务是去掉ESB后的SOA

  • 微服务是一种和SOA相似但是两种不同的架构设计风格

图片

图2 微服务与SOA的关系

了解微服务与SOA关系后,再对比一下功能差异:

图片

表1 微服务与SOA对比

2、携程SOA从1.0到 2.0演进
图片

图3 携程SOA演进

携程微服务:携程SOA2.0是微服务架构,推荐单机、单应用、单服务。

三、微服务的陷阱

微服务这个话术会将关注点错误的聚焦在“微”上,大家会误以为服务越小越好,实际上大小并不是第一考虑因素。接下来我们来看看开发微服务应用的时候容易踩到的陷阱。

下图可以看出,服务拆分越细,调用关系越复杂。

调用链路理论上有 n * (n-1) 条:

图片

图4 服务粒度越细调用关系越复杂

应用粒度拆分过细容易带来以下几个问题:

1、重复调用

调用路径 C - >D ->E 和 C ->E, 对于E的一次请求,可能会被调用了多次。

图片

图5 一次请求中服务E被重复调用

2、循环依赖

一条链路出问题,导致其他链路故障。当服务B1或B2 性能变差时,最终导致链路A/B都会被影响,严重情况下导致宕机。

图片

图6 循环依赖

3、链路太长

服务层级过深,一次请求链路太长会导致性能下降,每层网络延时和序列化反序列化时间都有性能损失,层级越深,下游性能越差。

链路太长,定位问题困难(效率低),当服务F出现故障时,下游A~E 应用 owner 需要排查原因。

图片

图7 链路越长,性能损失越大

以上这些问题,在日常开发中容易遇到,下面我们看看怎么解决这些问题。

四、微服务治理

从下图中可以看到应用之间调用关系复杂,并且有严重的循环依赖问题。

图片

图8 应用调用关系图(双黄线表示循环依赖)

循环依赖是微服务里面容易忽视的问题,系统稳定的情况下不会出现问题,由于某些原因,当系统从稳定变成非稳定状态时,循环依赖容易导致更严重的故障。我们先看1个生产案例:

案例:发布过程中下游超时,订单下跌

刚接入流量的机器因线程初始化、类加载锁、JIT等会产生慢请求。

图片

图9 发布过程中的慢请求

当流量接入时,请求在刚拉入的机器中多次来回调用,因多次慢请求叠加,导致接口越来越慢,机器资源耗尽,一台一台被拖垮,最终整个服务不可用,产生雪崩(如下图)。

图片

图10 发布过程中循环依赖导致应用雪崩

当然如果应用间循环依赖QPS很小,例如单机QPS在10以内,少量慢请求无法将资源耗尽,一般不导致故障,但是这种“坏味道”会给系统埋下隐患,严重的时候会演变为接口级的循环依赖,导致死循环,并且这种死循环可能在测试环境由于命中缓存没有被发现,发布到生产后有些缓存穿透的请求就会导致循环调用,直到超时。

如果单机QPS上百,产生的慢请求短时间内耗尽资源,阻塞后续请求,导致性能下降,产生故障。

故障恢复期间,由于调用关系复杂,分不清上下游关系,无法根据调用关系来限流,导致定位困难,恢复时间长。

上述案例主要是由循环依赖引起,像一颗炸弹,为系统埋下隐患。

除了循环依赖,还有下面几类问题可以优化:

  • 层级太深:

  • 透传字段要改多个应用,需求迭代效率低

  • 每层网络延时、序列化和反序列化都有性能损失,导致终端体验差

  • 重复缓存:同一个DB不同应用重复构建缓存

  • 重复调用,直接调用或者间接调用,末尾服务压力大

  • 离线任务峰值波动太大

  • 未隔离:核心、非核心流量未隔离

  • 效能低:人均应用多/资源使用率低

针对上面的几类问题,我们制定了微服务治理目标、原则和治理策略。

 1、治理目标

1)稳定:故障隔离,提升系统稳定性

2)交付:独立迭代、独立扩展、快速交付

  • 横向拆分:减少耦合,独立迭代。

  • 纵向拆分:减少应用层级,提高开发效率,缩短交付周期。

3)重用:相同功能复用

不同系统重复功能复用,减少重复开发,提升一致性。

 2、治理原则

1)避免跨团队维护一套代码。

2)服务粒度要与团队规模匹配,人均应用数在3个以内。

根据历史经验,一个人在超过3个应用之间来回切换开发,开发效率会降低,日常处理告警繁琐,业务和性能优化也无法聚焦。

3)应用分层:上一层可以依赖任意下一层级(不可反向依赖)。

4)层级深度:垂直域/小组内,应用层级控制在5层以内。

这里的“5层”是我们根据实际业务实际情况来定的。一个垂直域/小组内应用层级超过5层,一个需求上下游依赖太多,开发效率会降低。

 3、构建原则

1)业务领域拆分:单一职责,业务建模(对人员要求高)

2)数据存储:独立的数据读写API

3)复用性:功能复用(比如基础数据提供能力,提供给不同小组使用)

  • 核心与非核心隔离

4)稳定规则与易变动规则隔离

5)快速失败:设置合理的熔断规则

6)异步通信:将与此次请求无关的操作/调用异步化

4、治理策略

1)去除循环依赖

问题:服务B和服务C 循环依赖

  • 应用分层与定位:第一步划分应用层级(分层工具有传统三层架构、泛领域分层等),将应用定位划分到不同的层级。

  • 确认依赖关系:每一层内如果有多个应用,确认上下游关系。这个根据业务场景来,根据父子关系,包含关系,依赖关系,确认每一层内的依赖关系和应用职责。

图片

图11 循环依赖治理

2)缩短调用链路

问题:服务BCD 链路太长(垂直域/小组内)

  • 领域细分:将粗粒度的应用按照业务领域垂直划分,不同层级负责不同的职责,让系统更独立。

  • 减少透传:每个层级职责清晰,减少不必要的透传,让开发效率更高。

图片

图12 缩短调用链路

3)复用性

问题:服务BCD 对相同数据重复缓存(存在一致性问题)

  • 下沉基础服务,提供基础数据:将相同的功能下沉为基础服务,例如:基础数据服务提供缓存,翻译等功能。避免不同的使用方重复缓存,重复接入翻译。

图片

图13 重复功能下沉

效果:下沉基础数据服务,统一缓存,翻译等功能,提供给不同的开发组使用。

4)流量治理

 ① 重复调用

问题:一次请求,服务C同一个接口被重复调用

功能内聚:将同一个功能对下游的依赖放到同一个服务内调用。由于系统自身迭代导致的不合理调用,可以按照上述方法优化。如果为了解耦将功能拆开,可以根据实际情况评估影响和收益。

图片

图14 功能内聚合并重复调用

效果:功能内聚,多次调用合并为一次调用。

 ② 降低调用量

问题:一个服务中,不同的接口功能拆分太细,下游使用的时候都需要调用多个接口组装结果。例如:一次请求服务B的a、b、c、d、e接口都被调用,下游为实现一个功能,需要调用太多小接口。

合并服务B中同一领域功能:将相同的功能合并到一个接口,减少调用量。

一个接口提供模块参数,按需调用:

  • 支持按需使用,对不同业务场景非必须的功能,提供模块参数,按需传参。

  • 对于独立的前端页面接口,对外透明,内部封装对应场景需要的模块参数,例如前端首屏请求。

图片

图15 请求合并

效果:聚合相同功能,合并小接口,多次调用合并为一次调用。

 ③流量隔离

问题:非核心流量(例如:Job调度)大于用户流量

流量隔离:一套代码,隔离部署,将核心和非核心流量隔离。核心流量承载用户请求,保证交易的稳定性,非核心流量承载离线任务调度和非核心场景调用。

图片

图16 流量隔离

效果:总成本不变,核心链路稳定性得到提升,非核心链路CPU使用率得到提升。

 ④离线调度流量消峰

问题:单位时间内调度过于集中(Job)

合理的延长调度时间:适当延迟调度时间,降低每分钟的调用峰值,让每分钟内调用量更加平稳。

图片

图17 离线调度流量消峰

效果:调度总时间在可接受范围内,调度时间拉长,单位时间内调用总量降低,降低服务端峰值压力。

问题:每秒内调度不均衡(Job),导致服务稳定性差或为了能承载请求需要冗余更多服务器资源。

图片

图18 客户端调度QPS不均衡

客户端削峰填谷:调度波动太大,会导致请求到了服务端被限流或者服务端扩缩容。对于调度不均衡的离线任务,我们在客户端控制每秒内发送的请求量,让每秒内请求更加平稳,任务调度总时间不变。

图片

图19 客户端调度从不均衡变为均衡

效果:分钟内总的调用量不变,服务端调用量从波动变为平稳。

 ⑤降低人均应用数/提升CPU使用率



  • 人均应用过多,开发效率降低

  • CPU使用率6%以下应用数占比超过50% 且总核数占比超过30%



  • 短期:缩容,将单边服务器数缩容到SRE标准最小配置。

  • 长期:合并拆分过细的应用,参考历史、现状和将来的规划,将拆分过细、CPU使用率长期小于6%的应用做合并。

图片

图20 应用合并

五、实施效果

1、循环依赖(应用分层,解除应用间循环依赖)

  • 去掉65条循环依赖链路,消除雪崩的风险

  • 超时类告警降低99%

  • 排障效率提升至分钟级别

2、链路长(减少应用层级):调用链深度缩短 40%

3、复用性(下沉基础数据服务,减少重复功能)

  • 新增基础数据服务,缓存统一,解决一致性问题

  • 缓存容量减少60%

4、流量治理(降低水位线)

  • 重复调用:功能内聚,去除重复调用

  • 调用量大:合并小接口、消除调用峰值;离线任务削峰填谷,降低峰值调用量

  • 核心应用调用量减少73%,核心系统峰值降低50%

5、开发效率(解耦&减少中间层)

  • 水平拆分独立功能,减少耦合,独立开发

  • 垂直领域减少3层,开发效率提升

6、查询引擎性能提升65%,QPS从8w提升至24w

  • 减少了系统不稳定导致的服务变慢

  • 领域划分,垂直优化系统,专注用户端到底层的优化

7、人均应用:人均应用数控制在2个以内

8、资源使用率(应用合并,提升CPU使用率)

  • 40+个应用CPU使用率(加权平均)从18%提升至32%

  • 治理前后查询引擎链路对比:

图片

图21 门票活动查询引擎微服务治理前后对比

微服务架构下服务拆分越细,调用关系越复杂,层级越深,性能损耗越大,开发效率越低(垂直域/小组内),所以服务不是越小越好,而是“合适的大小”。

在构建微服务的时候,要根据业务体量、团队规模、成本等因素综合考虑,按照合理的原则,构建出适合的大小,以达到预期的目标。

服务治理是一个长期的过程,制定目标持续优化,让系统更快更稳定,为业务赋能。

作者丨HongLiang 来源丨公众号:携程技术(ID:ctriptech) dbaplus社群欢迎广大技术人员投稿,投稿邮箱:[email protected]

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK