提兵百万西湖上,立马吴山第一峰!这篇文章主要讲述百度商业托管页系统高可用建设方法和实践相关的知识,希望能为你提供帮助。
文章图片
全文7986字,预计阅读时间19分钟。
一、背景介绍百度商业托管:是百度为了实现营销新生态的建设,以高效连接和投放优化为目标,为商业客户提供一站式的的运营阵地,连接服务和消费者,是百度从流量运营到用户运营的重要转变。代表的产品有基木鱼、度小店等建站和电商平台。
随着托管页的业务不断发展,系统的规模和业务复杂度不断增加,系统的可用性面临巨大挑战,本文从可用性建设的方法到实践,深入分析稳定性建设的思路,从规范、监控、冗余、降级、预案等多方面实现系统的高可用。
「可用性指标定义:」 对于系统而言,最理想的情况是系统能提供24小时不间断的提供服务、但由于软件系统的复杂度高,尤其在分布式系统环境中经常会由于系统BUG、软硬件异常、容量不足等导致系统无法提供100%的可用性,因此通常采用N个9来评估系统可用性,此指标也作为一些基础服务的SLA的标准。
文章图片
二、可用性整体建设思路
文章图片
系统的高可用建设是一件庞大的工程、需要从不同维度去综合考虑,整体建设思路可以围绕系统故障发生的时间、范围、频率,处理速度等方面来综合考虑。
2.1 故障发现早从故障的发生时间来看,在用户或客户反馈问题之前,研发人员能够第一时间发现问题是非常重要的,每一次故障发生之后我们都会深入思考一个问题,能不能更早的发现问题,我们有哪些常用的手段和方法,下面就逐一介绍下。
2.1.1 故障发现早-规范化:
- 「日志规范化」规范化的核心思想是通过一定约束来保证整体系统能够协调统一,托管内部的服务是基于统一的微服务框架构建,但由于各个系统和模块的日志千差万别,在开发、测试和运维阶段带来较高的成本。日志规范主要是针对开发过程中关键业务信息的记录,高效的定位问题;在QA测试阶段进行问题排查;在数据统计分析提供有效指导手册。
- 「全局通用规范」:
- 全局上下文采用统一的MDC实现,用中括号和空格分割。
- 所有的logger均需设置addtivity=false,禁止重复打印。
- msg信息需要简明、易懂。
- 相关日志禁止重复打印到console.log中。
- 打印日志使用slf4j门面。
- 「TRACE」 调试详细信息,线上禁止开启。
- 「DEBUG」 开发调试日志,线上禁止开启。
- 「WARN」 警告日志 日志常用来表示系统模块发生问题,但并不影响系统运行。
- 「INFO」 信息记录 日志级别主要用于记录系统运行状态等关联信息。
- 「ERROR」 错误信息输出 此信息输出后,主体系统核心模块正常工作,需要修复才能正常工作。
![图片](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/75533b7134ee435baa9b050a677035b1~tplv-k3u1fbpfcp-zoom-1.image)
- 「logPattern」
<
property name="ENCODER_PATTERN" value="https://www.songbingjia.com/android/%d{yyyy-MM-dd HH:mm:ss.fff} [%thread] [%X{reqid}] [%X{ip}] [%X{baiduid}] [%X{cuid}] %-5level %logger{5}: %msg%n"/>
- 「关键日志的格式要求」此处涉及的细则规范较多,不一一列举,主要涉及到贯穿日志的核心上下文,需要包含来源ip,请求路径,状态码,耗时等。
- 「报警规范化」报警规范化主要针对错误日志的报警监控,做到报警的分级监控、定义了分级监控的监控项目名称的定义。针对不同级别的报警,采用不同的采集任务和监控策略,并定义配套的跟进流程。
- 通用服务的性能监控报警。
- 强依赖的性能监控报警。
- 服务异常状态码监控报警。
- 第三方服务耗时监控报警。
文章图片
- 「值班规范化」针对值班同学的通报、止损、定位、解决等核心规范和流程,保证线上的问题能够第一时间处理和解决。
2.1.2 故障发现早-系统监控
系统监控主要是从问题感知到问题定位的全面能力的建设。前面提到的日志规范化整改是实现自动化问题感知的前提,当系统日志规范完成之后,我们就可以通过一些自动化的方式来建设统一的监控。在问题感知层面主要包含业务指标、业务功能、系统稳定性、数据的正确性、时效性等。
文章图片
- 「问题感知:」业务指标是指系统关注的核心业务指标,主要是通过实时数据采集的方式能够发现业务指标的变化,能够实时监控到系统问题对业务的影响范围。
业务功能是指针对核心的业务功能分场景的自动化测试和监控能力。
系统稳定性会从多个维度去衡量。会从网关入口来衡量可用性、会从模块自身来看可用性、以及从依赖的第三方的稳定性来衡量系统的稳定性。
数据一致性校验本质上是一种离线或近线的对账场景,对于分布式的微服务来说,绝大部分都是采用补偿来实现最终一致性,因此数据的对账就显得尤为重要。
- 「问题定位:」问题定位主要是结合一些核心业务场景,建设一些异常指标的报警和监控,其中包括流量异常、平响异常、pvlost等。在数据正确性和时效性上面来看,包括数据延时、数据不一致等。
2.1.3 故障发现早-容量评估
容量评估是提前发现系统容量问题的有效手段,尤其是当有一些特定的业务场景的时候,需要工程师或者架构师进行系统的容量评估来判断系统是否需要扩容等操作,在这里需要我们提前做很多准备,常见的容量评估的方式有静态分析和动态评估两种手段。
静态分析是指通过分析现有系统的依赖拓扑,结合在当前流量的情况下,通过理论计算出系统能否承受的最大流量的负载能力和系统瓶颈。静态分析只能提供一种预估的结果,不一定客观和准确。动态评估是指针对线上的服务进行模拟压测,通过系统的实际情况来评估容量情况,此种方式相对客观准确,但线上的全链路压测会有一定风险,而且容易对业务数据带来污染。因此实际在做容量评估的时候可以采用静态分析+动态评估两者结合的方式来进行。
文章图片
- 「动态评估相关注意事项:」
- 尽量模拟线上真实的流量(流量回放)来进行线上压测,因为不同的分支逻辑可能带来的系统负载不同,例如如果针对某一个相同请求和参数进行压测,极可能命中cache,则会导致压测结果不置信。
- 动态评估之前需要通过静态分析排查可能对业务带来的影响,需要增加相关的开关避免对用户带来干扰,例如:针对下单行为给用户或者商家发短信等。
- 动态评估可能会对线上系统带来影响,因此要在流量低峰期进行,并且能够做到快速启停。
- 动态评估需要业务系统配合做数据打标和清理,避免脏数据对线上业务的影响。
2.2.1 故障范围小-存储隔离
系统建设初期,为了提升研发效率和节省资源,很多业务都是共用存储的。随着业务的发展,经常会出现以下问题
- A业务的慢sql导致整个集群变慢。影响了B、C、D业务。
- B业务的大表的添加字段,导致主从延时,影响了A、C、D业务。
- C业务线下离线统计分析导致从库CPU100%,影响了A、B、D业务。
文章图片
其中新集群的容量评估、资源申请、以及切换过程中的双写同步都是非常重要的流程和步骤,双写后业务要及时校验数据的准确性。关于其他redis等其他的存储隔离的思路和方法与上述一致。
2.2.2 故障范围小-服务隔离
- 「服务隔离」服务隔离一种方式是从业务视角去看的,此处涉及到微服务的拆分的原则,一般方法和原则如下:
- 将容易变化的,频繁变更的部分隔离出来服务。
- 将高并发等级高的应用与低等级的应用隔离出来。
- 按照组织架构划分将服务进行拆分和隔离(康威定律|垂直拆分)。
- 沉淀底层通用的基础信息和服务,保证通用性(水平拆分)。
此处的冗余更多的是指接入层和服务的冗余,对于无状态的服务冗余是很容易实现的,但是对于有状态的基础组件和存储服务做多地域冗余成本是很高的,可以分场景去实现,例如对数据一致性要求不高的查询场景,可以采用存储的多地域部署,但是对一致性要求很高的,需要考虑set化来实现,具体可参考阿里的三地五中心架构。
- 「老旧服务清理」由于系统不断变更和迭代,不断会有一些技术项目对现有的系统进行重构或者重写,对于多个版本的接口或者系统并存的情况在互联网公司并不罕见。尤其是对一些底层的基础服务,此处需要程序员或者架构师有高度的敏感度和责任心,对于一些技术的尾巴要及时清理,来保证系统的高可用。
- 对于基础服务的提供方,涉及到老旧版本的升级,需要及时推动上游系统进行升级。
- 对于依赖一些无人维护的老旧服务,需要重新梳理服务依赖拓扑,进行优化替代。
- 对于依赖的基础组件、需要及时进行评估和更新上线,尤其涉及到一些安全问题,性能问题等。
2.2.3 故障范围小-权限隔离
系统故障大多数都是由于变更导致的,在变更管控上重要的一点就是要做到权限隔离,服务发布和上线的权限隔离,此处需要依托于容器化管理平台的能力,但是团队内部需要及时清理相关权限。避免不相关人员误操作导致线上风险。
- 线上数据库的读写权限隔离,IP授权的管控。
- 线上服务的发布和部署权限隔离,分级发布的审核人员名单管控。
- 代码库的权限隔离,保证CR的质量。
- 对于服务的入口层以及管理权限的隔离。
文章图片
失效时间是指上一次设备恢复正常状态(图中的up time)起,到设备此次失效那一刻(图中的down time)之间间隔的时间。可以将MTBF用如下的数学式表达:
文章图片
我们面临的是各种复杂的网络环境,故障频率是衡量我们系统自我保护能力的重要指标,接下来介绍下常见的方法和实践。
2.3.1 故障频率低-服务限流每个系统都有自己的最大承受能力,即在达到某个临界点之前,系统都可以正常提供服务。为了保证系统在面临大量瞬时流量的同时仍然可以对外提供服务,我们就需要采取流控。尤其是针对一些底层基础服务或者被较多应用依赖的业务服务。限流算法常见的有记数法(固定窗口和滑动窗口)令牌桶和漏桶算法。
- 「常见限流算法」
- 令牌桶算法:在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。
文章图片
- 漏桶算法:漏桶算法这个名字就很形象,算法内部有一个容器,类似生活用到的漏斗,当请求进来时,相当于水倒入漏斗,然后从下端小口慢慢匀速的流出。不管上面流量多大,下面流出的速度始终保持不变。
文章图片
- 「限流方式」:从托管页系统来看,主要是两大类服务的限流,一种是直接面向用户的web服务或者api,这种通常情况下都会有网关层,例如百度有自己的BFE平台,可以很方便实现限流规则的配置。另一种是RPC服务,这种需要自己来实现限流,目前比较流行的限流方式是RateLimiter - resilience4j(基于令牌桶实现),能够跟Springboot很好的集成,具体实现和使用方法可参考https://resilience4j.readme.io/docs/ratelimiter
- 限流的难点是如何评估合理的阈值,通常要结合线上的实际情况,和动态压测结果来准确评估。
- 由于我们的服务会提供多个API,需要针对服务进行全局的限流配置以及核心重要API的限流配置,优先级是全局>
局部。
- 为保证限流操作的及时性,系统需要支持动态修改配置。
2.3.2 故障频率低-降级熔断分布式系统的熔断就像家用电路的保险丝一样,当系统超过承载的阈值时,会自动熔断,起到系统保护的作用。尤其在微服务发展迅猛的今天,服务依赖的拓扑越来越复杂,架构师都很难画出来所有的服务依赖拓扑,当出现某一个服务不可用但是没有相应的熔断措施的话,极可能出现雪崩,这种灾难性的故障需要我们通过合理的熔断和降级来保证的。
- 「强弱依赖梳理」
要做好熔断降级的前提是要梳理好强弱依赖,此处的强弱依赖梳理主要从对业务的影响来评估,例如下单操作,对于商品的库存服务就是强依赖,因为要保证数据的一致性。此处不可降级。但是在商品详情页展示的价格依赖营销算价服务,此处可以定义成弱依赖,因为就算价格服务不可用,商品可以按照原价展示。降级一般来说对业务都有影响,我们核心要做的是降级后预期是什么,会对哪些业务产生影响。
- 「框架选择」熔断降级框架目前比较常用的是 https://resilience4j.readme.io/docs/circuitbreaker。这是一款轻量级的断路器框架(6w行代码),使用简单。(Hystrix停止更新,转入维护模式),同样比较常见的是 Sentinel,网上对比文章较多,此处不再赘述。
文章图片
与Hystrix相同,Resilience4j熔断器也存在三种状态,即关闭状态(CLOSED)、半开启状态(HALF_OPEN)和开启状态(OPEN),但除此之外,Resilience4j还有两个特殊的状态,不可用状态(DISABLED)和强制开启状态(FORCED_OPEN)
Resilience4j使用ring bit buffer 这种数据结构来存储被保护方法的调用结果。一次成功调用,存储1,一次失败调用存储0。ring bit buffer是一个类似bitset的数据结构,其底层是一个long型的数组,仅需16个元素就可以存储1024次调用的结果。
2.3.3 故障频率低-超时设置超时和重试设置的不合理同样会带来系统故障,托管系统针对超时、容错、池化、等进行了全盘的梳理和整改。主要集中在如下方面
- 「合理的超时设置」
- RPC依赖和HTTP依赖均应设置合理的超时时间,可根据依赖服务线上99分位值,增加30%-50%的buffer。
- 许多框架都有默认的超时时间,需要酌情调整。例如redis连接池默认读写和连接超时为2000ms,okhttp的连接池默认为10s,hikari连接超时默认为30s. 很多默认的超时连接对于并发高的服务和应用都不太合理,需要结合业务场景综合考虑。
- 「容错机制」
- 对于读操作可以选择failover的容错策略,重试次数<
=2次。
- 对于写操作的重试需要酌情考虑,要充分考虑下游服务是否能保证幂等性,为风险起见,对于下游无法保证幂等性的情况可以选择 failfast。
- 「池化设置」
线程池、连接池都是我们在程序开发中经常会使用的方式,核心目的就是为了减少频繁创建和销毁带来的系统开销,提升系统的性能,但是不合理的池化配置同样会给系统带来一定的风险。
- 线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式。显示定义线程池核心参数。(阿里编码规范)。
- redis和数据库连接池初始值需要考虑集群规模 以及存储服务允许的最大连接数,不可配置过大,配置的不合理会出现服务启动时就把存储服务打满的情况。
- 线程池和连接池需要设置有区分度的名称,以便于monitor和日志记录。
- 池化大小设置多少合适?要结合吞吐量和平响要求,建议公式:并发量(连接数、线程数)= 每秒请求数 (QPS)* 处理时间。也需要考虑CPU核数,磁盘,内存等综合考虑。建议根据线上压测来实际评估。
- 「快速扩缩容」
快速扩缩容体现了服务和系统的伸缩能力,这里要依赖于容器化的集群伸缩能力。由于历史问题,托管页系统有一些是运行在物理机或者无人维护的老平台上面,对于突发流量的应对简直束手无策,因此paas化是亟待解决的事情,依托于强大的paas平台的快速扩缩容的能力,能够做到快速止损。
- 「常规处理预案」
在平时多积累常规的预案是应对突发故障快速处理的有效手段,故障快速定位和止损相对理想的方式是打通故障定位和预案,当出现故障时,相关开发或者运维同学能够快速判断出故障类型并及时执行预案,主要有攻击限流、机房切换、快速扩容、常规紧急case的处理流程等。
- 将历史出现过的case进行复盘总结,分类归档到预案。
- 建立预案时尽量方便触发和执行。
- 上线或者变更引起的故障比较常见,每次上线需要有相应的变更回滚的完整方案。
- 对于流量和容量变化引起的故障,需要周期例行化的进行线上容量评估。
- 对于机房、网络、硬件等故障,要通过适当冗余和快速的流量切换保证服务稳定性。
- 「数据备份」
当服务出问题我们可以及时通过流量切换、重启、扩容解决,但是当数据出问题,例如删库,数据丢失等问题,恢复起来成本极高,因此平时我们需要对核心数据进行备份,例如MYSQL集群的核心数据要做到天级备份,并且可以通过binlog实时回溯数据,一般需要业务方和DBA共同确认数据的备份以及快速恢复机制。
---------- END ----------
百度 Geek 说
百度官方技术公众号上线啦!
技术干货 · 行业资讯 · 线上沙龙 · 行业大会
招聘信息 · 内推信息 · 技术书籍 · 百度周边
【百度商业托管页系统高可用建设方法和实践】欢迎各位同学关注
推荐阅读
- ZooKeeper分布式配置——看这篇就够了
- SimpleDateFormat线程不安全了(这里有5种解决方案)
- 我如何构建复杂的应用程序,逆向工程,文档和google绝对是你的利器!
- # 聊一聊悟空编辑器 #Gin + Swagger快速生成API文档
- 商城秒杀系统分析与设计
- 以太网链路聚合与虚拟路由器冗余协议
- 移植OpenHarmony 3.0到ARM单片机
- python函数高级
- ACL访问控制列表