最佳实践:高并发之扩容思路
系统在业务平峰期间运行稳定、性能良好,但在大流量时就会出现各种各样的问题,例如接口时延变大,CPU占用率升高、频繁发生Full GC、代码中出现死锁等等。大流量意味着高并发,高并发也是很多开发人员所期望拥有的经验,一方面能够接触更加复杂的业务场景,提高自身能力,另一方面对于高并发的解决思路需要依靠经验积累,通过踩坑填坑的方式不断精进。而这其中扩容又是最常见的应对高并发场景的思路。
什么是扩容
扩容,通常指为了提高系统的处理能力,而采取的增加计算或其他资源的一系列措施,以此来提升系统的性能。
传统意义上的扩容一般只单单针对硬件计算资源,策略上可以分为两种,一种是对单机整体扩容,也就是整机的CPU、内存、磁盘等等;另一种就是扩容对应的组件,例如提高CPU性能,升级读写性能更优秀的磁盘等。而在云原生、微服务等技术越来越普及后,扩容的概念也不再单单指计算资源,而是扩展到架构领域,例如流量高峰期针对某一中间件资源进行扩容,或针对某一核心服务进行扩容,这使得扩容能够更高效、更有目的性。
随着技术的发展和业务的复杂度的上升,也要求扩容更有目标性,更快速,这就要求在实践中对于扩容的目标、策略、方法,以及系统的架构设计都要有深入的理解,同时也需要有合适的工具对其进行技术支撑。
扩容目标
扩容是为了确保系统在面临高并发访问、大数据处理等场景时,能够保持良好的性能和稳定性,不会因为资源不足而出现服务响应缓慢、系统崩溃等问题。
扩容是一个系统性的工程,需要综合考虑成本、性能、可靠性等因素,并采取适当的策略和技术来实现。目标具体来看主要有以下几点。
提高系统并发能力:通过增加系统资源,提高系统处理请求的能力,从而应对高 并发访问。
保证系统稳定性:在扩容过程中,确保系统运行稳定,避免因资源分配不当导致的性能波动。
降低成本:在满足业务需求的前提下,合理利用现有资源,降低扩容成本。
易于实现:能够快速做出响应,同时不影响正常的业务功能设计和开发。
常见扩容思路
架构层面
从架构上来看扩容可以分为两大类:
横向扩展(scale-out)
又名水平扩展,即用更多的机器来支撑大量的请求。常见的集群模式往往就是这种思路。以运送货物为例,当大量货物需要运输时,使用更多的货车进行运输。
纵向扩展(scale-up)
又名垂直扩展,扩展一个节点或单一机器的能力,使一个点能够支撑更大的请求。例如使用高性能计算服务器,其往往有更强的单体计算能力。同样以运送货物 为例,当大量货物需要运输时,将货车升级,让每个货车更大更快。
业务层面
从业务类型上来看扩容也可以分为:
读操作扩展
如果系统中读操作占大多数,那么可以通过找到关键的资源瓶颈,对其进行扩容或增加其资源进行扩展。例如MySQL是资源瓶颈,那么增加多个只读从库,业务高峰期扩展只读库副本数进行横向扩展,亦可以提高MySQL服务器的性能采用垂直扩展的思路增强其处理能力;增加一个或多个redis将热点数据进行缓存等。这都是通过读写分离的思想,针对性的以业务角度出发对读操作进行扩展。
写操作扩展
如果系统中写操作为主,往往提高单个节点的能力性价比较低,通常考虑使用HBase、MiniIO等分布式存储方案,方便后期不断进行水平扩展。
异步处理
将一些有延迟、等待任务放入消息队列中,利用中间件实现业务功能,提高系统吞吐量。或通过异步的方式对服务进行解耦,一方面便于针对性进行扩容,另一方面将时延敏感度较低的业务分离,提高核心资源利用率。
利用云服务、CDN等第三方能力
对于一些静态资源或大文件读写场景,使用CDN缓存的方式来减少自身服务器的压力,同时云服务厂商很多功能目前都已提供弹性扩容能力,按需付费即可获得自动化的扩容缩容能力。以阿里云函数计算为例,预留模式中只需要配置好弹性伸缩规则,即可自动根据流量情况进行实例的扩容缩容。
实际业务场景中,往往不是单一地使用某一种扩容方法就能解决问题,选择哪种扩容思路取决于具体的业务需求、系统架构、预算以及预期的性能目标。最佳实践应当是结合多种扩容策略,实现灵活、高效、成本合理的系统扩展。
评估扩容需求的步骤和方法
评估是否需要扩容以及需要扩容哪些资源,通常需要进行全面的系统分析和性能监控,并且要能够准确地识别系统运行状态。
1. 性能监控:
使用监控工具(如Prometheus)来收集系统的性能数据,包括CPU使用率、内存使用率、磁 盘I/O、网络流量、响应时间等。
分析性能数据,找出系统的瓶颈所在。例如CPU使用率经常处于高位,则可能需要增加计算资源;如果磁盘I/O压力大,可能需要升级磁盘或使用更快的存储解决方案。
2. 容量规划:
根据业务增长趋势和用户需求预测,进行容量规划。考虑未来的数据增长、用户增长和交易量增长等,预测所需的资源量。
对比当前资源容量和预测的资源需求,确定是否需要扩容以及需要扩容的规模。
3. 压力测试:
通过模拟高负载场景来测试系统的性能极限。这可以帮助确定系统在压力下的表现,以及哪些资源会成为瓶颈。
分析负载测试结果,找出系统在哪些方面需要改进或增加资源。
4. 应用分析和优化:
分析应用代码和架构,找出性能优化点。通过优化代码或改进架构,减少对资源的依赖。
使用 Kindling-OriginX 确定扩容策略
这里以 Kindling-OriginX 为例,说明如何使用其提供的北极星指标体系找到高并发场景下的瓶颈点,为扩容方向提供明确指引。
北极星指标
cpu
程序代码执行所消耗的CPU cycles
runq
线程的状态是Ready,如果CPU资源是充分,线程应该被调度到CPU上执行,但是由于各种原因,线程并未调度到CPU执行,从而产生的等待时间。
net
网络时间,主要包括DNS,TCP建连,常规网络调用
futex
通常指的是一个线程在尝试获取一个futex锁时因为锁已经被其他线程占用而进入等待状态的时间。在这段时间内,线程不会执行任何操作,它会被内核挂起。
file
存储操作时间
通过上述指标的具体时间,我们就可以知道每一次调用程序具体耗时在哪些地方,结合SLO实时异常检测确认是否出现了影响用户体验的问题,即可快速对是否需要进行扩容,哪些节点,哪些资源需要扩容做出判断。
实战案例解析
下面以使用 Kindling-OriginX 为例,说明在实际生产环境中,如何有针对性地确定和实施扩容策略。以数据为导向,告别盲目使用扩容、升配试一试的方式应对高并发场景下的各种问题。
案例一
业务高峰期间通过SLO入口检测发现业务入口延迟变大,已经影响到用户体验。
通过查看慢调用的链路传播链,定位到造成影响的服务节点是ts-train-service
查看节点的北极星指标可以看到runq耗时异常,参照runq指标的含义,说明当前CPU不足,这种情况下优先考虑对该服务进行扩容。
案例二
该案例中定位到产品问题的节点是ts-order-service
同样利用北极星指标分析,发现是由于file耗时异常导致, 此时盲目进行横向扩容并不能解决问题,对该存储相关操作异常的原因继续下钻分析。
下钻后 Kindling-OriginX 将会定位到具体文件,及该文件具体的读写指标数据,通过这些数据首先分析该读写操作是否是正常业务行为,如果是正常业务行为,接下来根据读写情况来看是否需要对其进行读或者写扩展。如果文件读写较为平均,那么考虑对问题节点或机器的磁盘性能进行增加,采取垂直扩展的思路先解决问题,之后从代码设计层面和系统架构层面考虑重新设计该文件操作的业务流程。
通过上面两个案例可以看到,高并发场景下很多性能问题都可以通过扩容解决,但同样不存在银弹,盲目的扩大服务容量或提高机器性能,都可能只是无效扩容。没有合适的工具发现瓶颈点,选择了错误的扩容策略和方向,只会浪费时间、金钱、人力,却不能真正解决问题。
小结
在高并发场景中选择合适的扩容策略,往往要对系统整体架构和各个业务系统非常熟悉,同时也要对常见系统性能优化方式有深入了解,这都需要大量的经验积累和技术能力,也需要能合理根据实际场景选用先进的工具获取系统中的关键运行信息,不能一味生搬硬套业内经验。可以通过 Kindling-OriginX 等工具对系统执行过程进行无盲区的观测,以数据为导向,采取对症下药的扩容方式。