读书_高并发设计40问之一基础

高并发系统设计 40 问:https://zq99299.github.io/note-architect/hc/

01,高并发系统:它的通用设计方法是什么?

归纳起来共有三种方法:

Scale-out(横向扩展)

分而治之是一种常见的高并发系统设计方法,采用分布式部署的方式把流量分流开,让每个服务器都承担一部分并发和流量。

缓存

使用缓存来提高系统的性能,就好比用 「拓宽河道」的方式抵抗高并发大流量的冲击。

异步

在某些场景下,未处理完成之前,我们可以让请求先返回,在数据准备好之后再通知请求方,这样可以在单位时间内处理更多的请求。

02,架构分层:我们为什么一定要这么做?

软件架构分层在软件工程中是一种常见的设计方式,它是将整体系统拆分成 N 个层次,每个层次有独立的职责,多个层次协同提供完整的功能。
我们在刚刚成为程序员的时候,会被「教育」说系统的设计要是「MVC」(Model-View-Controller)架构。它将整体的系统分成了 Model(模型),View(视图)和 Controller(控制器)三个层次,也就是将用户视图和业务处理隔离开,并且通过控制器连接起来,很好地实现了 表现和逻辑的解耦,是一种标准的软件分层架构。
分层的设计可以简化系统设计,让不同的人专注做某一层次的事情。想象一下,如果你要设计一款网络程序却没有分层,因为你必须是一个通晓网络的全才,要知道各种网络设备的接口是什么样的,以便可以将数据包发送给它。你还要关注数据传输的细节,并且需要处理类似网络拥塞,数据超时重传这样的复杂问题。当然了,你更需要关注数据如何在网络上安全传输,不会被别人窥探和篡改。
而有了分层的设计,你只需要专注设计应用层的程序就可以了,其他的,都可以交给下面几层来完成。
再有,分层之后可以做到很高的复用。比如,我们在设计系统 A 的时候,发现某一层具有一定的通用性,那么我们可以把它抽取独立出来,在设计系统 B 的时候使用起来,这样可以减少研发周期,提升研发的效率。
最后一点,分层架构可以让我们更容易做横向扩展。如果系统没有分层,当流量增加时我们需要针对整体系统来做扩展。但是,如果我们 按照上面提到的三层架构将系统分层后,那么我们就可以针对具体的问题来做细致的扩展。

分层架构的不足
任何事物都不可能是尽善尽美的,分层架构虽有优势也会有缺陷,它最主要的一个缺陷就是增加了代码的复杂度。

03,系统设计目标 1:如何提升系统性能?

高并发系统设计的三大目标:高性能、高可用、可扩展

性能优化原则

「天下武功,唯快不破」。性能是系统设计成功与否的关键,实现高性能也是对程序员个人能力的挑战。不过在了解实现高性能的方法之前,我们先明确一下性能优化的原则。
首先,性能优化一定不能盲目,一定是问题导向
脱离了问题,盲目地提早优化会增加系统的复杂度,浪费开发人员的时间,也因为某些优化可能会对业务上有些折中的考虑,所以也会损伤业务。
其次,性能优化也遵循「八二原则
即你可以用 20% 的精力解决 80% 的性能问题。所以我们在优化过程中一定要抓住主要矛盾,优先优化主要的性能瓶颈点。
再次,性能优化也要有数据支撑
在优化过程中,你要时刻了解你的优化让响应时间减少了多少,提升了多少的吞吐量。
最后,性能优化的过程是持续的

性能的度量指标

一般来说,度量性能的指标是 系统接口的响应时间。
脱离了并发来谈性能是没有意义的,我们通常使用 吞吐量 或者 同时在线用户数 来度量并发和流量,使用吞吐量的情况会更多一些。

响应时间究竟控制在多长时间比较合适呢?这个不能一概而论。
从用户使用体验的角度来看:
200ms *是第一个分界点:
接口的响应时间在 200ms 之内,用户是感觉不到延迟的,就像是瞬时发生的一样。
而 *
1s
是另外一个分界点:
接口的响应时间在 1s 之内时,虽然用户可以感受到一些延迟,但却是可以接受的
*超过 1s *之后用户就会有明显等待的感觉,等待时间越长,用户的使用体验就越差。
所以,健康系统的 99 分位值的响应时间通常需要控制在 200ms 之内,而不超过 1s 的请求占比要在 99.99% 以上。

高并发下的性能优化

01,一种是提高系统的处理核心数
提高系统的处理核心数就是 增加系统的并行处理能力,这个思路是优化性能最简单的途径。

02,另一种是减少单次任务的响应时间。
想要减少任务的响应时间,首先要看你的系统是 CPU 密集型 还是 IO 密集型 的,因为不同类型的系统性能优化方式不尽相同。
CPU 密集型系统中,需要处理大量的 CPU 运算,那么选用更高效的算法或者减少运算次数就是这类系统重要的优化手段。发现这类问题的主要方式,是通过一些 Profile 工具来找到消耗 CPU 时间最多的方法或者模块,比如 Linux 的 perf、eBPF 等。
IO 密集型系统指的是系统的大部分操作是在等待 IO 完成,这里 IO 指的是磁盘 IO 和网络 IO。我们熟知的系统大部分都属于 IO 密集型,比如数据库系统、缓存系统、Web 系统。这类系统的性能瓶颈可能出在系统内部,也可能是依赖的其他系统,而发现这类性能瓶颈的手段主要有两类。
第一类是 采用工具
Linux 的工具集很丰富,完全可以满足你的优化需要,比如网络协议栈、网卡、磁盘、文件系统、内存,等等。这些工具的用法很多。除此之外呢,一些开发语言还有针对语言特性的分析工具,比如说 Java 语言就有其专属的内存分析工具。
另外一类就是通过 监控 来发现性能问题。
在监控中我们可以对任务的每一个步骤做分时的统计,从而找到任务的哪一步消耗了更多的时间。
那么找到了系统的瓶颈点,要如何优化呢?优化方案会随着问题的不同而不同。比方说,如果是数据库访问慢,那么就要看是不是有锁表的情况、是不是有全表扫描、索引加得是否合适、是否有 JOIN 操作、需不需要加缓存,等等;如果是网络的问题,就要看网络的参数是否有优化的空间,抓包来看是否有大量的超时重传,网卡是否有大量丢包等。
总而言之,「兵来将挡水来土掩」,我们需要制定不同的性能优化方案来应对不同的性能问题。

04,系统设计目标 2:系统怎样做到高可用?

高可用性(High Availability,HA)是你在系统设计时经常会听到的一个名词,它指的是系统具备较高的无故障运行的能力。
可用性的度量
可用性是一个抽象的概念,你需要知道要如何来度量它,与之相关的概念是: MTBF 和 MTTR。
MTBF(Mean Time Between Failure)是平均故障间隔的意思,代表两次故障的间隔时间,也就是系统正常运转的平均时间。这个时间越长,系统稳定性越高。
MTTR(Mean Time To Repair)表示故障的平均恢复时间,也可以理解为平均故障时间。这个值越小,故障对于用户的影响越小。
可用性与 MTBF 和 MTTR 的值息息相关,我们可以用下面的公式表示它们之间的关系:

1
Availability = MTBF / (MTBF + MTTR)

1. 系统设计

a,failover(故障转移)
一般来说,发生 failover 的节点可能有两种情况:
01,完全对等 的节点之间做 failover。
02,不对等 的节点之间,即系统中存在主节点也存在备节点。
使用最广泛的故障检测机制是「心跳」。你可以在客户端上定期地向主节点发送心跳包,也可以从备份节点上定期发送心跳包。当一段时间内未收到心跳包,就可以认为主节点已经发生故障,可以触发选主的操作。
选主的结果需要在多个备份节点上达成一致,所以会使用某一种分布式一致性算法,比方说 Paxos,Raft。

b,调用超时控制
调用最怕的就是延迟而非失败 ,因为失败通常是瞬时的,可以通过重试的方式解决。而一旦调用某一个模块或者服务发生比较大的延迟,调用方就会阻塞在这次调用上,它已经占用的资源得不到释放。当存在大量这种阻塞请求时,调用方就会因为用尽资源而挂掉。

c,降级
降级是为了保证核心服务的稳定而牺牲非核心服务的做法。 比方说我们发一条微博会先经过反垃圾服务检测,检测内容是否是广告,通过后才会完成诸如写数据库等逻辑。

d,限流
限流完全是另外一种思路 ,它通过对并发的请求进行限速来保护系统。
比如对于 Web 应用,我限制单机只能处理每秒 1000 次的请求,超过的部分直接返回错误给客户端。虽然这种做法损害了用户的使用体验,但是它是在极端并发下的无奈之举,是短暂的行为,因此是可以接受的。

2. 系统运维

a,灰度发布
灰度发布指的是系统的变更不是一次性地推到线上的,而是按照一定比例逐步推进的。一般情况下,灰度发布是以机器维度进行的。比方说,我们先在 10% 的机器上进行变更,同时观察 Dashboard 上的系统性能指标以及错误日志。如果运行了一段时间之后系统指标比较平稳并且没有出现大量的错误日志,那么再推动全量变更。
那么我们如何知道发生故障时系统的表现呢?这里就要依靠另外一个手段: 故障演练。

b,故障演练(混沌工程)
故障演练指的是对系统进行一些破坏性的手段,观察在出现局部故障时,整体的系统表现是怎样的,从而发现系统中存在的,潜在的可用性问题。
一个复杂的高并发系统依赖了太多的组件,比方说磁盘,数据库,网卡等,这些组件随时随地都可能会发生故障,而一旦它们发生故障,会不会如蝴蝶效应一般造成整体服务不可用呢?我们并不知道,因此,故障演练尤为重要。

05,系统设计目标 3:如何让系统易于扩展?

从架构设计上来说,高可扩展性是一个设计的指标,它表示可以通过增加机器的方式来线性提高系统的处理能力,从而承担更高的流量和并发
高可扩展性的设计思路
拆分是提升系统扩展性最重要的一个思路,它会把庞杂的系统拆分成独立的,有单一职责的模块。相对于大系统来说,考虑一个一个小模块的扩展性当然会简单一些。将复杂的问题简单化,这就是我们的思路。

  1. 存储层的扩展性(业务切分,数据特征切分,减少事务)。
  2. 业务层的扩展性(业务维度 ,重要性维度 和 请求来源维度。)

06,面试现场第 1 期:当问到组件实现原理时,面试官是在刁难你吗?

了解基本原理,除了可以夯实你的基础知识以外,还可以为你的方案设计提供思路。比如说,当需要更高效的读写内存中的数据时可以怎么做,遇到并发问题时要如何来解决等等。
最后,也是最重要的,了解了组件的基本原理你在使用这些组件的时候才能够充分发挥它的优点、避免踩坑,在遇到问题的时候也会有排查的思路。

架构设计和高并发系列
读书_大型网站技术架构01_李智慧
读书_大型网站技术架构02_李智慧
读书_大型网站技术架构03_李智慧
读书_高并发设计40问之一基础
读书_高并发设计40问之二数据库
读书_高并发设计40问之三缓存
读书_高并发设计40问之四消息队列
读书_高并发设计40问之五分布式服务
读书_w3c架构师01通用设计与方法论
读书_w3c架构师02典型架构实践
读书_w3c架构师03数据库与缓存
分布式事务
高并发之缓存
高并发之降级
高并发之限流
数据库_读写分离
消息队列_01消息队列入门
消息队列_02rabbitMQ入门
消息队列_03rabbitMQ安装和使用

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×