架构面试精讲第四节 高并发下高可用、高性能架构详解

15 如何向面试官证明你做的系统是高可用的?

我们已经用了五个模块分别讲了架构原理、分布式技术、中间件、数据库,以及缓存,这些都是面试中必考的技术领域和技术点,又因为我们处在大数据和互联网时代,所以高可用高性能这些非功能性需求的考察,也是你需要了解的,所以在最后一个模块,我会带你打卡高可用高性能的架构设计面试实战。

我在 01 讲中说过,高级研发工程师和架构师的区别不在于掌握了多少技术,而在于你所能驾驭系统的边界。这其实也反映了一个研发工程师的成长历程,起初独立负责一个功能,然后负责一个系统模块,再负责一个系统,最后负责多个系统或业务条线。

但是不管你在哪个阶段,有一个问题你肯定逃不开:怎么证明自己负责的系统是高可用的?因为任何一个系统架构初衷,最基本的诉求是要保证系统的稳定性和可用性,然后才是基于高流量的场景下,保证系统的并发承载能力。

案例背景

一般来讲,面试官在考察你系统架构的设计能力时,经常会让你说一下你在上一家公司是怎么设计系统架构的,以此了解你的设计能力和思路。

而你在讲解架构设计时,也是在向面试官逐步证明,自己负责的系统是如何做到高可用的。这会涉及一个公认的论证——SLA。服务等级协议(Service-Level Agreement,SLA)最根本的形式是协议双方(服务提供者和用户)签订的一个合约或协议。这个合约规范了双方的商务关系或部分商务关系。简单点儿说,你可以认为 SLA 是服务可用性一个重要衡量指标。

业界一般用几个 9 的 SLA 服务等级来衡量互联网应用的可用性。比如京东的可用性是 4 个 9(京东的服务 99.99% 可用):京东的服务要保证在所有的运行时间里只有 0.01% 不可用,也就是说一年大概有 52.6 分钟不可用,这个 99.99% 就叫作系统的可用性指标。

52.6 分钟是怎么计算出来的呢?

SLA 的计算公式

从公式中可以看出, SLA 等于 4 个 9,也就是可用时长达到了 99.99% ,不可用时长则为是0.01%,一年是 365 天, 8760 个小时,一年的不可用时长就是 52.6 分钟,那么:

  • SLA 等于 3 个 9,就相当于一年不可用时长等于 526 分钟;

  • SLA 等于 5 个 9,就相当于一年不可用时长等于 5.26 分钟。

可以发现,用 SLA 等于 4 个9 作为参照物,少个 9 相当于小数点往后移一位,多个 9 相当于小数点往前移一位(我把系统可用性指标总结成一张表格)。

系统可用性指标

那么问题就来了: 既然 SLA 是服务可用性的一个衡量指标,那么你在面试时,怎么设置这个指标的阈值才合理呢?

  • 一般来讲,2 个 9 表示系统基本可用,年度不可用时间小于 88 小时。

  • 3 个 9 是较高可用,年度不可用时间小于 9 个小时。

  • 4 个 9 是具有自动恢复能力的高可用,年度不可用时间小于 53 分钟。

  • 5 个 9 指极高的可用性,年度不可用时间小于 5 分钟。

在电商平台中(比如淘宝、京东、拼多多),系统可用性大多是 4 个 9。那么你在回答时,一要了解 SLA 的概念,N 个 9 代表什么含义,更要清楚互联网对于 SLA 的主流设定阈值。

讲到这儿,你可能会觉得:那我清楚了 SLA 的概念,然后也了解了 SLA 的主流设定阈值,当面试官问我“你们的系统高可用做得怎么样”时,我回答系统做到了 N 个 9 是不是就可以了?

案例分析

给你 10 秒钟的时间思考一下,当面试官听到你按照时间指标度量系统可用性,会不会满意?

要知道,任何一家互联网公司,都有流量的低峰期和高峰期,你在低峰期停机 1 分钟和高峰期停机 1 分钟,对业务影响的结果完全不同。如果认识不到这一点,面试官很容易认为你很业余,并没有实践经验。

所以,仅凭理论指标在有些情况下是不能满足实际需求的,那有没有更加科学的度量方式呢?答案就是基于一段时间(比如 1 年)的停机影响的请求量占比,进行评估,公式如下:

这样一来,你就可以评估,业务在高峰期停机和在低峰期停机分别造成多少的损失了。所以,如果你再回答系统高可用指标的时候,我建议你可以遵循这样的套路:先摆明度量的两种方式,“N 个 9” 和 “影响请求量占比”,然后再结合实际业务场景表明第二种方式的科学性。

总的来说,作为候选人,要立足业务价值去回答问题,不是仅停留于技术概念的堆砌,这才能体现你的思考。

当然了,以上的内容基本可以满足你应聘初中级研发工程师的需求,如果你要面试高级研发工程师或者是架构师,你还要有一个思路的闭环。为了方便你的记忆,我把这个思路总结为:“可评估”“可监控”“可保证”。

所以,当你向面试官证明系统高可用时,其实是在回答这样几个问题:

  • 如何评估系统高可用?

  • 如何监控系统高可用?

  • 如何保证系统高可用?

接下来,我们继续学习“如何监控系统高可用”至于“如何保证系统高可用”我将在下一讲中为你解答。

案例解答

我们以设计一个保证系统服务 SLA 等于 4 个 9 的监控报警体系为例。监控系统包括三个部分:基础设施监控报警、系统应用监控报警,以及存储服务监控报警。 接下来,我就围绕这三个最核心的框架带你设计一个监控系统,并基于监控系统的设计,让你了解到系统哪些环节会影响系统整体的可用性,从而在面试中对系统高可用设计有更加清晰的掌握。

  • 基础设施监控

基础设施监控由三个部分组成:监控报警指标、监控工具以及报警策略。

监控报警指标分为两种类型。

  1. 系统要素指标:主要有 CPU、内存,和磁盘。

  2. 网络要素指标:主要有带宽、网络 I/O、CDN、DNS、安全策略、和负载策略。

为什么我们要监控这些指标?因为它们是判断系统的基础环境是否为高可用的重要核心指标。

监控报警指标

监控工具常用的有ZABBIX(Alexei Vladishev 开源的监控系统,覆盖市场最多的老牌监控系统,资料很多)、Open-Falcon(小米开源的监控系统,小米、滴滴、美团等公司内部都在用)、Prometheus(SoundCloud 开源监控系统,对 K8S 的监控支持更好)。这些工具基本都能监控所有系统的 CPU、内存、磁盘、网络带宽、网络 I/O 等基础关键指标,再结合一些运营商提供的监控平台,就可以覆盖整个基础设施监控。

监控报警策略一般由时间维度报警级别阈值设定三部分组成

监控报警策略

为了方便你理解监控报警策略,我举个例子。假设系统的监控指标有CPU、内存和磁盘,监控的时间维度是分钟级,监控的阈值设置为占比。那么你可以定义出如下的监控报警策略:

为了第一时间监测到指标的健康度,报警级别可以分为紧急、重要,以及一般。当 CPU、内存,以及磁盘使用率这三项指标的每分钟采集的指标达到 90% 使用率时,就触发“紧急报警”;达到 80% 触发“重要报警”;70% 触发“一般报警”。

  • 系统应用监控

业务状态监控报警,关注点在于系统自身状态的监控报警。和基础设施监控一样,它也是由监控指标,监控工具,报警策略组成,不同的是,系统应用监控报警的核心监控指标主要有流量、耗时、错误、心跳、客户端数、连接数等 6 个核心指标,监控工具有 CAT、SkyWalking、Pinpoint、Zipkin 等。

系统应用监控

  • 存储服务监控

一般来讲,常用的第三方存储有 DB、ES、Redis、MQ 等。

对于存储服务的监控,除了基础指标监控之外,还有一些比如集群节点、分片信息、存储数据信息等相关特有存储指标的监控。

对于存储服务监的内容细节,我这里就不再一一介绍,在面试中,你只需要基于监控系统的三个核心组成部分(基础设施监控、系统应用监控、存储服务监控)来回答问题即可,比如,你可以回答:我为了确保系统的健康可靠,设计了一套监控体系,用于在生产环境对系统的可用性进行监控,具体的指标细节可以结合业务场景进行裁剪,比如你们是游戏领域,所以很关注流量和客户端连接数。

总的来说,让面试官认可你有一个全局的监控视角,比掌握很多监控指标更为重要。

当然,很多互联网公司都很重视系统服务稳定性的工作,因为服务的稳定性直接影响用户的体验和口碑,线上服务稳定性是研发工程师必须要重点关注的问题。所以当你回答完上述问题后,有的面试官为了考察候选人的责任意识,一般还会追问:“如果线上出现告警,你作为核心研发,该怎么做呢?”

对于线上故障,要有应急响应机制,我总结以下几点供你参考:

总结

我们来回顾一下今天的重点内容。

为了在面试中更好地回答怎么评估系统高可用,我们讲解了 SLA 的概念以及评估方法,并得出“以停机时间影响的系统请求量作为评估指标”比较科学。

为了确保线上服务的稳定运行,在设计监控系统时,要考虑三个核心点,基础设施监控、系统应用监控,以及存储服务监控。

另外,我强调了故障处理是研发工程师在进阶过程中必须经历的,而故障处理能力也是面试官最为看重的能力之一,所以对于怎么处理各类故障,你要形成一套体系化的知识框架。

为了方便你的记忆,我将今天的内容总结如下。

最后,留一个话题我们来讨论吧:在你所处了领域中,你设计系统架构时,更关注哪些可用性指标?感谢你的阅读,我们下一讲见。


16 如何从架构师角度回答系统容错、降级等高可用问题?

上一讲,我带你学习了“评估系统高可用的指标”以及“如何监控系统高可用”今天这一讲,我们继续学习保证系统高可用的有效手段,比如系统容错、降级等,以及在面试时的重要考察点。

系统容错、降级等手段你肯定不陌生,很多文章都反复重申过,所以我的重点不再是普及相关理论知识,而是带你深入面试中对架构高可用问题的考察,带你避免面试中的易错点。

案例背景

先来看这样一道面试题:

商品的一次查询

某电商平台中有商品系统、促销系统、积分系统。商品的一次查询操作是由网关系统先调用商品系统查询商品列表,然后根据返回的商品列表信息,再查询促销和积分系统,匹配商品信息的促销活动和积分奖励,最终返回给客户端展示给用户。

大部分互联网公司,会有专门的研发团队分别负责这三个系统(比如 A 团队负责商品系统、 B 团队负责促销系统)。这会带来一个问题:出现流量高峰期时,虽然作为服务请求入口的商品系统很容易扩容,但对于商品系统依赖的其他服务,就不会有实时性的响应。

那么促销或积分系统就可能因为无法承担大流量,请求处理缓慢,从而让执行商品查询操作的服务线程阻塞,不能释放,直到所有线程资源被占满,无法处理后续的请求。

对于这种问题,你该如何处理呢?

案例分析

这道面试题就涉及了高可用架构的设计,我们再来分析一下商品的调用链条。在电商平台的商品系统中,一次系统查询的流程经历了三次调用,从网关系统开始,然后依次调用商品系统、促销系统、积分系统的三个服务,如果此时积分系统的响应时间变长,那么整条请求的响应时间也会因此变长,整体服务甚至会发生宕机。这就是服务雪崩现象:即局部故障最终导致了全局故障。

在分布式环境下,系统某一个服务或者组件响应缓慢,从而拖垮整个系统的情况随处可见。那你要怎么避免呢?这就涉及我们在 15 讲中的内容了。在 15 讲中我提到了,对于系统可用性,你要通过三个方面来解决:分别是“评估”“检测”和“保证”,具体如下。

  1. 用科学的方法评估系统的可用性指标;

  2. 通过实时监控预警检测系统的可用性

  3. 通过系统架构设计保证系统的可用性。

解决的思路是:在分布式系统中,当检测到某一个系统或服务响应时长出现异常时,要想办法停止调用该服务,让服务的调用快速返回失败,从而释放此次请求持有的资源。这就是架构设计中经常提到的降级和熔断机制。

对应到面试中,面试官一般会通过如下两个问题考察候选者:

  • 熔断和降级是怎么做的(考察你对原理性知识的掌握)?

  • 你在项目中如何实现熔断降级(考察你的实战能力)?

你先要了解熔断和降级的原理,再结合实践设计实现它们。

案例解答

熔断设计的原理

形象一点儿说:熔断机制参考了电路中保险丝的保护原理,当电路出现短路、过载时,保险丝就会自动熔断,保证整体电路的安全。

而在微服务架构中,服务的熔断机制是指:在服务 A 调用服务 B 时,如果 B 返回错误或超时的次数超过一定阈值,服务 A 的后续请求将不再调用服务 B。这种设计方式就是断路器模式。

在这种模式下,服务调用方为每一个调用的服务维护一个有限状态机,在这个状态机中存在关闭半打开打开三种状态。

  • 关闭:正常调用远程服务。

  • 半打开:尝试调用远程服务。

  • 打开:直接返回错误,不调用远程服务。

这三种状态之间切换的过程如下。

  • “关闭”转换“打开”:当服务调用失败的次数累积到一定的阈值时,服务熔断状态,将从关闭态切换到打开态。

  • “打开”转换“半打开”:当熔断处于打开状态时,我们会启动一个超时计时器,当计时器超时后,状态切换到半打开态。

  • “半打开”转换“关闭”:在熔断处于半打开状态时,请求可以达到后端服务,如果累计一定的成功次数后,状态切换到关闭态。

熔断状态变更示意图

在工作中,研发工程师经常会通过 Netflix 的开源项目 Hystrix 来实现熔断的功能,并不会探究其中的原理,我在 07 讲中就说过:

虽然在实际工作中不推荐重复“造轮子”,但在面试中要证明自己具备“造轮子”的能力,因为要评价一个程序员是否对技术栈有全面的认识,考察其“造轮子”的能力是一个不错的切入点。

所以很多时候,面试官也会考察你在不通过开源组件的前提下,怎么实现断路器的功能。

如何设计实现一个断路器

断路器的流程图

  • “关闭”转“打开”: 当请求到来,首先判断是否在熔断中,如果没有熔断,则正常调用系统服务,此时统计系统的调用状态,如果失败次数超过阈值,则断路器“打开”。

// 如果是关闭状态
if(breaker.isClose()) {
    // 失败次数超过阈值
    if(failCount.incrementAndGet() >= FAILS_THRESHOLD) {
        // 设置为打开状态
        breaker.setOpen();
    }
}
  • “打开”转“半打开”: 如果已经熔断,就初始化一个定时器,定期检测服务状态的可用性,如果服务达到了熔断的倒计时,则设置当前熔断器为“半打开”状态。

// 初始化定时器定期检测服务是否可用
new Timer("Service-Recover", true).scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        if (breaker.isOpen()) {
            // 设置为半打开态
            breaker.setHalfOpen(); 
        }
    }
}, 0, recoverInterval);
  • “半打开”转“关闭”: 如果服务状态是半打开,则判断成功次数是否超过阈值,超过则设置断路器的状态为“关闭”。

// 如果断路器是半打开状态
if(breaker.isHalfOpen()) {
    // 判断成功次数是否超过阈值
    if(successCount.incrementAndGet() >= SUCCESS_THRESHOLD) {
        // 设置断路器为关闭状态
        breaker.setClose(); 
    }
}

这样,当某一个服务节点出现问题,服务调用者的熔断器就会实时监测到,并且不再请求有问题的服务节点,避免单个节点的故障导致整体系统的雪崩。

说完了熔断设计的原理和实现,我们再来看看降级设计的原理。

降级设计的原理

降级设计本质上是站在系统整体可用性的角度上考虑问题:当资源和访问量出现矛盾时,在有限的资源下,放弃部分非核心功能或者服务,保证整体的可用性。这是一种有损的系统容错方式。

这样看来,熔断也是降级的一种手段(除此之外还有限流、兜底服务等)。

降级的实现手段是:在请求流量突增的情况下,放弃一些非核心流程或非关键业务,释放系统资源,让核心业务正常运行。比如 618 零点大促,电商平台一般会暂时关闭评论、退款功能。

那么问题来了,当你被问到“怎么做降级设计?”时,要怎么回答呢?

如何设计一个降级机制

从架构设计的角度出发,降级设计就是在做取舍,你要从服务降级功能降级两方面来考虑。

在实现上,服务降级可以分为读操作降级和写操作降级。

  • 读操作降级: 做数据兜底服务,比如将兜底数据提前存储在缓存中,当系统触发降级时,读操作直接降级到缓存,从缓存中读取兜底数据,如果此时缓存中也不存在查询数据,则返回默认值,不在请求数据库。

  • 写操作降级: 同样的,将之前直接同步调用写数据库的操作,降级为先写缓存,然后再异步写入数据库。

我们提炼一下这两种情况的设计原则。

  • 读操作降级的设计原则,就是取舍非核心服务。

  • 写操作降级的设计原则,就是取舍系统一致性:实现方式是把强一致性转换成最终一致性。比如,两个系统服务通过 RPC 来交互,在触发降级时,将同步 RPC 服务调用降级到异步 MQ 消息队列中,然后再由消费服务异步处理。

而功能降级就是在做产品功能上的取舍,既然在做服务降级时,已经取舍掉了非核心服务,那么同样的产品功能层面也要相应的进行简化。在实现方式上,可以通过降级开关控制功能的可用或不可用。

另外,在设计降级时,离不开降级开关的配置,一般是通过参数化配置的方式存储在配置中心(如 Zookeeper),在高并发场景下,手动或自动开启开关,实现系统降级。

总结

这一讲我带你了解了雪崩产生的原因,服务熔断的实现方式以及服务降级的策略,今天你需要了解的重点是:

  • 服务熔断其实是一个有限状态机,实现的关键是三种状态之间的转换过程。

  • 降级就是在做取舍(取舍服务、取舍功能),本质上是为了解决资源不足和访问量过大的问题。实现上可以降低系统一致性、裁剪非核心服务,以及简化产品功能。

总之,服务的熔断和降级是互联网保证系统稳定性和可用性的重要手段,在你的架构设计中,如果涉及系统与第三方服务调用的情况下,都需要考虑增加服务熔断和降级方案。当然,高可用的设计方案不仅仅只有熔断和降级,还有如服务冗余、负载均衡、故障隔离、服务限流等设计方式。

总而言之,既然系统出故障是不可避免的,那做架构设计时就要把故障当作不可或缺的一环来处理,因此在分布式系统设计的和开发的过程中,要通过各种架构手段来提高系统可用性。

本节课的思考题是:结合你项目中的实际工作经历,说一说你在项目中都制定了哪些降级的策略?我们下一讲见。


17 如何向面试官证明你做的系统是高性能的?

前两讲,我带你了解了“高可用的衡量标准”以及“如何设计高可用的架构”,接下来我们会用两讲的时间来聊一聊“高性能”的话题,今天咱们先来探讨怎么向面试官证明你做的系统是高性能的?(其中会涉及性能优化的指标和关注点,以及怎样分析系统的性能瓶颈)。

案例背景

我曾经面试过很多研发工程师和架构师,他们在介绍系统性能时,一般会说:“我们的架构最高支持 200 万的并发流量”。

如果不考虑实际业务需求,这样的回答没有任何意义,因为高性能与业务是强相关的:

  • 如果一台网络游戏服务器,可以支撑 2 百名玩家同时在线开黑,可能就算高性能;

  • 如果一台网络直播服务器,可以支撑 2 千名用户同时在线观看,可能就算高性能;

  • 如果一台电商平台服务器,可以支撑 2 万名用户同时在线下单,可能就算高性能;

这些数据也许有出入,但逻辑没问题,并且在实际的业务场景中,你要关注很多业务相关性指标,比如游戏需要关注稳定性;视频需要关注延时;电商需要关注一致性……

在明确了业务场景之后,你还要关注系统的性能指标主要有吞吐量、延迟以及 TP。

案例分析

我们在拿到产品经理的 PRD 文档时,心里就会清楚要关心哪些系统性能指标,因为需求文档中会描述同时支持多少人在线访问,你也可以借此估算出系统的并发用户数。一般来讲,系统建立的会话数量就是用户同时访问系统的数量。你也可以通过公式,估算出系统的吞吐量(throughput)和延迟(latency)。

延迟和吞吐量,是衡量软件系统最常见的两个指标。

  • 吞吐量(系统处理请求的速率):反映单位时间内处理请求的能力(单位一般是TPS或QPS)。

  • 延迟(响应时间):从客户端发送请求到接收响应的时间(单位一般是ms、s)。

一般来说,延迟和吞吐量既互斥,又不绝对的互斥,你可以通过性能压测分别绘制吞吐量和延迟的曲线图:

延迟总是非递减的曲线,开始时表现比较平稳,到了某一个特定值后,会迅速增大。而吞吐量曲线在开始时迅速增加,到达峰值后逐渐减小。

总体来看,随着压力增大,单位时间内系统被访问的次数增加。结合延迟和吞吐量观察的话,吞吐量曲线的最高点,往往是延迟曲线最低偏后的一个时间点,这意味着延迟已经开始增大一段时间了。那么对一些延迟要求比较高的系统来说,系统优化性能指标是要找到延迟趋向最低和吞吐量趋向最高的点

从图中你也可以看出,如果不做流量控制,在系统压力不断增大后,系统便什么也做不成。这也是一些不够健壮的系统,在压力较大的特殊业务场景下(比如一元秒杀、抢购、瞬时流量非常大的系统),直接崩溃,对所有用户拒绝服务的原因。

除了吞吐量和延迟,TP(Top Percentile)也经常被提到。 以 TP 99 为例,它是指请求中 99% 的请求能达到的性能,TP 是一个时间值,比如 TP 99 = 10ms,含义就是 99%的请求,在 10ms 之内可以得到响应。

关于 TP 指标,你要掌握两个考点。

  • 计算 TP 指标: 比如 TP 99,把一段时间内所有请求的响应时间,从小到大进行排序,然后取 99% 对应的请求的响应时间,即为 TP99 的值。

  • TP指标相比于性能均值的意义: 为什么要用 TP 99 这样的比例方式,而不直接用平均数来定义性能呢?这是为了更符合实际系统的情况。

举个例子,比如在一个系统的 100 个请求中,99 个都在 1 s 左右返回,剩下 1 个 100s 还不返回,如果计算平均时间,就是,无法反映系统的真实情况。因为耗时 100 s 的请求也许是异常请求,正常请求的平均时间仍是 1 秒,而 TP99 就比较能反映真实情况,因为 TP99 就可以达到 1 秒。

对初中级研发工程师来说,回答“吞吐率、延迟、TP 99(TP 99 比较有代表性)”这三个指标就够了,但如果你应聘高级研发工程师,还要站在系统全链路的角度上思考问题,从端到端的角度思考系统的性能指标(也就是从架构师的视角分析系统)。

案例解答

用架构师的视角分析系统性能指标

架构师视角说白了就是系统的全链路视角,我们从前端请求流程开始,来讲解一次请求链路会涉及哪些前后端性能指标。

一次请求链路

步骤一:DNS解析

用户在浏览器输入 URL 按回车,请求会进行 DNS 查找,浏览器通过 DNS 解析查到域名映射的IP 地址,查找成功后,浏览器会和该 IP 地址建立连接。对应的性能指标为:DNS解析时间

那你怎么提升域名DNS解析性能呢?

答案是通过 DNS缓存或 DNS 预解析,适当增大域名的TTL 值来增大 DNS 服务器缓存域名的时间,进而提升了缓存的命中率。也可以用 dns-prefetch 标签实现域名的预解析,让浏览器在后台把要用的 DNS请求提前解析,当用户访问的网页中包含了预解析的域名时,再次解析 DNS 就不会有延迟了,比如京东针对静态资源域名的预解析如下:

<link rel="dns-prefetch" href="//static.360buyimg.com">

步骤二:建立TCP连接

由于 HTTP 是应用层协议,TCP 是传输层协议,所以 HTTP 是基于 TCP 协议基础上进行数据传输的。所以你要建立 TCP 请求连接,这里你也可以用 TCP的连接时间来衡量浏览器与 Web 服务器建立的请求连接时间。

步骤三:服务器响应

这部分就是我们开篇讲到的最重要的性能指标了,即服务器端的延迟和吞吐能力。针对影响服务端性能的指标,还可以细分为基础设施性能指标、数据库性能指标,以及系统应用性能指标。

  • 基础设施性能指标主要针对 CPU 利用率、磁盘 I/O,网络带宽、内存利用率等。

举个例子,如果 CPU 占用率超过80%,很可能是系统出了问题。如果内存利用率 100%,可能是因为内存中存放了缓存,因此还要衡量 SWAP 交换空间的利用率。另外,还要考虑容器的 JVM 的Full GC 情况、磁盘 I/O 是否可以优化、网络带宽是否存在瓶颈等问题都会影响系统的最终性能。

  • 数据库的性能指标主要有 SQL 查询时间、并发数、连接数、缓存命中率等。

  • 系统应用性能指标和系统业务有关,因为业务场景影响架构设计,比如To C 的系统一般会设计成同步 RPC 调用,因为要实时反馈 C 端用户的请求,而 To B 的系统则可以设计成事件驱动模式,通过异步通知的方式,推送或拉取数据,两种架构对比,显然异步事件驱动的吞吐量会更高。

步骤四:白屏时间

当浏览器与 Web 服务器建立连接后,就可以进行数据通信了。Web 服务器接收请求后,开始处理请求,浏览器这时会等待Web 服务器的处理响应。

由于浏览器自上而下显示 HTML,同时渲染顺序也是自上而下的,所以当用户在浏览器地址栏输入 URL 按回车,到他看到网页的第一个视觉标志为止,这段白屏时间可以作为一个性能的衡量指标(白屏时间越长,用户体验越差)。

优化手段为减少首次文件的加载体积,比如用 gzip 算法压缩资源文件,调整用户界面的浏览行为(现在主流的Feed流也是一种减少白屏时间的方案)。

步骤五:首屏时间

用户端浏览界面的渲染,首屏时间也是一个重要的衡量指标,首屏时间是指:用户在浏览器地址输入 URL 按回车,然后看到当前窗口的区域显示完整页面的时间。一般情况下,一个页面总的白屏时间在 2 秒以内,用户会认为系统响应快,2 ~ 5 秒,用户会觉得响应慢,超过 5 秒很可能造成用户流失。

如何分析系统的性能瓶颈?

通常情况下,系统性能不达标一般会反映在TP 99 的延迟上面,但这只是表层的现象,怎么找到系统真正的性能瓶颈呢? 你可以遵循这几个步骤。

  • 设计阶段,定义系统性能目标

你要在项目初期定义好系统大致的性能目标,比如希望单台服务器能够负载多少 TPS 的请求,因为不同的性能会影响到系统的架构设计,也会带来不同的成本,一旦过了设计阶段,再因为性能问题调整系统架构,成本极高。比如,当前单机性能是 80 TPS,要想优化到100 TPS,可以做一些小的性能优化,但要提升到 1000 TPS,就要进行架构改造了,代价非常大。

  • 开发阶段,走查代码和业务流程

也就是评审代码,代码包括应用程序源代码、环境参数配置、程序整个调用流程和处理逻辑。比如,用户在 App 中触发了“立即下单”按钮,服务端的应用程序从线程池里取得了线程来处理请求,然后查询了几次缓存和数据库,都读取和写入了什么数据,再把最终的响应返回给 App,响应的数据报文格式是什么,有哪些状态码和异常值……

  • 测试阶段,压测发现系统性能峰值

一般来说,你要在系统上线前,对系统进行全方位的压力测试,绘制出系统吞吐量和延迟曲线,然后找到最佳性能点,并在超过最佳性能点时做限流,如果达不到最佳性能点(比如多数系统的吞吐量,随着压力增大,吞吐量上不去)就需要考虑出现延迟和吞吐量的这几种情况。

1.定位延迟问题

你要本着端到端的策略,大到整体流程,小到系统模块调用,逐一排查时间消耗在哪里。

你可以使用 kill -3 PID, jstack 等命令打印系统当前的线程执行的堆栈;还可以用一些性能分析工具,如 JProfiler 来监控系统的内存使用情况、垃圾回收、线程运行状况,比如你发现了运行的 100 个线程里面,有 80 个卡在某一个锁的释放上面,这时极有可能这把锁造成的延迟问题。

2.  对于吞吐量问题的定位

对于吞吐量指标要和 CPU使用率一起来看,在请求速率逐步增大时,经常会出现四种情况:

总结

对于怎么评估系统高性能,你可以从系统的吞吐量、延迟以及 TP 99,这三个指标出发回答面试官提出的问题。而对于高级研发工程师,不仅仅要了解后端的性能指标,还有对全链路的性能指标有所了解。

另外,在实际生产环境,还会涉及 CDN 加速、ISP 路由策略、边缘计算等一系列网络工程层面的性能优化指标,这里展开的内容相对较多,你可以自己课下学习。总的来说,你要在大脑里先建立起整个请求的链路蓝图,熟悉每个环节的性能损耗。

今天的作业是:对于我们常用的系统或中间件,你能说出它们的性能指标吗?欢迎在留言区分享你的看法,下一讲我将从架构师的角度分析系统高性能是如何设计的,其中会涉及从产品到架构,从前端到后端具体的性能优化技术。


18 如何从架构师角度回答怎么应对千万级流量的问题?

上一讲,我带你学习了“如何评估系统的性能指标”以及“如何分析系统的性能瓶颈”,今天我们继续上一讲的话题,来解答“如何设计高性能的架构”。

我会从两方面出发,带你看一看在面试中遇到高性能架构设计问题时,初中级研发工程师和高级研发工程师不同的回答思路。

高性能设计中的“术”

学完上一讲后,你应该知道自己要从系统全链路的视角,从前端请求到后端服务评估各环节的性能指标,那么对于系统性能的优化,你依然要从全链路的视角上进行高性能的设计。

前端优化

前端的优化主要有三个环节:减少请求次数页面静态化边缘计算

减少请求次数:减少前端脚本与后端服务的请求次数,有三种方案。

  • 增加缓存控制:前端研发同学经常会设置 HTML 的缓存控制头部(Cache-Control 头),这样浏览器在请求同一个文件时,只访问本地保存的资源副本,从而加速文件的访问速度。

  • 减少图像的请求次数:你可能经常会发现,大部分网站会将所有用到的多张图片拼成一张,这样多张图片只需要下载一次,然后再通过 CSS 中的 background-image 和 background-position 来定位目标位置选择显示哪一张图片。

  • 减少脚本的请求次数:通用的做法就是 CSS 压缩和 JavaScript 压缩,将多个文件压缩成一个,目的是减少传输文件的大小,而更重要的是减少请求数。

而页面静态化就是缓存的一种方式,相当于把整个页面元素缓存起来,那么缓存在哪里呢?

通常是将页面文件事先存储在 CDN 节点中,比如将商品详情页做静态化,就是将商品详情页的页面元素存储在 CDN 节点,然后所有请求就可以直接以由 CDN 来提供服务,就不会到达后端服务了,就减少了对后端服务的压力。

边缘计算,被很多人提及,原因是大数据处理的实时性越来越高,由集中式的服务系统提供实时性的计算能力捉襟见肘,所以很多大厂开始将计算能力放到距离用户最近的 CDN 节点中,这就要求原有的 CDN 节点不只作为静态资源文件的缓存,而是要提供可以定制化的计算能力。

这部分内容会涉及一些新的概念,比如无服务架构 Serverless、BaaS、FaaS,在面试中不要求候选人必须掌握,但它会是你的加分项。

后端优化

后端环节的性能问题,可以从基础设施层面、网络层面、架构层面三个角度进行考量,为了帮助你记忆,我总结了一张脑图给你参考。

比如,网络层面可以考虑网络专线、CDN 优化;架构层面可以考虑动静分离、集群优化、数据隔离、服务拆分、异步驱动、负载均衡等方案。

以上就是高性能架构设计中的技术点,初中级研发工程师要能知道系统的性能瓶颈在哪儿,以及如何优化,但高级研发工程师,不能只停留掌握技术点上,而是要有自己对技术的理解(例如下面的例子)。接下来,我就通过讲解自己对高性能的认知,帮你了解并培养自己对于技术的思考。

高性能设计中的“道”

你在设计高性能系统架构时,首先是清楚认知系统的硬性性能指标,举个例子。

  • 指标需求:我们要保证系统的 TP 99 为 2s;

  • 表面意思:系统要保证 99% 的请求的响应时间都在  2s 内;

  • 深层意思:对有经验的架构师来说,这样的需求其实是不明确的,任何系统都有自己的承载能力范围,换句话说就是在并发用户数限定范围之内,一定要保证系统的 TP 99 = 2s,例如“我们要保证系统并发数在 100 万用户内的时候,TP 99 为 2s”,对于系统设计者而言,要清楚系统有所能,有所不能。

所以,对一个架构师来说,要设计一个高性能的架构,至少要有以下四个系统设计的思考步骤。

  • 明确指标: 比如当系统小于 100 万并发用户时,要保证系统的 TP 99 = 2s 。

  • 保护系统: 当系统的并发用户数量超过 100 万,要做到保证有 100 万用户的 TP 99 = 2s ,然后保护系统,并拒绝其他用户的连接请求。

  • 用户体验: 为了用户体验,要给系统承载容量外的用户提供优雅的体验,比如服务器排队机制,并附加具体、明确的信息提示。

  • 快速扩容: 这一步很容易被一些同学忽略,如今系统的性能指标还有一点就是贵在快速反应,比如能承诺出现流量压力时,可以在 3 分钟内完成扩容,并保证扩容后能承载的并发用户数量的 TP 99 = 2s。

在明确了性能指标之后,高性能架构的落地工作,可以分为以下三个关键技术点。

  • 做好系统限流: 通过流量控制来保证系统的稳定性。当实际并发压力超过系统性能设计指标的时候,就拒绝新的请求的连接,让用户进行排队。

  • 做好快速扩容: 对于扩容能力,一般要储备额外的计算资源,用于不时之需,也就是事先通过预估流出一部分资源池。

有的同学可能会疑惑,既然有多余的资源为什么不提前扩容呢?这是出于对 IT 成本的考虑,作为系统设计者也要把成本作为系统的设计原则之一。

另一个关键因素是系统的扩容速度。这是在当今互联网软件中非常重要的系统能力之一了,就算架构设计的不够优雅,但如果响应够快,也是能解决问题。

  • 做好系统优化: 就是我在上面讲的前后端优化的技术点,我要再补充一点,对系统设计者来说,性能设计要贯穿于系统建设的始终。以一个系统的研发管理过程为例,内容大致包括需求阶段、设计阶段、研发阶段、测试阶段、上线阶段、运行阶段

对于性能设计(不仅仅是性能设计,所有非功能性的设计)要在项目的各阶段都进行考虑,以便根据项目过程的推进随时进行调整和优化。

总结

技术行业发展到今天,很多技术上的问题都不存在挑战了,所谓的高性能架构设计,也仅仅变成了一种标准化的应对流程

你要做的就是将业务问题,抽象成一个技术问题,比如具体到数据库设计、缓存设计、队列设计、线程设计等技术细节,然后不管你通过什么渠道,Google 也好,问同事也好,或者购买付费知识也好,都能找到技术的应对方案。

而对于面试,你的答题思路应该是这样的:

  • 先落实到技术上,比如结合业务场景,识别系统最关键的服务,然后针对性地为关键服务进行性能设计与测试,确保关键服务没有问题,然后为非关键服务提供降级和熔断处理方案。

  • 再深化自己对于技术的理解,你要记住,任何复杂问题都可以按时间(系统建设周期)和空间(系统设计分层)拆解为简单的问题,然后逐一攻克,这样你才能有的放矢,这其中体现出来的思维能力才是每一个技术人的安身立命之本。

最后,留一个话题我们来讨论吧:在你曾经设计的系统中,遇到过哪些性能问题,你是怎么解决的?欢迎在留言区分享你的看法。


案例串联 如何让系统抗住双十一的预约抢购活动?

到目前为止,我们讨论了很多的面试思路,比如 02 讲中关于架构设计的“四步回答法”,不过大部分内容都是比较独立的知识点(比如分布式事务、分布式锁……)为了让你更深入掌握前几讲内容,把相对独立的知识串联起来,我们今天就来回顾、梳理近期学习的内容,通过“电商预约抢购”的场景,用前几讲内容,做一道完整的架构设计题。

案例背景

在大促活动期间,“预约抢购”已经是各大电商平台的主要促销手段,京东自然也会和一些大的供应商合作,推出一些低价的爆款产品,比如 2019 年的 “1499 元抢购飞天茅台”活动,就让很多人每天准时准点拿着手机拼人品。

那这类电商领域的大促抢购场景涉及专栏的哪些内容呢?它们是怎么通过架构设计的方式组合在一起,实现一个完整的需求流程呢?这就是今天要讨论的话题。

我们先把需求梳理一下,总的来说,实现一个抢购系统大概可以分为四个阶段。

  • 商品预约:用户进入商品详情页面,获取购买资格,并等待商品抢购倒计时。

  • 等待抢购:等待商品抢购倒计时,直到商品开放抢购。

  • 商品抢购:商品抢购倒计时结束,用户提交抢购订单,排队等待抢购结果,抢购成功后,扣减系统库存,生成抢购订单。

  • 订单支付:等待用户支付成功后,系统更新订单状态,通知用户购买成功。

接下来,我们就针对各阶段容易出现的问题,来分析其中的技术考点和解决方案。

商品预约阶段

这几年,很多电商平台为了方便流量运营,改造了传统秒杀场景,通过先预约再抢购的方式预热商品,并根据预约量调整运营策略。而且在预约抢购的活动中,为了增加商品售卖量,会允许抢购前,预约资格超过实际的库存数量。

那么问题来了:如何在高并发量的情况下,让每个用户都能得到抢购资格呢?这是预约抢购场景第一个技术考察点。 那你可以基于“06 | 分布式系统中,如何回答锁的实现原理?”来控制抢购资格的发放。

我们基于 Redis 实现分布式锁(这是最常用的方式),在加锁的过程中,实际上是给 Key 键设置一个值,为避免死锁,还要给 Key 键设置一个过期时间。

SET lock_key unique_value NX PX 10000
  • lock_key 就是 key 键;

  • unique_value 是客户端生成的唯一的标识;

  • NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;

  • PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。

而解锁的过程就是将 lock_key 键删除,但不能乱删,要保证执行操作的客户端就是加锁的客户端。而这个时候, unique_value 的作用就体现出来,你可以通过 Lua 脚本判断 unique_value 是否为加锁客户端。

选用 Lua 脚本是为了保证解锁操作的原子性。因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,保证了锁释放操作的原子性。

// 释放锁时,先比较 unique_value 是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

这样一来,就通过使用 SET 命令和 Lua 脚本在 Redis 单节点上完成了分布式锁的加锁和解锁。但你要注意,此方案是基于单节点的 Redis 实例实现的,如果此时 Redis 实例发生故障宕机,那么锁变量就没有了,客户端也就无法进行锁操作,就会影响到业务的正常执行。
所以,基于 Redis 实现分布式锁时,你还要掌握如何保证锁的可靠性,也就是怎么基于多个 Redis 节点实现分布式锁(这部分也可以参考 06 讲中的内容)。

等待抢购阶段

用户预约成功之后,在商品详情页面中,会存在一个抢购倒计时,这个倒计时的初始时间是从服务端获取的,用户点击购买按钮时,系统还会去服务端验证是否已经到了抢购时间。

在等待抢购阶段,流量突增,因为在抢购商品之前(尤其是临近开始抢购之前的一分钟内),大部分用户会频繁刷新商品详情页,商品详情页面的读请求量剧增, 如果商品详情页面没有做好流量控制,就容易成为整个预约抢购系统中的性能瓶颈点。

那么问题来了:如何解决等待抢购时间内的流量突增问题呢?有两个解决思路。

  • 页面静态化:提前对抢购商品的详情页面做静态化,生成一个静态页面,再把页面放到距离用户最近的 CDN 节点中,这样一来,当浏览器访问页面时,就会自动缓存该页面的静态资源文件(对于静态化技术,很多页面端的模板引擎都支持这样的功能,我就不展开讲了)。

  • 服务端限流:对商品详情页中的动态请求接口设置最大并发访问数量(具体的数量根据上线前的性能压测为准),防止超出预期的请求集中访问系统,造成系统压力过载。操作上,你可以在商品详情页的后端系统入口层(如网关系统)中进行接口限流,如果使用 Nginx 来做反向代理,可以直接基于 Nginx 配置限流算法,比如 Nginx 的 ngx_http_limit_req_module(限制单位时间内所有 IP 的请求数量)和 ngx_stream_limit_conn_module(限制单位时间内单个 IP 的请求数量)两个模块就提供了限流控制的功能,所以你还要提前掌握限流策略的原理,如令牌桶算法的原理。

商品抢购阶段

在商品抢购阶段,用户会点击提交订单,这时,抢购系统会先校验库存,当库存足够时,系统会先扣减库存,然后再生成订单。在这个过程中,短时间之内提交订单的写流量非常高,所以为了做流量削峰,会将提单请求暂存在消息队列中,并提示用户“抢购排队中……”然后再由后端服务异步处理用户的请求。

而你可以基于数据库和缓存两种方式,来实现校验库存和扣减库存的操作。

但因为抢购场景的瞬时流量极高,一般不会直接基于数据库来实现(因为每次操作数据库,即使通过消息队列做了流量削峰,对数据库来说压力也很大,会产生性能瓶颈)。如果非要基于数据库的话,你要通过分布式锁来优化扣减库存的并发操作,但此阶段的分布式锁对可靠性的要求会极高(因为在大促抢购阶段,小的可用性故障,都可能造成大的线上事故),所以基于单节点 Redis 实现的分布式锁不合适,你要选择多节点 Redis 实现分布式锁,或者选型 ZooKeeper。

为了避免上述问题,我们一般基于缓存来存储库存,实现扣减库存的操作。这样在提交订单时,库存的查询和锁定就不会给数据库带来性能瓶颈。不过你仍要注意,基于缓存(如 Redis)的库存扣减操作,仍要考虑缓存系统的单点问题,就算是多节点存储库存,也要引入锁的策略,保证 Redis 实现库存的一致性。

实现了校验库存和扣减库存之后,最后一步是生成抢购订单。由于数据库表会承载订单数据,一旦出现瞬时流量,磁盘 I/O、数据库请求连接数等资源都会出现性能瓶颈,你可以考虑对订单表分库分表,通过对用户 ID 字段进行 Hash 取模,实现分库分表,提高系统的并发能力。

从“商品抢购阶段的架构设计”中我们可以总结出三个技术考点:流量削峰、扣减库存、分库分表。

  • “流量削峰”的面试考点

流量削峰是由于正式抢购场景下,短时间内的提单请求非常高,所以引入消息队列做异步化,然后在抢购系统的后端服务中,启动若干个队列处理消息队列中的提单请求,再执行校验库存、下单等逻辑。

那么如何快速处理消息队列中的提单请求,避免出现大量的消息积压,就是本阶段的考点之一了,方案可以参考“08 | MQ:如何回答消息队列的丢失、重复与积压问题?”

  • “扣减库存”的面试考点

我刚刚提到,当基于 Redis 实现库存的扣减时,要考虑怎么解决 Redis 的单点问题。而如果基于 Redis 集群来实现扣减库存,还要解决 Redis 在哨兵模式部署的情况下,因为主从切换带来的数据不一致的问题。这就涉及“06 | 分布式系统中,如何回答锁的实现原理?”中的内容。

  • “分库分表”的面试考点

生成订单后如何实现分库分表?你可以参考“04 | 亿级商品存储下,如何深度回答分布式系统的原理性问题?”中的解决方案。

当然还有一个容易忽略的问题:带宽的影响。由于抢购入口的请求量会非常大,可能会占用大量带宽,为了不影响提交订单的请求,有时会从网络工程的角度解决,通过单独的子域名绑定独立的网络服务器,这里就会涉及 DNS 的设计与优化手段。

订单支付阶段

在用户支付订单完成之后,一般会由支付平台回调系统接口,更新订单状态。在支付回调成功之后,抢购系统还会通过异步通知的方式,实现订单更新之外的非核心业务处理,比如积分累计、短信通知等,此阶段可以基于 MQ 实现业务的异步操作。

订单支付后操作

不过针对服务的异常(如宕机),会存在请求数据丢失的可能,比如当支付回调系统后,修改订单状态成功了,但是在异步通知积分系统,更新用户累计积分时,订单系统挂掉了,此时 MQ 还没有收到这条消息,那么这条消息数据就无法还原了。

订单支付后操作(异常)

所以你还要考虑“05 | 海量并发场景下,如何回答分布式事务一致性问题?”中,可靠消息投递机制:先做消息的本地存储,再通过异步重试机制,来实现消息的补偿。比如当支付平台回调订单系统,然后在更新状态的同时,插入一个消息,之后再返回第三方支付操作成功的结果。最后,通过数据库中的这条消息,再异步推送其他系统,完成后续的工作。

订单支付后操作(新方案)

总结

今天,我们用前几讲的内容实现了一个完整的预约抢购的系统设计,为了加深你的理解,我总结了每个阶段的注意点。

  • 商品预约阶段:要掌握如何在高并发的场景下通过锁的方式,让每一个用户都获取到抢购资格,结合业务场景对于并发控制的需求诉求和成本的考虑,在商品预约阶段,你可以基于 Redis 来实现分布式锁。

  • 等待抢购阶段:此阶段对页面的查询请求会很高,尤其是临近抢购倒计时的流量突增,解决方案是做页面静态化和服务端限流。

  • 商品抢购阶段:商品抢购是整个流程中涉及技术点最多的阶段,瞬时流量会带来极大的压力,所以通过 MQ 做了同步转异步,实现对流量的削峰,从而让请求排队等待,然后有序且有限地进入到后端服务,而你必须掌握消息队列的丢失、重复和积压问题的解决方案;另外在扣减库存的时候,为了解决扣减存储不超售的问题,同样还需要引入锁的机制。

  • 订单支付阶段:在用户支付完成后,系统通常还需要处理一些非核心操作,你可以通过 MQ 通知的方式来实现系统间的解耦和异步通信,但依旧要保证消息的可靠性(当然也可以通过 RPC 同步调用的方式来实现),所以你也要掌握 RPC 和 MQ 的相关知识点。

总的来说,互联网中大数据里的存储设计(如商品与订单数据的存储设计),你可以参考 04 讲;关于秒杀或抢购场景下的库存扣减设计,你可以参考 06 讲;分布式系统之间的事务一致性的架构设计,你可以参考 05 讲;关于架构设计中的服务强依赖的设计,一般会通过 RPC 远程同步调用的方式实现,你可以参考07讲;系统解耦,流量削峰的设计问题,你可以参考 08讲。

留个作业:用户提交订单抢到商品后,此时系统的库存已经扣减掉了,但是订单中的状态还是未支付,如果此时用户是恶意的行为,只抢购不支付,那么你怎么优化架构设计来应对这样的操作呢?欢迎把答案写到留言区,和我一起讨论,我们下一讲见。


彩蛋 互联网架构设计面试,你需要掌握的知识体系

研发工程师的职业成长路线,基本是初级研发工程师,进阶为中高级研发工程师,提升至架构师,然后再寻求更高的突破。这直接说明我们认同架构师的价值,想要努力成为架构师。虽然目标很明确,但我调研后发现,大多数研发工程师把“成为架构师”当作目标,但却没找到方法。

因为在工作中,不是每一个研发都有机会参与架构设计;很多公司也不会主动去培养你成为架构师。所以,有很多职场人在一家公司工作三年或五年之后并没有多大的提升。

而很多的架构师都是研发自己在机遇巧合下,遇到大项目、参与其中、趟了坑、解决了问题,最终形成自己的知识体系和解决问题的能力之后才成长起来的。那么如果没有这些条件,你还有没有途径成为一名架构师呢?

当然有,在我看来,你要先掌握架构师的知识体系,然后再通过实践进行检验,这样才能逐步成长为一名架构师。

架构师能力模型

很多研发同学经常问我:“成为架构师应该掌握哪些技术?”

在我看来,成为架构师是要掌握一定的知识储备,再经过项目历练,但你更应该通过“知识储备+项目历练”,看自己达到了什么能力,你的能力是否能够匹配架构师这个岗位。

换句话说,你是否具有架构师的能力模式?

我们拿互联网大厂(比如 BAT)的能力模型来对标,从它们对架构师的能力要求来看:能不能覆盖一个领域子方向,也就是能不能作为一个系统的技术负责人?比如交易系统的负责人、商品系统的负责人。换句话说,你能不能把握整个系统的规划、设计、落地,和演进。如果你能独挡这一面,就具备了成为架构师的条件。

那架构师的能力到底由哪几部分组成呢?

  1. 基础技术架构:这部分是纯技术架构,所有非功能性的技术都是基础技术的范畴。

  2. 业务架构:在业务场景下对业务需求的抽象。

  3. 开发技能:这是架构师落地架构的能力。

你要怎么理解这个模型呢?

举个例子,我们在开发时会经历需求分析、架构设计、架构选型、架构落地几个阶段,这几个阶段对架构师的能力要求总结为一句话就是“架构师要把握系统技术”。

  • 在需求分析阶段:架构师对于业务架构,要给出一个合理的需求分析抽象模型。

  • 在架构设计和架构选型阶段:架构师要充分考虑技术的合理性,制定合理的设计方案。

  • 在架构落地阶段:架构师要能指导研发进行落地,并推进项目的执行。

你看,从需求分析、架构设计、到架构选型、再到架构落地,架构师都需要参与,而这些阶段体现出来的需求分析能力、架构设计能力、代码开发能力,最终都会作用在一个系统上,这就是所谓的“把握系统技术”。也就是说,你如果想成为架构师就要做到、做好系统开发各环节的技术把控!

那么在架构师能力模型的指引下,你要掌握哪些知识体系呢?

架构师知识体系

我们以互联网分布式系统架构师的知识体系为例,回顾一下 03 讲中的内容。

分布式系统看起来就像一个计算机。计算机包括五大体系结构(即冯诺依曼结构),它有五大部件:分别是控制器、运算器、存储器、输入及输出。你可以这么理解:一个分布式系统也包含这五大部件,其中最重要的是计算与存储。计算与存储由一系列网络节点组成,每个节点之间的通信就是输入与输出,各节点之间的调度管理就是控制器。

图 分布式架构技术组成

所以,对于从事互联网分布式设计的架构师来说,你可以从以下四个角度来进行知识体系的拆解。

  • 存储

存储指分布式存储系统,你要理解什么是分布式存储系统?为什么选型分布式存储系统?以及分布式存储中关注哪些问题?

首先,为了解决数据的水平扩展,要做数据分片,因为分布式系统区别于传统单机系统就在于能将数据分布到多个节点,并在多个节点间实现负载均衡。这种数据水平扩容的操作叫数据分片。

数据分片会涉及分片规则,常见的有范围分片和哈希分片,不同的分片规则就有不同的分片算法,如哈希分片就会涉及哈希取模算法、虚拟桶算法、一致性哈希算法。

又因为数据要分布到多个节点,你还需要数据复制,数据复制就会存在同步复制和异步复制。为了保证数据的可靠性和可用性,增强系统容错,数据复制就会产生副本,副本则是分布式存储系统解决高可用的唯一手段。

而多个副本同步会产生一致性的问题,从而引出一致性问题的分类,如强一致性、弱一致、最终一致,要想解决一致性问题,会涉及一致性问题的协议:如两阶段提交协议(Two-PhraseCommit,2PC)、Paxos协议选举、向量时钟(VectorClock)、RWN协议、Raft协议。

多个副本还会带来主选举,这会涉及分布式锁的问题:多个机器竞争一个锁,当某个机器释放锁或者挂掉,其他机器可以竞争到锁,继续执行任务。为了解决锁的容错性,比如解决双主(脑裂)问题,就会涉及租约机制,租约机制可以解决网络分区问题造成的“双主”问题。

最后,为了衡量副本可用性和一致性,就会引出分布式系统的基础理论 CAP 、BASE,以及 PACELC。

这样一来,我们就梳理清楚了分布式存储的知识体系。可以说,分布式存储是分布式系统知识体系中最基础的理论,也是最复杂的问题。

  • 计算

分布式计算就会涉及三个概念:并行计算、分布式计算、云计算。

  • 并行计算:同时使用多种计算资源解决计算问题的过程,比如多线程就是一种并行计算;服务集群也是一种并行计算。

  • 分布式计算:是从集群技术发展而来,区别在于集群虽然连接了多台机器,但某项具体的任务执行时还是会被转发到某台服务器上,分布式计算则将任务分割到多台服务器上并行计算,然后得到结果。

  • 云计算:分布式计算 + 虚拟化技术的综合技术的统称,不同商业公司有着各自不同的定义,通俗来讲就是开发者利用云 API 开发应用,然后上传到云上托管,并提供给用户使用,而不关心云背后的运维和管理,以及机器资源分配等问题。

作为架构师,你要了解分布式领域中的计算模式,如分布式并行计算框架 Hadoop 中的 MapReduce 的设计思想,以及基于流式计算框架 Storm、Spark、Flink 的架构设计方案。

当然对于计算领域,很多公司会设立大数据架构师的岗位,如果你面试的是系统架构师,了解这部分知识体系即可,不用过度聚焦于分布式计算上,不过很多计算框架的设计理念还是很有参考价值的,值得你去学习了解。

  • 输入输出

系统架构中的输入输出,是指系统间通信的技术。

其中会涉及一些基础知识,比如网络通信最基础的协议(诸如 TCP/UDP 协议等);网络 I/O 模型(Blocking-IO,NonBlocking-IO、Asyn-IO),最后是偏应用的知识,需要了解例如连接复用、序列化/反序列化、RPC、MQ 消息队列等。

作为架构师,你要理解高性能的原理,掌握流量的流转过程以及应对方案,比如当请求到达网络设备时,你要依次考虑以下问题:

  • 网络设备如何处理流量?这会涉及中断和缓存。

  • 操作系统如何处理流量?这会涉及 I/O 模型,select、poll、epoll,以及 I/O 多路复用。

  • 应用系统如何处理流量?这会涉及 NIO 的开发,如 Reactor 模式、Netty 框架原理等。

  • 系统线程如何处理流量?还会涉及多线程的设计模式。

最后,你还要掌握分布式系统通信的核心技术:RPC 和 MQ。

  • 控制器

你可以把分布式系统知识体系中的控制器,理解为系统架构中的调度系统,包括流量调度和资源调度。

  • **流量调度(我们常说的流量控制):**作为架构师就要掌握流量控制的常用方案策略,比如负载均衡、服务路由、熔断、降级、限流等,其实常用的高可用、高性能的解决方案很多都是基于流量上的调度。

  • **资源调度:**如果我们将流量调度迁移到服务器的计算资源、存储资源或基础资源上面的话,就会引出另一种基于资源的调度,如 Mesos、Yarn 基于计算资源的调度;HDFS、GlusterFS、Ceph 基于存储资源的调度;Kubernetes、Mesos 基于容器资源的调度(包括计算、存储、网络等综合性的资源调度)。

总的来说,你至少要掌握常用系统调度设计,调度算法与负载策略。举个例子,如果让你对单个服务器的计算资源做调度,你至少要具备设计思路:让集群选举一个主节点,每个从节点会向主节点汇报自己的空闲资源,当请求到来时,主节点通过资源调度算法选择一个合适的从节点来处理该请求。

总结

无论你从事哪个领域的架构设计工作,都要明白作为架构师,一定是技术出身,但是要突破技术思维的限制,向上立足于部门和公司、向下管控系统和研发,站在全局的角度去规划、组织、系统技术的发展。

为了方便你理解,我把学习架构设计知识的思路总结为以下几点:

  • 想要学习架构设计知识,可以从自己熟知的领域出发,这样你才有不断的正反馈,从而更有信心,容易理解新的知识。

  • 形成知识网络图谱,如今技术错综复杂,各种技术又相互耦合,确实无法简单划分层次,所以我建议你把自己的核心知识梳理出一个脉络清晰的结构图,然后结合已有知识,再逐步将零散的知识点补充到这张网络图谱之上,这样你就拥有了核心知识和扩展知识。

  • 养成对技术判断力,针对同一问题有不同方法,不同维度、不同角度的分析和对比。这是为了提升你今后在工作中对技术的领悟力。

不知道在听完今天的内容之后,你有什么样的收获呢?欢迎在留言区分享你的想法,我们互相交流进步。


结束语 程序员的道、术、势

时间如白驹过隙,经过两个多月的课程更新,到今天,咱们的课程也就正式结束了。

在更新课程的过程中,我看了很多同学的留言,有积极提问题的,比如 blossom、徐力辉同学等,也有分享自己方案的,比如 coder、Reiser同学。很开心,你们能在课程中有所收获。

当然了,在这个过程中我也发现了一个很明显的问题,那就是:作为技术人员,很容易在学习的过程中,纠结于具体的形式(比如案例、代码)。在我看来,相比于形式,思维过程最为重要。 比如我是怎么思考到某个点?有哪些合理或者不合理的地方?哪些能变成你自己的?哪些你只是看个热闹?

你只有真正思考、转化、并积累,能力才能得到提升。

关于结束语

结束语我想了很久,希望能在最后用微薄的能力来帮你深入理解技术人员的职业规划和发展:不仅要关注专业技术(术),还要有自己的分析能力(道),并且要懂得顺势而为(势)。

举个例子,几年前,架构师 A 空降到某互联网大厂做某个系统的技术负责人,当时团队有 5 个核心研发,他们对于 A 的态度有抵触、有观望、有站队,还有左右摇摆。总之,他们表现出了职场老油条各种该有的表现。

那么如果你是架构师兼技术负责人,该怎么驾驭团队呢?A 只做了这几个工作:

  1. 立规矩:找所有研发谈话,明确告知他们做事的准则和底线,任何人不可以越红线半步。

  2. 建系统:推进新系统的建设,招聘新人,培养自己的人,并让老人维护老系统,确保新系统在建设的过程中,老系统不出问题,保证业务的正常运行。

  3. 定节奏:让团队按照新的节奏做事儿,加班制度、值班制度,打破原有的工作节奏,整体提速。

一个季度过后,A 带出了一个富有激情和战斗力的团队,而老团队的人有的改变,也有的离开。

你可能会问,这个故事和“道术势”有什么关系?这不就是一个技术管理人应该具备的素质吗?

并不是,很多事情你看似简单,但其中会有很多思考(认知层)、执行(技术层)和借势(战略层)的操作,不是每一个人都能做好的。

那当你看完这个故事,有没有思考这样几个问题呢?

  • 为什么 A 重新“立规矩”就能立下来?

  • 为什么 A 说推进“新系统”就能获得老板的同意?

  • 为什么 A 说重新“定节奏”就能得到团队的支持?

我就把这三个问题当作这一讲的思考题,欢迎你在留言区发表自己的想法,我们再一起讨论。

接着说回来,作为技术人员的你,一定不要高估辛苦工作的作用,低估判断力的价值。举个不恰当的例子,CEO 一年看起来什么都没做,但他一个判断就可能让整个公司的业务走向巅峰或者没落。

讲了这么多,我想给你一些职场建议。

送给老人的四条建议

  • 思考

你要有敏感的思考能力(这里用的是敏感,不是敏捷或敏锐)。因为对于同样一个问题,每个人都有自己的理解方式,不同人由于学历、阅历、经历的不同,对问题的理解深度也会有所不同,所以不是所有人天生就能有敏锐的视角,这个很正常。

但我希望你的思维是敏感的,通过敏感驱动思考,通过思考驱动学习和总结,直至敏锐。这确实需要大量的练习,它也是一个潜移默化的过程,最终你会因为这个习惯受益一生。

  • 表达

你要有良好的表达能力,无论是和同事还是领导之间的沟通,这里的沟通并不是指会说话,而是会表达、敢表达、表达准确。

有一点你要注意,表达不是指口才好,而是建立在你充分思考基础之上的分析,而我建议你多学习一下结构化思维,比如《金字塔原理》。相信你会发现表达、汇报、甚至 PPT 功底都会有所提升。

  • 惊艳

这一点其实是我对自己的要求,我会暗示自己,当作一件很有价值的事情时,评估的方式就是要惊艳到自己。

因为只有惊艳到自己,你做的事情才会超出别人的预期,超出领导对你的期望。领导要的可能是 1,你要尽力做到 1.2,甚至 1.5,因为 1 谁都能做到,而超出的 0.2 / 0.5 就是你与其他人拉开的差距。事事有回应,件件有着落,回应和着落都要超出预期。 千万不要当职场老油条,因为懒惰会变成习惯,最终会影响你的判断力。

  • 认知

工作几年的职场老人,一定要对“地位、格局、方法论、手段”,这些看似很空的词有明确的认知,你要让这些极空的词儿变得很具体。举个例子,比如一道面试题“什么是架构设计?”,作为一名合格的职场技术人,至少要有以下的认知。

  1. 和一面解释:架构就是系统设计,比我在做 A 项目时,考虑到问题 X,我的解决办法是 Y。

  2. 和二面解释:架构就是业务发展中将系统变得有序,比如架构设计就是合理的组织系统、模块、组件,让它们更加有序,为的是让系统有能力快速响应(需求/用户/市场)变化。

  3. 和部门总监解释:架构即管理

送给新人的四条建议

对于职场新人,我也有几条建议送给你。

  • 要有计划,有积累:在寻求公司价值的同时,要对自己个人未来的价值有追求和计划。

  • 学习一些时间管理四象限:重视重要不紧急的事情,新人不要觉得时间多就随意挥霍,你的价值才能决定你的圈子和你的人脉。

  • 要自己定位自己:不要让别人或你的上级定位你,虽然屁股决定脑袋,但你对自己的定位会决定你屁股的位置。

  • 要善于归纳总结和思考:这会让你和其他人拉开差距。

讲到这儿,我想送你一句话:作为技术人,要在职业发展过程中,让自己体会到这样的三个阶段,“做下不做上”“做下也做上”“做上不做下”,相信在看完今天的内容,你应该会对这几句话有新的感悟。

最后,我为你准备了一份结课问卷,希望你对本课程的内容进行评价,以便我及时优化课程内容,另外课程结束后也欢迎你随时在留言区留言,我会及时与你交流,共同进步。

点击链接,即可参与课程评价。


你可能感兴趣的