文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

打工人,支撑亿级高并发的系统长啥样?

2024-12-03 18:30

关注

[[349677]]
图片来自 Pexels

 

面对业务急剧增长你怎么处理?业务量增长 10 倍、100 倍怎么处理?你们系统怎么支撑高并发的?怎么设计一个高并发系统?高并发系统都有什么特点?......

诸如此类,问法很多,但是面试这种类型的问题,看着很难无处下手,但是我们可以有一个常规的思路去回答,就是围绕支撑高并发的业务场景怎么设计系统才合理?

如果你能想到这一点,那接下来我们就可以围绕硬件和软件层面怎么支撑高并发这个话题去阐述了。

本质上,这个问题就是综合考验你对各个细节是否知道怎么处理,是否有经验处理过而已。

面对超高的并发,首先硬件层面机器要能扛得住,其次架构设计做好微服务的拆分,代码层面各种缓存、削峰、解耦等等问题要处理好。

数据库层面做好读写分离、分库分表,稳定性方面要保证有监控,熔断限流降级该有的必须要有,发生问题能及时发现处理。这样从整个系统设计方面就会有一个初步的概念。

微服务架构演化

在互联网早期的时候,单体架构就足以支撑起日常的业务需求,大家的所有业务服务都在一个项目里,部署在一台物理机器上。

所有的业务包括你的交易系统、会员信息、库存、商品等等都夹杂在一起,当流量一旦起来之后,单体架构的问题就暴露出来了,机器挂了所有的业务全部无法使用了。

 

于是,集群架构的架构开始出现,单机无法抗住的压力,最简单的办法就是水平拓展横向扩容了,这样,通过负载均衡把压力流量分摊到不同的机器上,暂时是解决了单点导致服务不可用的问题。

 

但是随着业务的发展,在一个项目里维护所有的业务场景使开发和代码维护变得越来越困难,一个简单的需求改动都需要发布整个服务,代码的合并冲突也会变得越来越频繁,同时线上故障出现的可能性越大。微服务的架构模式就诞生了。

 

把每个独立的业务拆分开独立部署,开发和维护的成本降低,集群能承受的压力也提高了,再也不会出现一个小小的改动点需要牵一发而动全身了。

以上的点从高并发的角度而言,似乎都可以归类为通过服务拆分和集群物理机器的扩展提高了整体的系统抗压能力,那么,随之拆分而带来的问题也就是高并发系统需要解决的问题。

RPC

微服务化的拆分带来的好处和便利性是显而易见的,但是与此同时各个微服务之间的通信就需要考虑了。

传统 HTTP 的通信方式对性能是极大的浪费,这时候就需要引入诸如 Dubbo 类的 RPC 框架,基于 TCP 长连接的方式提高整个集群通信的效率。

 

我们假设原来来自客户端的 QPS 是 9000 的话,那么通过负载均衡策略分散到每台机器就是 3000,而 HTTP 改为 RPC 之后接口的耗时缩短了,单机和整体的 QPS 就提升了。

而 RPC 框架本身一般都自带负载均衡、熔断降级的机制,可以更好的维护整个系统的高可用性。

那么说完 RPC,作为基本上国内普遍的选择 Dubbo 的一些基本原理就是接下来的问题。

 

Dubbo 工作原理:

 

Dubbo 负载均衡策略:

现在把这些权重值平铺在一维坐标值上,[0, 5) 区间属于服务器 A,[5, 8) 区间属于服务器 B,[8, 10) 区间属于服务器 C。

接下来通过随机数生成器生成一个范围在 [0, 10) 之间的随机数,然后计算这个随机数会落到哪个区间上就可以了。

在服务运行一段时间后,性能好的服务提供者处理请求的速度更快,因此活跃数下降的也越快,此时这样的服务提供者能够优先获取到新的服务请求。

集群容错:

消息队列

对于 MQ 的作用大家都应该很了解了,削峰填谷、解耦。依赖消息队列,同步转异步的方式,可以降低微服务之间的耦合。

对于一些不需要同步执行的接口,可以通过引入消息队列的方式异步执行以提高接口响应时间。

在交易完成之后需要扣库存,然后可能需要给会员发放积分,本质上,发积分的动作应该属于履约服务,对实时性的要求也不高,我们只要保证最终一致性也就是能履约成功就行了。

对于这种同类性质的请求就可以走 MQ 异步,也就提高了系统抗压能力了。

 

对于消息队列而言,怎么在使用的时候保证消息的可靠性、不丢失?

消息可靠性

消息丢失可能发生在生产者发送消息、MQ 本身丢失消息、消费者丢失消息3个方面。

①生产者丢失

生产者丢失消息的可能点在于程序发送失败抛异常了没有重试处理,或者发送的过程成功但是过程中网络闪断 MQ 没收到,消息就丢失了。

由于同步发送的一般不会出现这样使用方式,所以我们就不考虑同步发送的问题,我们基于异步发送的场景来说。

异步发送分为两个方式:异步有回调和异步无回调,无回调的方式,生产者发送完后不管结果可能就会造成消息丢失,而通过异步发送+回调通知+本地消息表的形式我们就可以做出一个解决方案。

以下单的场景举例:

 

在监控平台配置或者 JOB 程序处理超过一定次数一直发送不成功的消息,告警,人工介入。

一般而言,对于大部分场景来说异步回调的形式就可以了,只有那种需要完全保证不能丢失消息的场景我们做一套完整的解决方案。

②MQ 丢失

如果生产者保证消息发送到 MQ,而 MQ 收到消息后还在内存中,这时候宕机了又没来得及同步给从节点,就有可能导致消息丢失。

比如 RocketMQ:RocketMQ 分为同步刷盘和异步刷盘两种方式,默认的是异步刷盘,就有可能导致消息还未刷到硬盘上就丢失了,可以通过设置为同步刷盘的方式来保证消息可靠性,这样即使 MQ 挂了,恢复的时候也可以从磁盘中去恢复消息。

比如 Kafka 也可以通过配置做到:

acks=all 只有参与复制的所有节点全部收到消息,才返回生产者成功。这样的话除非所有的节点都挂了,消息才会丢失。replication.factor=N,设置大于1的数,这会要求每个partion至少有2个副本min.insync.replicas=N,设置大于1的数,这会要求leader至少感知到一个follower还保持着连接retries=N,设置一个非常大的值,让生产者发送失败一直重试

虽然我们可以通过配置的方式来达到 MQ 本身高可用的目的,但是都对性能有损耗,怎样配置需要根据业务做出权衡。

③消费者丢失

消费者丢失消息的场景:消费者刚收到消息,此时服务器宕机,MQ 认为消费者已经消费,不会重复发送消息,消息丢失。

RocketMQ 默认是需要消费者回复 ack 确认,而 Kafka 需要手动开启配置关闭自动 offset。

消费方不返回 ack 确认,重发的机制根据 MQ 类型的不同发送时间间隔、次数都不尽相同,如果重试超过次数之后会进入死信队列,需要手工来处理了。(Kafka 没有这些)

 

消息的最终一致性

事务消息可以达到分布式事务的最终一致性,事务消息就是MQ提供的类似XA的分布式事务能力。

半事务消息就是 MQ 收到了生产者的消息,但是没有收到二次确认,不能投递的消息。

实现原理如下:

最终,如果MQ收到二次确认commit,就可以把消息投递给消费者,反之如果是rollback,消息会保存下来并且在3天后被删除。

 

数据库

对于整个系统而言,最终所有的流量的查询和写入都落在数据库上,数据库是支撑系统高并发能力的核心。

怎么降低数据库的压力,提升数据库的性能是支撑高并发的基石。主要的方式就是通过读写分离和分库分表来解决这个问题。

对于整个系统而言,流量应该是一个漏斗的形式。比如我们的日活用户 DAU 有 20 万,实际可能每天来到提单页的用户只有 3 万 QPS,最终转化到下单支付成功的 QPS 只有 1 万。

那么对于系统来说读是大于写的,这时候可以通过读写分离的方式来降低数据库的压力。

 

读写分离也就相当于数据库集群的方式降低了单节点的压力。而面对数据的急剧增长,原来的单库单表的存储方式已经无法支撑整个业务的发展,这时候就需要对数据库进行分库分表了。

针对微服务而言垂直的分库本身已经是做过的,剩下大部分都是分表的方案了。

水平分表

首先根据业务场景来决定使用什么字段作为分表字段(sharding_key),比如我们现在日订单 1000 万,我们大部分的场景来源于 C 端,我们可以用 user_id 作为 sharding_key。

数据查询支持到最近 3 个月的订单,超过 3 个月的做归档处理,那么 3 个月的数据量就是 9 亿,可以分 1024 张表,那么每张表的数据大概就在 100 万左右。

比如用户 id 为 100,那我们都经过 hash(100),然后对 1024 取模,就可以落到对应的表上了。

分表后的 ID 唯一性

因为我们主键默认都是自增的,那么分表之后的主键在不同表就肯定会有冲突了。

有几个办法考虑:

主从同步原理

主从同步原理如下:

 

由于 MySQL 默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。

①全同步复制

主库写入 binlog 后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。

②半同步复制

和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回 ACK 确认给主库,主库收到至少一个从库的确认就认为写操作完成。

缓存

缓存作为高性能的代表,在某些特殊业务可能承担 90% 以上的热点流量。

对于一些活动比如秒杀这种并发 QPS 可能几十万的场景,引入缓存事先预热可以大幅降低对数据库的压力,10 万的 QPS 对于单机的数据库来说可能就挂了,但是对于如 Redis 这样的缓存来说就完全不是问题。

 

以秒杀系统举例,活动预热商品信息可以提前缓存提供查询服务,活动库存数据可以提前缓存,下单流程可以完全走缓存扣减,秒杀结束后再异步写入数据库,数据库承担的压力就小的太多了。

当然,引入缓存之后就还要考虑缓存击穿、雪崩、热点一系列的问题了。

热 Key 问题

所谓热 key 问题就是,突然有几十万的请求去访问 redis 上的某个特定 key,那么这样会造成流量过于集中,达到物理网卡上限,从而导致这台 redis 的服务器宕机引发雪崩。

 

针对热 key 的解决方案:

缓存击穿

缓存击穿的概念就是单个 key 并发访问过高,过期时导致所有请求直接打到 DB 上,这个和热 key 的问题比较类似,只是说的点在于过期导致请求全部打到 DB 上而已。

解决方案:

 

缓存穿透

缓存穿透是指查询不存在缓存中的数据,每次请求都会打到DB,就像缓存不存在一样。

 

针对这个问题,加一层布隆过滤器。布隆过滤器的原理是在你存入数据的时候,会通过散列函数将它映射为一个位数组中的 K 个点,同时把他们置为 1。

这样当用户再次来查询 A,而 A 在布隆过滤器值为 0,直接返回,就不会产生击穿请求打到 DB 了。

显然,使用布隆过滤器之后会有一个问题就是误判,因为它本身是一个数组,可能会有多个值落到同一个位置,那么理论上来说只要我们的数组长度够长,误判的概率就会越低,这种问题就根据实际情况来就好了。

缓存雪崩

 

当某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求进来直接打到 DB上,这样可能导致整个系统的崩溃,称为雪崩。

雪崩和击穿、热 key 的问题不太一样的是,他是指大规模的缓存都过期失效了。

 

针对雪崩几个解决方案:

针对不同key设置不同的过期时间,避免同时过期

限流,如果redis宕机,可以限流,避免同时刻大量请求打崩DB

二级缓存,同热key的方案。

稳定性

 

熔断:比如营销服务挂了或者接口大量超时的异常情况,不能影响下单的主链路,涉及到积分的扣减一些操作可以在事后做补救。

限流:对突发如大促秒杀类的高并发,如果一些接口不做限流处理,可能直接就把服务打挂了,针对每个接口的压测性能的评估做出合适的限流尤为重要。

降级:熔断之后实际上可以说就是降级的一种,以熔断的举例来说营销接口熔断之后降级方案就是短时间内不再调用营销的服务,等到营销恢复之后再调用。

预案:一般来说,就算是有统一配置中心,在业务的高峰期也是不允许做出任何的变更的,但是通过配置合理的预案可以在紧急的时候做一些修改。

核对:针对各种分布式系统产生的分布式事务一致性或者受到攻击导致的数据异常,非常需要核对平台来做最后的兜底的数据验证。

比如下游支付系统和订单系统的金额做核对是否正确,如果收到中间人攻击落库的数据是否保证正确性。

总结

其实可以看到,怎么设计高并发系统这个问题本身他是不难的,无非是基于你知道的知识点,从物理硬件层面到软件的架构、代码层面的优化,使用什么中间件来不断提高系统的抗压能力。

但是这个问题本身会带来更多的问题,微服务本身的拆分带来了分布式事务的问题,HTTP、RPC 框架的使用带来了通信效率、路由、容错的问题。

MQ 的引入带来了消息丢失、积压、事务消息、顺序消息的问题,缓存的引入又会带来一致性、雪崩、击穿的问题。

数据库的读写分离、分库分表又会带来主从同步延迟、分布式 ID、事务一致性的问题,而为了解决这些问题我们又要不断的加入各种措施熔断、限流、降级、离线核对、预案处理等等来防止和追溯这些问题。

作者:科技缪缪

编辑:陶家龙

出处:科技缪缪(ID:kejimiumiu)

来源:科技缪缪内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯