大型网站架构核心要素之扩展性:可扩展架构

前言

续上节大型网站架构核心要素性能之后,我们今天要讲的是第四个要素:扩展性,什么叫扩展性呢?简单的说就是在对现有系统影响最小的情况下,系统功能可持续扩展及提升的能力,讲扩展性之前,我先讲下扩展性和伸缩性的区别,因为这两个点经常有人会混淆;

扩展性:指对现有系统影响最小的情况下,系统功能可持续扩展或提升的能力,表现在系统基础设施稳定,不需要经常变更,应用之间较少依赖和耦合,对需求变更可以快速敏捷响应。它是系统设计层面的开闭原则(对扩展开放,对修改关闭),架构设计考虑未来功能扩展,当系统增加新功能时,不需要对现有系统的结构和代码进行修改;

伸缩性:指系统能够通过增加或者减少自身资源规模的方式来增强或者减小自己计算处理事务的能力,如果这种增减是成比例的,那就称作线性伸缩性。在网站架构中,通常指利用集群的方式增加服务器数量、提高系统的整体事务吞吐能力。

搞清楚扩展性和伸缩性之间的区别之后,那么我们就从下面这几个方面开始讲如何构建可扩展性的架构设计:构建可扩展的网站架构、利用分布式消息队列降低系统耦合性、利用分布式服务打造可复用的业务平台、可扩展的数据结构、利用开放平台建设网站生态圈

 

构建可扩展的网站架构

开发低耦合系统是软件设计的终极目标之一,低耦合的系统更容易扩展,低耦合的模块更容易复用,一个低耦合的系统设计也会让开发过程和维护变得更加轻松和容易管理。那么如何分解系统的各个模块,如何定义各个模块的接口、如何复用组合不同的模块构成一个完整系统就成了软件设计中最有挑战的工作;

设计网站的可扩展性架构的核心思想在于模块化,并在此基础之上,降低模块间的耦合性,提高模块的复用性;上节我们讲到网站通过分层和分割的方式进行架构伸缩,分层和分割也是模块化设计的主要手段,利用分层和分割的方式将软件分为若干个低耦合的独立化组件,这些组件模块以消息传递及依赖调用的方式聚合成一个完整的系统。

在大型网站中这些模块通过分布式部署的方式,独立的模块部署在独立的服务器(集群)上,从物理上分离模块之间的耦合关系,进一步降低耦合性提高复用性;模块分布式部署以后具体聚合方式主要有分布式消息和分布式服务

 

利用分布式消息队列降低系统耦合性

如果模块之间不存在直接调用,那么新增的模块或者修改的模块就对其他模块影响最小,这样系统的可扩展性就会好些;

  • 事件驱动架构:通过在低耦合的模块之间传输事件消息,以保持模块的松散耦合,并借助事件消息的通信完成模块间合作,典型的事件驱动就是生产者消费者模式,在大型网站架构中,具体的实现方式很多,典型的就是分布式消息队列

    《大型网站架构核心要素之扩展性:可扩展架构》

    消息队列利用发布-订阅模式工作,消息发送者发布消息,一个或者多个消息接收者订阅消息。消息发送者是消息源,在对消息进行处理后将消息发送至分布式消息队列,消息接收者从队列中获取消息进行处理。整个过程中,消息发送者和消息消费者之间没有直接耦合,消息发送者将消息发送给消息队列之后就结束了对消息的处理,而消息接收者只需要从队列去获取消息进行处理就可以,不需要知道消息从何而来,由谁生产。对新增业务,如果需要调用消息,只需要订阅该消息,对原有系统和业务没有任何影响,从而实现了网站业务的可扩展性;

    消息接收者在对此消息进行过滤、处理、包装后,可以构成一个新的消息,继续发送出去,等待其他消息接收者订阅处理该消息;因此基于事件驱动的业务架构可以是一系列流程;

    事件驱动设计优势:由于消息发送者不需要等待消息接收者处理数据就可以直接返回,系统具有更好的响应延迟;同时在网站访问高峰的时候,消息可以暂时存储在消息队列里面,等待接收者根据自身负载处理能力控制消息处理速度,减轻数据库等后端存储的负载压力。

  • 分布式消息队列:队列是一种先进先出的数据结构,之前我们也讲过这点,分布式消息队列可以看作将这种数据结构部署到独立服务器上,应用程序可以通过远程访问接口使用分布式消息队列,进行消息存取操作,进而实现分布式的异步调用,基本原理如下图:

    《大型网站架构核心要素之扩展性:可扩展架构》

    消息生产者通过远程访问接口将消息推送给消息队列服务器,消息队列服务器将消息写入本地内存队列后立即返回成功响应给消息生产者。消息队列服务器根据消息订阅列表查找订阅该消息的消息消费者应用程序,将消息队列中的消息按先进先出的原则通过远程通信接口发送给消息消费者程序。

    目前开源的分布式消息队列有很多,比如RocketMQ,kafka等等,这些产品除了实现分布式消息队列的一般功能,在可用性,伸缩性,数据一致性,性能和可管理性方面也做了很大改善。

    伸缩性:由于消息队列服务器上的数据可以看作是被即时处理的,因此类似于无状态的服务器,伸缩性设计比较简单,将新服务器加入分布式消息队列集群中,通知生产者服务器更改消息队列服务器列表即可;

    可用性:为了避免消费者进程缓慢,分布式消息队列服务器内存空间不足造成的问题,如果内存队列满了,就会将消息写入磁盘,消息推送模块在将内存队列消息处理完以后,将磁盘内容加载到内存队列继续处理;

    防消息丢失:为了避免消息队列服务器宕机造成消息丢失,会将消息成功发送到消息队列的消息存储在消息生产者服务器上,等消息真正被消费者处理之后才删除消息,在消息队列服务器宕机时,生产者服务器会选择分布式消息队列服务器集群中其他的服务器发布消息;

    分布式消息队列可以很复杂,例如企业总线ESB或者面向服务架构SOA;也可以很简单,例如我们就用MySQL来当做分布式消息队列,生产者将消息存入数据库,消费者按生成时间从数据库取消息消费,这就实现了一个简单的分布式消息队列,而且还有结合MySQL的主从,读写分离手段达到较高的可用性和性能指标。

 

利用分布式服务打造可复用的业务平台

使用分布式服务是降低系统耦合性的另一个手段,如果说分布式消息队列通过消息对象分解系统耦合性,不同子系统处理同一个消息;那么分布式服务则是通过接口分解系统耦合性,不同子系统通过相同的接口进行服务器调用。

随着网站由小变大,表现为整个网站单一应用系统逐步膨胀发展的过程,在这个过程中,网站功能业务日益复杂,整个应该系统逐渐变成一个错综复杂的大系统,如果不使用分布式服务来解耦的话就会带来如下问题:编译部署困难;代码分支管理困难;数据库连接耗尽;新增业务困难。

解决上述的问题方案就是拆分,将模块独立部署,降低系统耦合性。拆分又分为横向拆分和纵向拆分;

横向拆分:将复用业务拆分出来,独立部署为分布式服务,新增业务只需要调用这些分布式业务,不需要依赖具体的模块代码,即可快速搭建一个应用系统,而模块内部业务逻辑变化时,只要接口保持一致就不会影响业务程序和其他模块。

纵向拆分:将一个大应用拆分为多个小应用,如果新增业务较为独立,那就直接将其设计部署为一个独立的web应用系统。

纵向拆分相对比较简单,通过梳理业务,将较少相关的业务剥离,使其成为独立的web应用,而横向拆分,不但需要识别可复用的业务,设计服务接口,规范服务依赖关系,还需要一个完善的分布式管理框架。

  • 大型网站分布式服务的需求与特点

    • 服务注册与发现:基本上现在都是基于zk来实现的

    • 服务调用:通过SOAP协议实现

    • 负载均衡:对热门服务,需要部署在一个集群上,分布式服务框架要能够支持服务请求者使用可配置的负载均衡算法访问服务,使服务提供者集群实现负载均衡

    • 失效转移:分布式服务框架支持服务者提供的失效转移机制,当某个服务实例不可用,就将访问切换到其他服务实例上,以实现服务整体高可用

    • 高效远程通信:对于大型网站,核心服务每天的调用次数达到数以亿计,如果没有高效的远程通信手段,服务调用就会成为整个系统性能的瓶颈

    • 整合异构系统:由于网站可能会使用不同的语言开发部署在不同的平台上,所以分布式服务框架需要整合这些异构系统

    • 对应用最少侵入:网站技术是为业务服务的,是否使用分布式服务需要根据业务发展规划,分布式服务也需要渐进式的演变,甚至出现反复,即使用了分布式服务后又退回到集中式部署,分布式服务框架需要支持这种渐进式演变和反复,当然服务模块本身也需要支持可集中式部署,也可分布式部署

    • 版本管理:分布式服务框架支持服务多版本发布,服务提供者先升级接口发布新版本服务,并同时提供旧版本的服务供请求者调用,当请求者调用接口升级后才可以关闭旧版本服务

    • 实时监控:没有监控的服务是不可能实现高可用的,分布式服务框架还需要监控服务提供者和调用者的各项指标

  • 分布式服务框架设计:大型网站需要更简单更高效的分布式服务框架构建其SOA,这里以阿里巴巴的Dubbo为例,分析其架构设计,如下图所示:

    《大型网站架构核心要素之扩展性:可扩展架构》

    流程分析

    服务消费者程序通过服务接口使用服务,而服务接口通过代理加载具体服务,具体服务可以是本地的代码模块,也可以是远程的服务,因此对应用侵入较少(应用程序只需要调用服务接口,服务框架根据配置自动调用本地或者远程实现);

    服务框架客户端模块通过服务注册中心加载服务提供者列表(服务提供者启动后自动向服务注册中心注册自己可提供的服务接口列表),查找需要的服务接口,并根据配置的负载均衡策略将服务调用请求发送到某台服务提供者服务器。如果服务调用失败,客户端模块会自动从服务者列表选择一个可提供同样服务的另一台服务器重新请求服务,实现服务的自动失效转移,保证服务高可用;

    Dubbo的远程服务通信模块支持多种通信协议和数据序列化协议,使用NIO通信框架具有更高的网络通信性能。

 

可扩展的数据结构

传统的关系数据库为了保证关系运算(SQL)的正确性,在设计数据库表结构的时候,就需要指定表的schema-字段名称,数据类型等,并要遵循特定的设计范式,这些规范带来的一个问题就是僵硬的数据结构难以面对需求变更带来的挑战,有些应用系统设计者通过预先设计一些冗余字段来应对,但是显然这是很糟糕的设计,难以做到扩展,那么有没有更好的方案呢,无需修改表结构就可以新增字段呢?很多NoSQL数据库使用ColumnFamily(列族)设计就是一种解决方案,这是一种面向列族的稀疏矩阵存储格式,使用ColumnFamily结构的NoSQL数据库,创建表的时候,只需要指定ColumnFamily的名字,无需指定字段(Column),可以在数据写入时在指定,通过这种方式数据表可以包含数百万的字段,使得应用程序的数据结构可以随意扩展,而在查询时,可以通过指定任意字段名称和值进行查询。

 

利用开放平台建设网站生态圈

大型网站为了更好的服务自己的用户,开发更多的增值服务,会把网站的服务封装成一些调用接口开放出去,提供开发接入文档,供外部的第三方开发者使用,这个提供开放接口的平台称为开放平台。第三方开发者利用这些开放的接口开发应用程序或者网站,为更多的用户提供价值。网站、用户、第三方开发者互相依赖,形成一个网站的生态圈,既为用户提供更多的价值,也提高了网站和第三方开发者的竞争力和盈利能力。

开放平台是网站内部和外部交互的接口,外部需要面对众多的第三方开发者,内部需要面对网站内许多的业务服务。开放平台的架构设计大同小异,如下图所示:

 

                     《大型网站架构核心要素之扩展性:可扩展架构》

API接口:开放平台暴露给开发者使用的一组API,其形式可以是restful,RPC等各种形式;

协议转换:将各种API输入转换成内部服务可以识别的形式,并将内部服务的返回封装成API的格式;

安全:除了一般应用需要的身份识别,权限控制等安全手段,开放平台还需要分级的访问带宽限制,保证平台资源被第三方应用公平合理使用,也保护网站内部服务不会被外部应用拖垮;

审计:记录第三方应用的访问情况,并进行监控。计费等;

路由:将开放平台的各种访问路由映射到具体的内部服务;

流程:将一组离散的服务组织成一个上下文相关的新服务,隐藏服务细节,提供统一接口供开发者调用。

    原文作者:Justin的后端书架
    原文地址: https://blog.csdn.net/chengyabingfeiqi/article/details/106357356
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞