零、前言
面试的场景题目,很多是搜集的。相应的解决方案,其实大同小异。因为面试的短暂时间,只能够展示我们的技能,并不足以开启实际解决问题。
有些解决方案是存在类似的。内容存在借鉴部分,但是保证每篇文章都是经过本人的思考的。
一、精准把握题意
面试官或许会简要提出一个挑战:若QPS(每秒查询率)骤增10倍,你将如何设计系统以应对?面对此题,你的关注点务必聚焦于“提升10倍”这一核心。因为突如其来的增长,短期内仅能依靠高可用策略应急,诸如限流、降级、熔断及预案等三板斧。但这些话题很快便能讨论完毕,毕竟它们背后的支撑组件,并非面试时深入探讨源码的时刻,也无法充分展现你的技术深度。因此,你的重心应落在如何构建一个能够承载10倍流量增长的系统架构上,进而自然过渡到高可用性的讨论,形成一个完整且闭环的回答逻辑。
二、互联网架构的演进之路
既然已明确回答的关键点,接下来便是找准切入点。互联网的架构发展,从单体架构起步,历经服务架构,现已迈入微服务架构时代,并预示着未来函数式架构的可能性。在解答前,我们需先设定业务背景,进行具体问题具体分析。假设当前仍为单体架构,最直接的方法是增加服务器数量,但更优策略是逐步进行架构拆分,向微服务转型。这往往意味着,当发现问题时,业务规模已相当庞大,留给拆分的时间窗口较为紧迫。此时,可通过硬件升级暂时缓解压力,随后着手服务拆分,以彻底解决长期问题。
为何单纯增加服务器数量只能治标而不能治本?原因在于数据库资源的稀缺性。所有应用均需连接数据库,这限制了应用的数量。服务器无法实现水平扩展,仅能依赖纵向升级,从小型机到大型机,乃至超级计算机,最终或许会让财务部门宣告公司财务状况告急。
三、系统优化策略
第一步:紧急增加服务器数量,以解燃眉之急,同时着手服务拆分,为后续扩展预留空间。
第二步:实施精细化管理,进行全链路压测,并针对核心接口进行定向优化。
第三步:引入消息队列、缓存机制,实施分库分表策略。对于业务接口,采用读写分离,以平稳实现资源扩容。
第四步:构建高可用体系。至此,基础优化工作已完成,接下来需加强系统保护机制,如限流、降级、熔断及预案等,确保系统稳健运行。
四、策略实践详解
4.1 硬件扩容与微服务拆分并行
在互联网初期,单体架构足以支撑日常业务需求,所有业务服务均集成于单一项目中,部署于同一台物理服务器上。交易系统、会员信息、库存、商品等所有业务模块相互交织,一旦服务器出现故障,所有业务将全面瘫痪。随着流量的激增,单体架构的弊端逐渐显现。
于是,集群架构应运而生,它巧妙地解决了单机难以承受的压力问题。最直观且有效的策略便是进行水平拓展,即横向扩容。通过精心配置的负载均衡机制,能够将庞大的压力流量均匀地分摊到多台机器上,从而有效地规避了单点故障可能引发的服务不可用风险。
然而,随着业务的蓬勃发展,将所有业务场景糅合在一个项目中进行开发与维护,逐渐变得举步维艰。即便是微小的需求变动,也往往牵一发而动全身,需要整个服务进行发布,这无疑增加了操作的复杂度。与此同时,代码合并冲突的频率也日益攀升,给团队协作带来了不小的挑战。更为严重的是,这种集中式架构使得线上故障的风险大幅上升,一旦某个环节出现问题,很可能波及整个系统。正是在这样的背景下,微服务的架构模式应运而生,为业务的发展注入了新的活力。
将每个独立的业务模块进行拆分并独立部署,不仅显著降低了开发与维护的成本,还使得集群所能承受的压力得到了显著提升。如此一来,即便是微小的改动,也无需再“牵一发而动全身”,影响整个系统的稳定运行。从高并发的角度来看,这些优势似乎都可以归结为:通过服务的拆分以及集群物理机器的扩展,整体的系统抗压能力得到了实质性的增强。然而,随着拆分带来的便利,一系列高并发系统需要面对的问题也随之浮现,成为了我们必须正视并解决的挑战。
4.2 高性能 RPC
微服务架构所带来的优势与便捷性不言而喻,但与此同时,各个微服务间的通信问题也浮出了水面。传统的HTTP通信方式在性能方面显然并非最佳选择,大量的请求头及其他非必要信息的传输无疑是对性能的极大浪费。此时,引入诸如Dubbo这类高性能的RPC(远程过程调用)框架便显得尤为重要。
4.2 Dubbo RPC 的卓越性能
有小伙伴进行了详尽的对比测试,结果显示,Dubbo RPC 的性能竟是 Feign RPC 的10倍之多。假设原先客户端的 QPS 为9000,经过负载均衡策略的合理分配,每台机器承担的 QPS 为3000。而当我们将 Feign HTTP RPC 替换为 Dubbo RPC 后,接口的响应时间大幅缩短,单体服务与整体的 QPS 均有显著提升。更为值得一提的是,RPC 框架本身往往内置了负载均衡、熔断降级等机制,这为维护整个系统的高可用性提供了强有力的保障。谈及 RPC,作为国内广泛采用的 Dubbo,其基本原理自然成为了我们接下来探讨的重点。
4.3 消息队列:削峰填谷与解耦利器
消息队列(MQ)的作用,想必大家已耳熟能详。其主要功能包括:
削峰填谷,有效缓解系统压力,实现流量的平滑处理。
解耦微服务,通过同步转异步的方式,降低服务间的依赖程度。
对于那些无需同步执行的接口,我们可以巧妙地引入消息队列,实现异步处理,从而缩短接口响应时间。例如,在交易完成后,需要扣除库存并可能给会员发放积分。本质上,发放积分的动作属于履约服务,对实时性要求不高,只需确保最终一致性,即履约成功即可。对于这类性质的请求,完全可以通过 MQ 进行异步处理,这样不仅提高了系统抗压能力,还优化了用户体验。
4.4 三级缓存架构
缓存,作为高性能的代名词,在特定业务场景中,往往能够承载90%以上的热点流量。以秒杀活动为例,这类场景下的并发QPS可能高达数十万。若能在活动前引入缓存并进行预热,便能极大程度地减轻数据库的压力。试想,10万的QPS对于单机数据库而言,可能是难以承受之重,但对于像Redis这样的高性能缓存系统来说,却完全能够游刃有余地应对。
以秒杀系统为例,活动预热期间,商品信息可以提前缓存以供查询服务使用,库存数据同样可以提前缓存。在秒杀过程中,下单流程可以完全依赖于缓存进行库存扣减,秒杀结束后,再异步将相关数据写入数据库。这样一来,数据库所承受的压力便大幅减小。
4.5 数据库分库分表策略
在整个系统中,所有流量的查询与写入最终都会落到数据库上,因此,数据库是支撑系统高并发能力的核心所在。如何有效降低数据库的压力,提升数据库的性能,是支撑高并发场景的基石。针对这一问题,我们主要采用读写分离和分库分表的方式来解决。对于系统流量而言,其形态往往呈现出一个漏斗状。例如,我们的日活跃用户(DAU)为20万,但实际上每天访问提单页的用户QPS可能仅有3万,而最终转化为下单支付成功的QPS更是只有1万。在这种情况下,系统中的读操作数量是远大于写操作的。因此,我们可以通过读写分离的方式,将读操作与写操作分离到不同的数据库上,从而有效降低数据库的压力。
读写分离相当于采用了数据库集群的方式,有效减轻了单节点的压力。然而,面对数据的迅猛增长,原先的单库单表存储模式已难以支撑业务的持续发展,这时,对数据库进行分库分表就显得尤为必要。对于微服务架构而言,垂直分库往往已是既定之举,接下来的重点大多落在分表的策略上。
4.6 高可用性保障
4.6.1 熔断机制
当营销服务出现故障或接口出现大量超时等异常情况时,我们必须确保这些异常不会波及下单的主链路。例如,涉及积分的扣减等操作,可以在事后进行补救。
为此,我们需要对服务的依赖关系进行细致梳理,明确区分强依赖和弱依赖。强依赖关系意味着,一旦某个服务不可用,整个系统将受到严重影响。而弱依赖关系则允许我们在某个服务出现故障时,承担部分损失,以保障自身系统的稳定运行。当然,这种保障是被动的,即在我们发现某个服务出现故障后,才会采取相应的措施。
4.6.2 限流策略
面对大促、秒杀等高并发场景,如果不对某些接口进行限流处理,服务很可能会因过载而崩溃。因此,我们需要根据每个接口的压测性能评估结果,制定合适的限流策略。
限流的核心思想是:我们只服务一定数量的用户,超过这个数量的用户将被视为非目标客户,并被系统引导至其他渠道,以确保我们能够专注于服务核心客户。
4.6.3 降级处理
熔断机制实际上可以视为降级处理的一种。以熔断机制为例,当营销接口触发熔断后,降级方案就是在短时间内停止调用营销服务,直到营销服务恢复正常后再重新调用。
4.6.4 预案准备
通常,即使在业务高峰期,我们也不允许对统一配置中心进行任何变更。然而,通过预先配置合理的预案,我们可以在紧急情况下进行一些必要的修改。
4.6.5 数据核对
针对分布式系统可能产生的分布式事务一致性问题,或受到攻击导致的数据异常,我们需要一个核对平台来进行最后的数据验证。例如,我们需要核对下游支付系统和订单系统的金额是否一致,以及在受到中间人攻击时,确保落库数据的正确性。
4.6.6 熔断与降级的区别
熔断与降级是系统自我保护的重要机制,它们的主要区别在于概念、触发条件和归属关系。
熔断机制是一种全局性的保护策略,当请求失败率达到一定比例时触发,旨在防止系统因个别服务的故障而整体崩溃。在分布式系统中,当某个微服务的错误率达到预设阈值时,熔断机制将被激活,暂时停止对该服务的调用,以防止故障扩散。
降级则是一种更为具体的应对措施,通常在系统高负载或资源紧张时触发。它通过降低非核心功能的优先级或关闭部分服务,来确保核心功能的正常运行。例如,在电商交易系统中,为了保障支付功能的可用性,在双11期间可能会暂时关闭评论、物流等非核心功能。
在实际应用中,熔断与降级常常结合使用,以确保系统在异常情况下能够有序处理请求,避免雪崩效应。熔断机制通过监控系统性能指标,及时发现并处理服务故障;而降级策略则在系统负载过高时,主动减少非核心功能的使用,以保障核心功能的稳定运行。
4.7 总结
设计高并发系统本身并不难,它主要是基于我们所掌握的知识点,从物理硬件层面到软件架构、代码层面的优化,以及利用中间件来不断提升系统的抗压能力。然而,这个问题本身会引发更多的问题。例如,微服务的拆分带来了分布式事务的挑战;HTTP、RPC框架的使用带来了通信效率、路由和容错的问题;MQ的引入则带来了消息丢失、积压、事务消息和顺序消息的处理难题;缓存的引入又可能引发一致性、雪崩和击穿等问题;数据库的读写分离、分库分表则可能带来主从同步延迟、分布式ID生成和事务一致性的问题。为了解决这些问题,我们不断加入各种措施,如熔断、限流、降级、离线核对和预案处理等,以防止和追溯这些问题,确保系统的稳定运行。