网站的可扩展性架构设计,能够在对现有系统影响最小的情况下,系统功能可以可持续扩展及提升的能力。
在此,对容易混为一谈的 “扩展性” 和 “伸缩性” 的概念进行详细说明:
扩展性
表现为:基础设施不需要经常变更,应用之间较少依赖或耦合,可以对需求变更快速响应。它对扩展开放,对修改关闭。架构设计会考虑到未来功能的可扩展性,所以当系统增加新功能时,不需要对现有系统的结构和代码进行修改。
伸缩性
是指系统通过增加(或减少)自身资源规模的方式增强(或减少)处理业务的能力。如果这种增减是成比例的,就可以称之为线性伸缩性。通常是利用集群的方式增加服务器的数量,以提高系统整体业务吞吐能力。
1 构建可扩展的网站架构
度量一个开发框架、设计模式或编程语言优劣的一个重要尺度就是它是否能够让软件开发过程和软件产品更加低耦合。
因为低耦合的系统更容易扩展,也更容易被复用,而且也会让开发过程和维护变得更加容易。但如何分解系统的各个模块、如何定义各个模块的接口、如何复用、组合不同模块构造一个完整的系统,这是软件设计中最具挑战性的部分。
软件架构师的最大价值,就在于把一个大系统分解为 N 个低耦合的子模块的能力,这些子模块包含横向的业务模块与纵向的基础技术模块。这种能力来源于专业技术能力与经验、业务场景的理解、对人性的把握以及对世界的认知。
构建可扩展的网站架构的核心思想是模块化,并在此基础上,降低模块之间的耦合性,提高模块的复用性。
可以利用分层与分割的方式,把软件分割为若干个低耦合、独立的组件模块,然后在这些组件模块之间以消息传递或依赖调用的方式聚合成一个完整的系统。
这些模块可以通过分布式部署的方式,部署在独立的服务器上。这种从物理上分离模块之间的耦合关系,可以进一步降低耦合性。
模块的分布式部署后的聚合方式有:
- 分布式消息队列。
- 分布式服务。
1 使用分布式消息队列降低耦合性
如果模块之间不存在直接调用关系,那么新增或修改模块对其他部分的影响最小,这样的扩展性自然更好。
1.1 事件驱动架构
事件驱动架构指的是:在低耦合的模块之间传输事件消息,保持模块之间的松散耦合,通过事件消息来完成模块之间的通信。 事件驱动架构最常见的实现方式就是使用分布式消息队列。
消息队列基于发布——订阅模式工作,消息发送者发布消息,一个或多个消息接收者订阅消息。消息发送者把消息发送至分布式消息队列后就处理完毕,然后由消息订阅者从消息队列中获取消息进行处理。对于新增的业务,只要对某个消息感兴趣,就可以订阅该消息,而这对原有的系统和业务没有任何影响,从而实现系统的可扩展性设计。
消息接收者还可以对收到的消息再构造,定义出一个新的消息类型,然后再把消息发送给订阅了这一新消息类型的接收者。所以基于消息对象的事件驱动架构可以是一系列的流程。
因为消息发送者无须等待就可以返回,所以系统具有更好的响应时间;而且在访问高峰,消息可以暂存于消息队列中,从而减轻了数据库的存储负载压力。
1.2 分布式消息队列
队列是一种先进先出的数据结构,我们可以把消息队列部署到独立的服务器中。应用通过远程访问接口使用消息队列,进行消息的存取操作,从而实现分布式的异步调用:
目前较为流行的分布式消息队列是 Apache ActiveMQ。
因为消息队列服务器上的数据可以看做是即时处理的,所以在伸缩性上,我们把新服务器加入分布式消息队列集群后,只需要通知生产者服务器更改消息队列的服务器列表就好啦O(∩_∩)O~
在可用性上,如果内存队列满了,要将消息写入磁盘,这样当消息推送模块把内存队列中的消息处理完毕后,就会把磁盘中的消息加载到队列中继续处理。
为了避免消息队列服务器宕机造成消息丢失,会把消息存储在消息的生产者服务器上,这样等消息确实被消息消费者服务器处理后才会删除。如果消息队列服务器宕机,生产者服务器会选择分布式消息队列服务器集群内的其他服务器发布消息。
分布式消息队列可以很复杂,比如支持 ESB(企业服务总线)和 SOA(面向服务的架构)等。也可以很简单,比如使用 MySQL 作为分布式消息队列:消息的生产者把消息作为记录写入数据库,消费者查询数据库(按记录写入库表的时间戳排序),这就是一个分布式消息队列啦。再配上成熟的 MySQL 运维手段,也可以达到一个较高的可用性和性能指标哦O(∩_∩)O~
2 使用分布式服务构建可复用的业务平台
分布式服务可以通过接口降低系统的耦合性,不同的子系统之间通过相同的接口描述调用服务。
随着网站功能的日益复杂,系统会逐渐发展成为一个巨无霸,里面聚合了大量的应用和服务组件,这样的一个系统会给开发、维护、部署带来巨大的麻烦:
- 编译、部署困难。
- 代码分支管理困难:复用的代码模块由多个团队共同维护修改,所以在代码合并时总会发生冲突。
- 耗尽数据库连接:假设一个应用设定了 10 个数据库连接,那么一个拥有数百台服务器集群的应用就会在数据库上创建数千个连接。
- 新增业务困难。在这样一个剪不断、理还乱的系统中新增业务?开玩笑吧O(∩_∩)O~
所以我们要做拆分,把模块独立部署,降低系统的耦合性:
- 纵向拆分 - 把一个大应用拆分为多个小应用。如果新增的业务较为独立,就直接将其设计并部署为一个独立的 Web 应用。
- 横向拆分 - 把复用的业务拆分出来,独立部署为分布式服务,新增的业务只需要调用这些分布式的服务,就可以快速搭建出一个应用系统。即使模块内的业务逻辑发生变化,只要保持接口一致,就不会影响其他模块。
纵向拆分较简单,通过梳理业务,把关联较少的业务剥离,使其成为独立的 Web 应用。而横向拆分不仅需要识别出可复用的业务、设计服务接口以及规范服务之间的依赖关系,而且还需要一个完善的分布式服务管理框架。
2.1 Web Service 分布式服务
Web Service 曾经是企业应用系统在开发领域中最时髦的词汇之一,它用于整合异构系统以及构建分布式系统:
服务提供者通过 WSDL(Web Services Description Language,Web 服务描述语言)向注册中心(Service Broker)描述自身所能提供的服务接口内容,然后注册中心使用 UDDI(Universal Description, Discovery, and Integration,统一描述、发现和集成)发布服务提供者提供的服务。服务请求者从注册中心检索到服务后,通过 SOAP(Simple Object Access Protocol ,简单对象访问协议)与服务提供者通信,使用该服务。
Web Service 虽然有成熟的技术规范和实现,但有如下缺点:
- 臃肿的注册、发现机制。
- 低效的 XML 序列化手段。
- 开销较高的 HTTP 远程通信。
- 复杂的部署与维护手段。
这些问题导致 Web Service 难以满足大型网站对高性能、高可用、易部署与易维护的要求。
2.2 大型网站分布式服务的要求
分布式服务框架需要能够支持以下特性:
- 负载均衡 - 对于服务请求者能够使用可配置的负载均衡算法来访问热门服务(比如登录或商品服务,这些服务被部署在一个集群上)。
*失效转移 - 可复用的服务被多个应用调用,一旦服务不可用,就会影响到很多应用的可用性。所以即使是很少访问的服务,也需要集群部署。分布式服务框架检测到某个服务不可用时,就会切换到其他服务实例上,保证整体高可用。 - 高效的远程通信
- 整合异构系统
- 对应用最小侵入 - 分布式服务框架支持服务(服务模块需要即支持集中式部署,也支持分布式部署)的渐进式演化和反复。
- 版本管理 - 网站服务不可中断,所以分布式服务框架需要支持服务的多版本发布,服务提供者升级发布接口新版本的同时,还会继续支持旧版本的服务,直到请求者调用的接口升级后,才会关闭旧版本的服务。
- 实时监控 - 监控服务提供者和调用者的各项指标,提供运维与运营的支持。
2.3 分布式服务框架设计
大型网站需要更简单、更高效的分布式服务框架构建其 SOA(Service Oriented Architecture ,面向服务的体系结构)。目前国内有较多成功实施案例的开源分布式服务框架是阿里巴巴的 Dubbo。
服务消费者通过接口使用服务,接口通过代理加载具体服务,可以是本地的代码,也可以是远程的服务,因此对应用侵入较小。
客户端模块通过服务注册中心加载服务提供者列表(服务提供者启动后自动向服务注册中心注册自己可以提供的服务接口列表),然后根据配置的负载均衡策略把服务调用请求发送到某台服务提供者的服务器。如果服务调用失败,客户端模块会自动从服务提供者列表中选择一个可以提供同样服务的服务器重新请求,即自动失效转移,保证服务的高可用。
Dubbo 使用 NIO 通信框架,因此具有较高的网络通信性能。
4 可扩展的数据结构
使用 NoSQL 数据库(如 Cassandra)的 ColumnFamily (列族)技术可以做到可扩展的数据结构设计。它是一种面向列族的稀疏矩阵的存储格式。
只需要指定 ColumnFamily 的名字,即可创建表。字段可以在写入数据时再指定,通过这种方式,一张表可以包含数百万个字段。这就使得应用的数据结构可以随意扩展。只需要指定任意字段名称和值即可查询。
5 利用开放平台建立生态圈
用户只有得到他们想要价值,才会愿意使用网站的服务,这样的网站才有存在的意义。但一个网站毕竟不能满足所有用户的需求。
用户不会为网站提供的价值买单,所以网站必须提供更多的增值服务才能赚钱。根据长尾效应,增值服务的数量越大,种类越多,盈利也就越多。但一个网站能够自己开发的增值服务也是有限的。
大型网站为了更好地服务用户、为他们开发出更多的增值服务,会把网站内部的服务封装成接口开放出去,供外部第三方开发者使用,这个平台就叫做开放平台。第三方开发者利用这些开放的接口就可以开发应用程序(如 APP)或网站,为用户提供更多的价值。网站、用户、第三方开发者
相互依赖,形成一个生态圈。
开放平台是网站内部和外部交互的接口。外部会面对众多的第三方开发者,内部面对的是网站内众多的业务服务。下面是开放平台的架构:
- API 接口:暴露给开发者的一组 API,可以是 RESTful、WebService、RPC 等形式。
- 协议转换:把各种 API 的输入转换为内部服务可识别的形式,并把内部服务的返回信息封装为 API 格式。
- 安全:除了身份识别、权限控制等手段之外,还要对访问带宽进行分级限制,保证平台资源被第三方应用合理公平地使用,也能保证网站自身的内部服务不会被外部应用拖垮。
- 审计:监控第三方应用的访问情况并计费。
- 路由:把开放平台的各种访问路由映射到具体的内部服务。
- 流程:把一组松散的服务组织成一个上下文相关的新服务,对外提供接口供开发者使用。