【ArchSummit】如何通过AIOps推动可量化的业务价值增长和效率提升?>>> 了解详情
写点什么

如何在云平台构建大规模分布式系统

  • 2016-04-17
  • 本文字数:4886 字

    阅读完需:约 16 分钟

编者按:在云计算环境中,当用户业务面对流量激增、数据量翻番、访问量指数级攀升的“烦恼”时,如何利用云计算平台的弹性,结合业务自身特点,设计和构建一个高可用、高伸缩性的后端系统架构?本文以真实案例为背景,讲述从简单后端系统到大规模分布式系统的演进之路。

光快速扩容是不够的

很多企业和开发者在开发一款产品时,首要考虑的是产品功能的实现,其后端架构通常都是非常简单直接的。产品在刚上线初期,由于用户访问压力很小,数据量积累也并不大,在短时间内都会运行良好。

然而如今移动互联网的普及和成熟,让一款产品很可能在短时间内聚集大量用户,面对流量激增、数据量翻番、访问量指数级攀升等诸多“烦恼”,这本来是一件好事,可是如果后端系统不能及时扩展,势必会造成响应缓慢,频繁出错甚至拒绝服务的情况。

即便没有上述系统压力突然增大的“烦恼”,产品在不断开发升级的过程中,各种功能模块会变的越来越复杂,如果不能很好的梳理和组织后端架构,系统出错崩溃、不可使用的风险也会越来越大。

在没有云计算的时代,物理硬件从采购、上架、插线,到安装、调试、部署,再到真正投入使用,是一个漫长而耗费人力的过程,往往跟不上系统紧急扩容的节奏。而云服务的出现不仅仅让我们节约了使用成本,更重要的是可以利用云计算极度弹性的特点,让企业和开发者根据需求,对系统进行在线快速的扩容。

但仅仅在云服务上快速扩容是不够的,企业也需要在业务层面,关注各个系统组件的可用性和伸缩性。如何利用云计算的优势,结合企业的业务特点构建稳定可靠的分布式系统。

打造高性能、高可用的负载均衡集群

首先我们从一个最简单的后端架构开始:

(点击放大图像)

复制代码
接入层:Nginx
业务层:Java Application
数据层:MySQL

在云计算环境中,网络架构的组织非常重要,QingCloud 提供了基础网络和 VPC 两种网络,他们的区别在官网用户指南中已经介绍,这里不赘述。推荐企业使用 VPC 来构建自己的网络,将所有主机和系统资源放置在 VPC 网络中,指定内网网段(如 192.168.x.x / 172.16.x.x),主机可以通过内网地址进行通信,该地址不会变化。

随着主机越来越多,IP 地址不易记忆,为了方便主机间相互识别,可以给每台主机设置内网别名。为方便在控制台管理,给每个资源打上标签,按照标签来组织分类。

接下来我们回到上面那个简单的后端架构。随着访问压力越来越大,单台 Nginx + Java Application 可能不足以应付,你会看到这台主机的 CPU 越来越忙 Application,内存使用越来越多。而且这台主机一旦故障,整个服务都不可用了。

所以我们首先调整这里的结构,增加多台 Nignx + Java Application 同时提供服务,在接入层引入负载均衡器(下文用 LB 这个词代替),使外网请求首先发到 LB 上。LB 的选择有很多,比如提供七层负载能力的 Nginx 和 HAProxy,也有提供四层负载能力的 LVS,安装和配置的方法各有不同。

LB 的引入可以分摊请求压力到后端的多台业务服务器,并且可通过心跳检查,自动隔离后端出现故障的服务器,实现业务层的高可用。但这时 LB 本身也会成为一个单点,当出现故障也会导致全局不可用。所以可以使用 Keeplived 服务为 LB 提供一个副本,在一台出问题的时候可以马上顶上,部署方法网上有很多资料。

有人会说可以通过 DNS 轮询到不同的 IP ,实现 LB 的高可用,但事实上这样不行,因为一旦一台 LB 挂掉,DNS 还会解析到这个 LB,此时即便马上修改 DNS,在 DNS 缓存更新之前(通常要很久),服务也是不可用的。虽然 LB 的原理并不复杂,但是部署配置有很多工作量,而且为了实现 LB 的高可用还要额外做一些事情。

改造后的架构如下图所示:

(点击放大图像)

业务层扩展的问题

接下来我们来思考业务层的扩展问题。

首先要解决如何快速扩充业务服务器。如果业务服务器的运行环境和程序不会频繁更新,可以基于已有的业务服务器制作主机映像,当需要扩容时,直接基于映像创建新的主机,挂接到 LB 后端就可以马上对外服务了。

此时你还可以使用 AutoScaling 功能自动化这一过程,即当到达某种触发条件,如 LB 并发数、响应延迟达到多少后,自动触发主机的扩容。当触发条件不满足时,可以回收资源。

当然如果你的业务服务器的环境或程序需要频繁更新,不适合做成固定模版。此时可以自己搭建自动化部署(如 Puppet Application / Application Ansible)实现业务自动扩容(使用 QingCloud 的开放 API 接口可以轻松实现)。

此外你还需要保证业务服务器是无状态的,因为每次 LB 请求的后端可能不同,不能假设上一次请求和这一次请求落在同一台业务服务器上。如果服务器需要保存用户访问的 session 信息,可将其下放到缓存或数据库中存储。

随着产品功能越来越丰富,你会发现原有单一的业务项目越来越庞大,各种功能逻辑交织在一起,当一个功能出现故障,可以引发全局不可用。此时你需要考虑将单一的业务项目分拆成多个独立子服务。子服务之间可以基于消息的通信,亦或基于 RPC 的通信方式。

子服务的调用可分为需同步处理和可异步处理两类。你应该尽量异步化所有不需要马上返回结果的请求。对于可异步处理的请求,我们通过引入消息队列,为请求产生的数据做缓冲,请求的接收者(队列消费者)可根据队列中任务的数量做水平扩容。消息队列的选择有很多,例如 Redis, RabbitMQ, ActiveMQ, Kafka,QingCloud 平台上目前已经提供分布式、可分区、多副本的消息队列服务,具有高吞吐量、低延迟等特点,用户可以方便的集成到自己的系统中。

如今数据分析对于企业越来越至关重要,业务服务器在处理请求的过程中,可以将原始数据通过队列,源源不断地导入大数据处理系统。用户可以根据需求方便的创建、使用和扩容 QingCloud 大数据分布式处理平台的 Spark 和 Hadoop 服务。

通过拆分子服务,使得我们有能力在某项子服务发生故障时,尽可能降低对于全局的影响,提高系统整体的可用性。另外,对于处理压力比较大的子服务,我们还可以进行独立的水平扩容。操作方式和前面讲到的业务服务器扩容相似,QingCloud 内网 LB 服务也可以在这里发挥作用。

改造后的架构如下图所示:

(点击放大图像)

数据层的扩展问题

随着业务的增长,数据层面临的压力会越来越大,单机数据库已经不足以支撑。对于大多数的业务场景来说,数据的操作都是读多写少,而且读都集中在少部分的热点数据上。

我们首先想到的是引入缓存层来缓解数据库的读压力;如果缓存容量需求比较大,可以构建缓存集群,在上层按照 consistent Hashing 算法将数据分散到多个节点,后续需要增加新缓存节点时,只有少部分的数据会失效。

接着引入新的数据库种类。Redis 已经成为诸多企业的首选标配,因为其支持丰富的数据类型和数据查询接口,且内存型的数据库天然具有更高的性能。
你可以将业务中关系性要求不高的数据,从 MySQL 转移到 Redis 中,尤其是列表类的数据以及计数统计类的数据。给 MySQL 减负的同时提高数据的查询性能。如果单台 Redis 节点不能满足你对容量的需求,QingCloud 平台提供了支持多主多从 Redis 3.0 集群服务,一方面可对数据自动分区提高存储容量,另一方面保证了服务的高可用性。

对于 MySQL 的扩展可以分为几个步骤来做。

首先,增加 MySQL slave 节点,在上层将部分读请求分发到 slave 节点上去。由于 slave 同步可能有延时,业务应该能容忍短暂的数据不一致现象。例如你的一个用户修改了年龄属性,其他用户要等一会儿才能看到他的新年龄。QingCloud MySQL 数据库支持一主多从的架构,并且已经在多个从节点之上做好了负载均衡,你可以轻易在界面上操作增加新的从节点来为你分担读压力。

即便有 slave 作为数据副本,你也应该定期对你的数据库进行冷备份,方便当业务出现误操作时,能够回滚或恢复到曾经的某个时间点。在 QingCloud 平台上,备份的过程可以手动执行或者配置为自动任务,在备份过程中对数据库正常使用没有影响。

其次,随着数据的增长,单个数据库不能承载完整的数据集合,并且写操作对于单库的压力越来越明显,你应该考虑分库、分表技术。将比较庞大的数据表拆分出来单独存放,可以给主数据库腾出来一部分空间,分担读写压力。拆分的时候,还可以按照功能逻辑,把相关联的数据表存在一个库里。

第三,当数据库单表非常庞大,对读写都造成瓶颈时,你需要开始考虑水平分表 sharding。这种扩展方式可以同时解决单表容量过大,读压力和写压力很大的问题,但带来的研发和运维难度也会增大,推荐把上述的优化做完以后,最后在有必要的情况下再做这一步。

这里简略说一下水平分表的要点。首先要从数据表的字段中,选择一个合理的分区键(shard key),这个键应该是所有该表查询条件里,最经常用到的字段,这样才会使大部分的查询,能够提前判断应该向哪些特定的分区(shard)发送请求,如果查询条件中不带 shard key,需要遍历所有的分区,并将结果进行 merge。

有了 shard key 还要设计一种分区算法。比如常见的有按照区间,如 user_id in [0, 100] 在 shard 1,user_id in [101, 200] 在 shard2,还比如按照 Hash 取模等等。设计分区算法的时候要充分考虑业务特点,多从读写操作的角度思考,这样设计能否将 I/O 压力和数据均匀分摊到每个 shard 上去。

最后,还需要考虑数据层的扩展如何对上层透明。比如引入分布式数据库中间件,或者结合业务逻辑把数据库操作做成一个独立的子服务,供其它服务调用。如果不做成子服务,至少在业务代码里有独立的一层来封装对数据库的操作。

至此,数据层的扩展示意图如下所示:

(点击放大图像)

除了上述的结构化数据的存取以外,企业还有存储海量小文件数据(非结构化数据)的需求。单机硬盘、LVM 和 NAS 可以作为临时方案使用,但都无法同时满足无限容量、高性能、高安全性、高可用性的多重需要。而自行搭建分布式存储系统,如 Ceph、GlusterFS、HDFS 适用场景非常有限,且运维和二次开发的成本也非常高。

在 QingCloud 平台上用户可以使用 QingStor 对象存储服务来存储海量的数据文件,服务本身提供了无限容量、高扩展性、高可用性和高安全性的特性。

多活与灾备问题

讲完数据层的扩展技术,最后来谈一下多机房部署和异地容灾的话题。QingCloud 从北京3 区机房开始,通过自营的骨干网光纤和多路环网技术,使得当机房出现网络故障时对用户无感知,在基础设施上保障了高可用性。但是用户的业务如果能够多机房部署,可以在分摊访问负载的同时加速区域访问,比如加速中国南北方的用户或者海外用户的访问。

(点击放大图像)

如上图所示,若是有三个机房,中间是 QingCloud 北京 3 区机房,负责主营业务。左边是 QingCloud 亚太 1 区机房,主要服务亚太和海外的客户。这两个机房都使用了 QingCloud 私有网络(VPC)部署,通过 GRE 或 IPsec 加密隧道在网络上的互联互通。右边是你办公室的物理机房,IT 人员可以在这个环境下进行开发和办公。

在业务上实现异地多活时,通常从易到难有三个阶段:

  • 第一,在备用机房搭建反向代理,用户请求到备用机房,请求直接被转向主机房,如果两机房有专线互联或延时很小,这样部署最为简单。
  • 第二,两个机房同时部署业务服务器和缓存,由于大部分数据请求可以从缓存中读取,不用进行跨机房访问。但当缓存失效时,依然要从主机房的数据库去查询。
  • 第三,两机房同时部署全套系统,包括接入层、业务层和数据层。数据层依靠数据库双主或主从技术进行跨机房同步。

总结一下。没有一个所谓经典或完美的架构,只有最适合企业业务的架构,本文主要讲述在最通用的业务场景下,系统在接入层、业务层和数据层的常用扩展方法。企业后端架构的演进过程是一个漫长而艰巨的过程,不可能一蹴而就设计出一个万般周全的系统,但如果设计之初能更多着眼于未来,就可以为进一步优化留出了余地。

作者介绍

王煜,青云 QingCloud 系统研发工程师,原街旁团队创始成员,基础架构负责人,现负责 QingStor 对象存储服务的设计与研发。对 Linux 操作系统、计算机网络、分布式系统、云计算等领域有较深入的研究。九零前,文青程序员,代码诗人,北京土著。


感谢魏星对本文的策划和审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-04-17 17:415535

评论

发布
暂无评论
发现更多内容

DAPP和APP有哪些区别?多链跨链NFT铸造挖矿dapp系统开发技术原理分析

开发微hkkf5566

一文详解特权访问管理(PAM)

SEAL安全

安全 访问权限 访问管理 特权访问

用Rust编写的Linux内核GPU驱动程序,或将到来

非凸科技

Linux gpu rust 编程语言

前端监控系列2 |聊聊 JS 错误监控那些事儿

字节跳动终端技术

APM 前端监控 火山引擎 JS错误

35岁程序员危机,有何破解之法?

博文视点Broadview

人手一套的K8S命令集合,它来了!

wljslmz

云计算 Kubernetes 容器 8月月更

连流量染色都没有,你说要搞微服务?

得物技术

架构 微服务 云原生

DBPack 数据库限流熔断功能发布说明

峨嵋闲散人

分布式事务 云原生 分库分表 dbmesh Database Mesh

Groovy语境下的Map

FunTester

开源 | WLock:高可用分布式锁设计实践

开源 分布式 分布式锁

StarRocks 技术内幕 | 基于全局字典的极速字符串查询

StarRocks

数据库

Java 泛型 T,E,K,V,,傻傻分不清?

TimeFriends

8月月更

测试开发【Mock 平台】09 开发:项目管理(五)搜索、删除和Table优化

MegaQi

测试平台开发教程 8月月更

增强分析在百度统计的实践

百度Geek说

数据库

微服务性能分析|Pyroscope 在 Rainbond 上的实践分享

北京好雨科技有限公司

Kubernetes 微服务 云原生

属实不赖!Alibaba开源GitHub星标114K微服务架构全彩进阶手册

冉然学Java

Java 阿里巴巴 开源 微服务 微服务架构

以合规交易释放数据“红利”,合合信息旗下启信宝签约福建大数据交易所首批数商

合合技术团队

数据 峰会

阿里大佬 推荐的 “ Spring Cloud Alibaba项目文档 ” 正式发布

冉然学Java

Java 微服务 Spring Cloud Alibaba

寻找OpenHarmony「锦鲤」|万元豪礼+技术干货全是你的!

OpenHarmony开发者

OpenHarmony

为什么不做APP而要做小程序

源字节1号

小程序开发

一文搞懂│mysql 中的备份恢复、分区分表、主从复制、读写分离

MySQL 高并发 经验分享 签约计划第三季 8月月更

推荐一款微软出品的开发神器,体验不输IDEA!(含参考资料和项目源码)

收到请回复

面试 springboot 应届生 金九银十 java项目实战分享

为什么电商云产品需要 Assisted Service Module (ASM) 模块的支持

Jerry Wang

typescript 电商 SAP 8月月更 Storefront

《数字经济全景白皮书》银行业数字普惠金融发展与优化策略分析 发布

易观分析

金融 数字经济全景白皮书 易观分析

一对一直播系统源码——多人语音聊天室

开源直播系统源码

直播系统源码 语音直播系统 一对一直播视频源码 一对一语音直播

Kotlin协程解析系列(上):协程调度与挂起

vivo互联网技术

kotlin 协程

最常见的 10种网络安全攻击类型

郑州埃文科技

网络安全 IP地址 网络攻击

SpringBoot 日志的各种使用姿势,你真的用对了吗?

程序知音

Java spring 程序员 springboot 后端技术

多原则等于无原则,微服务识别方法究竟该怎么选?

老坛架构

架构 微服务

干货!这份阿里P8大佬纯手打总结Kafka学习笔记,真是yyds

了不起的程序猿

Java kafka java程序员 消息中间件 Java 开发

基于RocksDB实现高可靠、低时延的MQTT数据持久化

EMQ映云科技

物联网 mqtt RocksDB emqx 8月月更

如何在云平台构建大规模分布式系统_语言 & 开发_王煜_InfoQ精选文章