漫谈从零访问量到每秒千万访问量的架构设计

开发 弗里曼 2019-01-12 195次点击 来自 开发

弗里曼

写在前面

最近对 IT 架构的思考比较多。

虽然做 Web 应用的架构并不是我的主业(当前工作重心是平台建设,见之前的博文),但是随着对底层技术了解的深入,却越发有兴趣思考一些宏观上的东西,或许这就是“以小见大”吧。

本文是我在探究分布式存储系统(Ceph)时对 Web 应用高并发解决方案的思考小结。

适用人群

入门——初级√——中级——高级;本文适应初级及以上。

每秒千万访问量Web应用架构设计

几个术语

在进行一切讨论前,有必要先看几个压测工程师经常提到的术语。

RPS

RPS(Requests Per Second)指系统在单位时间内(每秒)处理请求的数量。比如在 100 秒内给一个系统发起 1000 个请求,而这些请求在这 100 秒时间内全部返回了,那么可以认为系统经受住了 1000100 = 10 RPS 的流量。为了严谨性需要说明一下:这里用 100 秒而不是 1 秒,是考虑到减小时间边界引起的误差(考虑第 1 秒钟和第 100 秒钟的请求以及响应,他们一定概率是不完整的)。

并发度

指在同一个时间点发起的请求数量,比如 12306 统一在下午两点钟放票,100 个人在下午两点钟同时向 12306 发起请求,此时可以认为并发度为 100

QPS

QPS(Query Per Second) 是指在一定并发度下,服务器每秒可以处理的最大请求数。 QPS 与 RPS 的定义类似,但前者是后者的一个极限值,且前者受到并发度的约束。

为什么说 QPS 要受到并发度的约束呢?可以想象这样两个场景:① 一个系统在 1 个并发的情况下的 QPS 为 100,这就意味着每个请求的响应时间为 1100 = 0.01 秒。② 一个系统在 10 个并发的情况下 QPS 为 100, 对于每个并发来说,每个请求的响应时间变成了 1/(10010) = 0.1 秒。从上面可以看出,相同的 QPS 下,并发度不同,相应时间不同,用户的体验自然也不同。

服务器平均请求处理时间 与 用户平均等待时间

服务器平均请求处理时间 = 1/QPS(秒)。如果一个系统的 QPS 为 1000,无论请求是由 1 个并发发起的还是 100 个并发发起的, 均意味着服务器处理一个请求的平均时间为 11000 = 0.001 秒。但是在并发度不同时,相同的 QPS 数据用户感受到的响应时间是不同的,这就有了:用户平均等待时间 = 1/(QPS/并发度) = 并发度/QPS(秒)。

上面的简单推导让我想起了小学三年级做的“应用题”,虽然有点绕但是很有意义,大家多咂摸咂摸吧😆。

WhereSmile.com——用Go写的一个Web网页

大概半年前,我在一个开源项目的基础上搭建了 https://WhereSmile.com 这个 Web 网页,用来满足我折腾各方面技术的需求(也满足了部分的虚荣心=。=)。 是用 Go 写的(详见 GitHub - chalvern/pybbs-go),底层存储用了 Mysql,目前部署在一台 1c2g 的云主机上面。

由于网页功能简陋(主要作为我个人的公开笔记本使用),且没有做过宣传(主要是我朋友圈里的一些转发),因此基本上流量靠近零;同时考虑到 Go 语言的运行时性能,我评估 WhereSmile 网站短时间内不会遇到性能瓶颈。

【题外话】:拜托大家不要去恶意压测、安全攻击 WhereSmile.com,不然我就没地方记随笔了。

假如,下面的内容全是假如(╯﹏╰)

现在 WhereSmile.com 每天的 RPS 最高时才个位数😑,假如它的流量慢慢变多起来,比如峰值达到几百 RPS,那么我该怎么改造它呢?如果峰值进一步增长到达了几万 RPS,这个时候又该怎么处理呢?如果流量更高了呢?让我说一下我的打算吧(开始 YY 了😆)。

百RPS——升级机器配置

对于一百 RPS 以内的流量,目测只需要考虑升级机器的配置即可。

目前 1c2g 的机器配置同时支撑了 Web 应用以及 Mysql 应用以及一些其他用于监控的应用。流量增加后,可以先考虑升级机器的配置,比如升配到 8c16g 的机器。这种情况下,应用架构、部署方案等均不需要做大的修改。在使用了云主机的情况下,只需要①关机、②升配、③重启三步即可。

千RPS——各组件拆分并优化

考虑到单台服务器的配置并不能无限的升级(升级 CPU、内存后可能其他指标也会出现瓶颈,比如磁盘IO),对于一千 RPS 以内的流量,可以考虑拆分各个组件并优化部署到专用的服务器

具体的:①修改原来的部署方案,比如把 Web 应用与 Mysql 数据库分别部署到不同的机器。② 对各个组件的性能进行重新评估,查找性能瓶颈的点以后对应用进行改造。比如 Mysql 数据查询遇到性能瓶颈,一方面考虑升级 Mysql 专用服务器的配置;另一方面对应用层进行改造,引入 Redis 做缓存,减少对 Mysql 的查询次数,从而降低它的压力。

万RPS——水平扩展

在万RPS的时候,假如单个 Web 的吞度量被压榨到了极限,此时可以考虑水平扩展 Web 应用了,必要情况下考虑水平扩展 Database。

对 Web 应用来说,引入 nginx 做一层反向代理,把流量均发到后面的服务器去从而减少对每台服务器的压力。对于数据库来说,可以考虑进行主备设计,① Web 层对主库进行写操作,② 多个从库同时到主库同步数据,③ Web 层对主库和从库进行读操作。

十万、百万、千万RPS——终极拆分及扩展

在 RPS 到达了十万以上的时候,简单的水平扩展已经没有办法满足需求了,这个时候很多地方都会相继出现瓶颈。① 反向代理的地方出现瓶颈。简单来说,由于用户实在太多了,一个入口已经无法满足需求,这个时候需要想办法对反向代理进行水平扩展。比如在不同地域的 DNS 服务器配置不同的 nginx 实例的地址,从而把不同地域用户的请求分发到不同的 nginx 实例上。

数据库遇到瓶颈。单个数据库已经无法响应所有的请求(因为数据量大,主从设计也不能满足需求了);这个时候需要对数据库进行水平分割(或垂直分割),把不同的数据表拆分到不同的数据库(拆库),或者把同一个数据表里的数据拆分到不同数据库的不同表(拆表),从而增加数据库的吞吐量。

③ 在拆库时,一般会根据功能模块把数据表拆分到不同的数据库,从而实现每类数据库对应不同的功能模块;这个时候需要考虑分布式事务的设计。比如一个系统中存在账户模块商品模块,账户模块依赖的数据库中主要包含的是用户相关的数据表,商品模块依赖的数据库中主要包含的是商品相关的数据表。当某个用户购物时,其账户中的财富值与其购物单之间存在数据一致性的问题,必须限定 a) 只有在下单成功的情况下才允许扣除用户账户中的财富值,b) 否则下单失败同时账户的财富值也不会扣除。

上面的讨论给出的是一种抽离了业务本身的粗线条架构方案,对应到实际的业务时需要根据具体的领域知识对架构进行细化,否则没有工程实践意义的。

小结

本文构思了一种架构设计,用于解决超高并发 Web 应用的性能瓶颈。首先解释了 RPS、并发度、QPS、服务器平均请求处理时间、用户平均等待时间等几个专业术语,接着从 https://WhereSmile.com 这个 Web 网页讲起,并假设在网页流量增加到不同的量级时分别考虑采取的应对措施,最后给出了一种终极解决方案以应对千万 RPS 的场景。

最后,就像前面提到的,WhereSmile.com 在能看到的未来里不可能达到千万的 RPS,想一想真是又悲又喜的🙃。

参考

目前暂无回复

©2018 Powered by 朋也社区