服务端高并发分布式结构进阶之路
序言
- 在技术求知的旅途中,鉴于多数读者缺乏在中大型系统实践中的亲身体验,难以从宏观角度把握某些概念,因此,本文特选取“电子商务应用”作为实例,详细阐述从百级至千万级并发场景下服务端架构的逐步演变历程。同时,将逐一列出每个演进阶段所涉及的关键技术,旨在为大家构建一个关于架构演进的全面认知框架,以便大家在后续的深入学习中能够拥有更为广阔的视野和坚实的基础。
目录
(一)常见概念
1.1 基本概念
1.1.1 应用(Application)/系统(System)
1.1.2 模块(Module)/组件(Component)
1.1.3 分布式(Distributed)
1.1.4 集群(Cluster)
1.1.5 主(Master)/从(Slave)
1.1.6 中间件(Middleware)
1.2 评价指标(Metric)
1.2.1 可用性(Availability)
1.2.2 响应时长(ResponseTimeRT)
1.2.3 吞吐(Throughput)vs并发(Concurrent)
(二)架构演进
2.1 单机架构
2.2 应用数据分离架构
2.3 应用服务集群架构
2.4 读写分离/主从分离架构
2.5 引入缓存⸺冷热分离架构
2.6 垂直分库
2.7 业务拆分⸺微服务
(三)总结
(一)常见概念
在正式引入架构演进之前,为避免大家对架构中的概念完全不了解导致低效沟通,优先对其中⼀ 些比较重要的概念做前置介绍:
1.1 基本概念
1.1.1 应用(Application)/系统(System)
为了实现一系列服务的功能,可能需要开发一个程序或是多个相互协作的程序集合。以日常生活中的例子来类比:就像是为了达成一个目标,而组建的一个单独个体或者一群彼此配合的人员所形成的团队。
1.1.2 模块(Module)/组件(Component)
当应用程序变得相当复杂时,为了明确划分职责,我们会把那些职责明确且内部联系紧密的部分提取出来,形成抽象概念,以便于理解和管理。用生活中的实例来比喻:就像在军队中,为了攻占某个据点,会将士兵划分为不同的专业小组,如突击小组、爆破小组、掩护小组和通信小组等。
1.1.3 分布式(Distributed)
当一个系统的多个模块被分别部署在不同的服务器上运行时,这样的系统就被称为分布式系统。例如,Web服务器与数据库服务器各自独立运行在不同的服务器上,或者多台Web服务器也被分别部署在各自独立的服务器上。用生活中的实例来比喻:为了更有效地满足实际需求,原本在同一办公地点工作的一个小组被分散到多个城市的不同工作地点,通过远程协作来完成目标。这些分散在不同地点的模块之间的通信,基本上需要依赖网络支持来实现。
1.1.4 集群(Cluster)
部署在多台服务器上,旨在实现特定目标的单个或一组特定组件的集合,整体上被称为集群。例如,多个MySQL实例运行在不同的服务器上,共同提供数据库服务,这样的组合可以被称为一个数据库集群。用生活中的实例来比喻:为了达成攻克防御坚固的大城市的军事目标,指挥部将大量炮兵部队集结起来,形成一个强大的炮兵打击集群。
关于分布式与集群的区别,通常我们不必过于拘泥于两者概念的细微差别。若深究,分布式更多强调的是物理分布状态,即工作在不同物理位置上的服务器通过网络通信相互协作以完成任务;而集群则更侧重于逻辑上的组合形态,即是否为了完成某个特定的服务目标而将多个组件或系统集合在一起。
1.1.5 主(Master)/从(Slave)
在集群中,往往会有一个程序或节点被赋予更多的职责,这个角色通常被称为主节点;而其他承担辅助或次要职责的节点则被称为从节点。以MySQL集群为例,如果只有其中一台服务器上的数据库允许执行数据的写入操作(如增加、删除、修改等),而其他数据库的数据修改都需要从这台数据库进行同步,那么这台数据库就被称为主库,而其他数据库则被称为从库。
1.1.6 中间件(Middleware)
一类软件,其作用是促进不同应用程序之间的通信,充当着不同技术、工具和数据库之间的连接桥梁。以生活中的实例来比喻:一家饭店在初创时,可能每天都会亲自前往市场挑选食材;但随着业务的扩大,饭店成立了一个专门的采购部门,这个部门专门负责食材的采购工作,从而成为了厨房与菜市场之间的连接纽带。
1.2 评价指标(Metric)
1.2.1 可用性(Availability)
可用性衡量的是在单位时间段内,系统能够正常提供服务的概率或期望值。例如,年化系统可用性可以通过系统正常提供服务的时间与一年总时间的比值来计算。这里隐含了一个关键指标,即如何评估系统服务是否正常,但对此我们不进行深入探讨。通常,我们会用“几个9”来简要描述系统的可用性,如4个9代表系统能提供99.99%的可用性,5个9则是99.999%的可用性,以此类推。而在日常交流中,我们更常用“高可用性(High Availability, HA)”这一非量化指标来表达我们对系统可用性的追求。
1.2.2 响应时长(ResponseTimeRT)
响应时长是指用户完成输入到系统给出反馈的时间间隔。以点外卖业务为例,响应时长就是从用户完成点单到拿到外卖的时刻之间的时间差。通常,我们需要关注的最长响应时长、平均响应时长和中位数响应时长。这个指标原则上越小越好,但在实际实现中,可能需要根据具体情况进行权衡。
1.2.3 吞吐(Throughput)vs并发(Concurrent)
吞吐衡量的是单位时间段内,系统能够成功处理的请求数量。而并发则是指系统在同一时刻能够支持的最大请求量。以高速公路为例,如果一条车道一分钟内可以通过20辆车,那么这条车道的吞吐量就是20辆/分钟,而并发量可以理解为车道上同时能容纳的车辆数(虽然在实际中,这个并发量很难直接获取,很多时候会用极短时间内的吞吐量来近似代替)。在日常交流中,我们常用“高并发(High Concurrent)”这一非量化指标来简要表达系统对并发处理能力的追求。
(二)架构演进
2.1 单机架构
在初创阶段,我们会依靠一支精炼的技术团队,迅速将业务系统推向市场以接受检验,并能够敏捷的应对需求的变化。幸运的是,由于前期用户访问量较少,系统性能和安全性方面的压力并不大,加之系统架构相对简单,无需配备专业的运维团队。因此,在这个阶段,采用单机架构是一个明智的选择。
当用户在浏览器中输入www.taobao.com时,首先会通过DNS服务将这一域名转换成对应的IP地址,然后浏览器会访问这个IP地址所对应的应用服务。
2.2 应用数据分离架构
系统上线后,我们取得了预期的成功,吸引了一批忠实用户,导致系统访问量持续增长,逐渐接近硬件资源的承载上限。与此同时,团队也积累了丰富的业务流程经验。面对日益增长的性能压力,我们未雨绸缪,决定进行系统重构和架构升级,以增强系统的承载能力。然而,由于预算仍然有限,我们选择了成本效益最高的方案,即将应用与数据分离,以此来实现系统承载能力的有效提升。
和之前架构的主要区别在于将数据库服务独立部署在同⼀个数据中心的其他服务器上,应用服务通过网络访问数据。
2.3 应用服务集群架构
我们的系统深受用户喜爱,甚至出现了爆款产品,导致单台应用服务器已无法满足需求。此时,单机应用服务器首先遭遇了性能瓶颈。技术团队面临两种解决方案,并就此展开了热烈讨论:
-
垂直扩展(Scale-Up):通过采购性能更强大但价格更高的应用服务器来应对增加的流量。此方案的优势在于无需对系统软件做任何调整;然而,劣势同样显著:硬件性能与价格的增长关系非线性,即性能提升两倍可能需要花费超过四倍的价格,且硬件性能提升存在明显上限。
-
水平扩展(Scale-Out):通过调整软件架构,增加应用层硬件,将用户流量分散到不同的应用服务器上,从而提升系统的承载能力。此方案的优势在于成本相对较低,且提升的空间很大。但劣势是增加了系统的复杂性,要求技术团队具备更丰富的经验。
经过团队的深入研究、调研和讨论,最终决定采用水平扩展方案来解决此问题。但这需要引入一个新的组件——负载均衡器。为了解决用户流量应分发到哪台应用服务器的问题,需要一个专门的系统组件进行流量分发。实际上,负载均衡不仅应用于应用层,还可能涉及其他网络层。同时,流量调度算法也多种多样,这里简要介绍几种常见的:
-
轮询(Round-Robin)算法:非常公平地将请求依次分配给不同的应用服务器。
-
加权轮询(Weighted Round-Robin)算法:为不同的服务器(如性能差异)分配不同的权重,实现“能者多劳”。
-
一致性哈希(Consistent Hashing)算法:通过计算用户特征值(如IP地址)的哈希值,根据哈希结果进行分发。其优点是确保来自同一用户的请求总是被分配给指定的服务器,类似于我们平时遇到的专属客户经理服务。
2.4 读写分离/主从分离架构
在上一节中,我们讨论了通过负载均衡将用户请求分发到不同的应用服务器进行并行处理,并可根据业务需求动态增加服务器数量以减轻压力。然而,当前的架构中,无论服务器数量如何扩展,这些请求最终都会涉及数据库的读写操作。当数据量达到一定程度时,数据库的压力将成为系统承载能力的瓶颈。那么,我们能否像扩展应用服务器那样扩展数据库服务器呢?答案是否定的,因为数据库服务具有其独特性:一旦数据被分散到不同的服务器上,数据的一致性就无法得到保证。这里的数据一致性是指,对于同一个系统,无论何时何地访问,我们都应该看到一个统一且一致的数据状态。以银行管理的账户金额为例,如果收到一笔转账后,一个数据库的数据更新了,而另一个数据库没有更新,那么用户查询到的存款金额将是错误的。
为了解决这个问题,我们采用了主从数据库架构。我们保留一个主要的数据库作为写入数据库(主库),其他数据库作为从属数据库(从库)。从库的所有数据都来自主库,并通过同步机制保持与主库数据的一致性。为了分担数据库的压力,我们将写数据请求全部交给主库处理,而将读请求分散到各个从库中。由于大多数系统中读写请求的比例是不均衡的,例如100次读请求对应1次写请求,因此通过将读请求分散到各个从库处理后,数据库的整体压力会显著降低。当然,这个过程并非没有成本,主库到从库的数据同步需要一定的时间,但这个问题我们在此暂不深入讨论。
应用中需要对读写请求做分离处理,所以可以利用⼀些数据库中间件,将请求分离的职责托管出去。
2.5 引入缓存⸺冷热分离架构
随着访问量的持续增长,我们发现业务中某些数据的读取频率远高于其他数据,我们将这部分数据定义为热点数据,而与之相对的是冷数据。为了提升热点数据的读取响应时间,我们可以采取增加本地缓存以及在外部部署分布式缓存的策略,缓存热门商品信息或热门商品的HTML页面等内容。通过缓存机制,我们可以在读写数据库之前拦截掉绝大部分请求,从而极大地减轻数据库的压力。
在这一过程中,我们会运用到多种技术,比如使用memcached作为本地缓存解决方案,使用Redis作为分布式缓存系统。然而,缓存的引入也带来了一系列需要关注的问题,如缓存一致性、缓存穿透/击穿现象、缓存雪崩问题以及热点数据集中失效等。这些问题都需要我们在设计和实现缓存策略时给予充分的考虑和应对。
2.6 垂直分库
随着业务数据量的膨胀,单一数据库已难以满足存储需求,因此,我们可以根据业务逻辑,将数据分散存储。例如,针对评论数据,我们可以采用商品ID进行哈希运算,然后路由到相应的表中存储;而对于支付记录,则可以按小时为单位创建表,并进一步将每个小时的数据拆分成小表,通过用户ID或记录编号来引导数据路由。只要确保实时操作的表数据量保持在一个较小的范围内,且请求能够均匀分配到部署在多台服务器上的小表上,数据库便可通过水平扩展的方式来提升性能。前面提及的Mycat也支持在大表拆分为小表的情况下的访问控制。
然而,这种做法显著提升了数据库运维的复杂性,对DBA的专业能力提出了更高要求。当数据库设计到这种架构时,它实际上已经是一个逻辑上的分布式数据库整体,但内部的不同组件是由不同的系统来实现的。比如,分库分表的管理和请求分发由Mycat承担,SQL的解析工作则由单机数据库完成,读写分离可能依赖于网关和消息队列,而查询结果的汇总则可能由数据库接口层负责。这种架构实质上是大规模并行处理(MPP)架构的一种具体实现。
2.7 业务拆分⸺微服务
随着团队规模的扩大和业务的发展,我们将业务分配给不同的开发团队进行独立维护。每个团队负责实现自己的微服务,并通过隔离数据直接访问的方式,确保各服务间的独立性。为实现服务间的相互调用与关联,我们可以采用Gateway网关、消息总线等技术手段。此外,为了提升效率和复用性,我们还可以将一些通用的业务功能,如用户管理、安全管理、数据采集等,提取出来作为公共服务提供给各个团队使用。
(三)总结
至此,一个相对合理的高可用、高并发系统的基本框架已经显现。需要注意的是,上述架构的演变顺序是基于某个侧面进行的单独改进,但在实际情境中,可能会同时面临多个问题,或者首先遇到瓶颈的可能是其他方面。因此,在实际操作中,我们应针对具体问题采取相应解决方案。例如,在政府类项目中,尽管并发量可能不大,但业务需求却可能十分复杂,此时,高并发就不是首要解决的问题,而满足丰富的业务需求则成为优先考量。
对于一次性实施且性能指标明确的系统,其架构设计只需满足既定的性能指标要求即可,但应预留扩展接口以应对未来可能出现的需求。而对于不断发展的系统,如电商平台,其架构设计则需考虑未来一段时间内的用户量和性能指标,并随着业务的增长不断迭代升级,以支持更高的并发量和更丰富的业务功能。
“大数据”实际上是对海量数据采集、清洗、转换、存储、分析以及数据服务等场景解决方案的总称。在每个场景中,都有多种可选的技术,如数据采集工具Flume、Sqoop、Kettle等,数据存储方案包括分布式文件系统HDFS、FastDFS以及NoSQL数据库HBase、MongoDB等,数据分析则可能涉及Spark技术栈、机器学习算法等。总的来说,大数据架构是根据业务需求整合各种大数据组件而成的,它通常提供分布式存储、分布式计算、多维分析、数据仓库以及机器学习算法等能力。而服务端架构则更多地关注应用组织层面的设计,其底层能力往往由大数据架构来提供。