8.4 亿级PV高性能高并发网站架构设计

事实上,如果我们的网站每天能达到亿级PV甚至10亿级PV的访问量,那么这个数字是一个相当惊人的数字,这么大流量的进出量,对系统整体水平的要求都是很高的,不仅仅是服务器层面的压力,代码、数据库、缓存乃至文件系统都是有要求的。对于一个高并发高流量的网站来说,任何一个环节的瓶颈都会造成网站性能的下降,影响用户的体验,从而造成无法弥补的损失。下面就以目前正在维护的DSP大型电子广告系统来举例说明,6个数据中心,每天日PV接近10亿,平均3万QPS,业务机器单机并发连接数2.2万以上。

考虑到业务涉及世界各地,6个数据中心需要进行全球化部署,业务高峰期间能够快速增添机器以应付暴增流量,并且业务需要进行Hadoop/Spark分析数据,还要考虑稳定的存储文件系统等,而这些AWS都有相对应的产品,可以极大地简化运维成本。因此最终考虑采用AWS云计算平台。

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

1.负载均衡层

实际上,网站面对这么大的流量冲击,我们的第一反应应该就是采用一级分流的方式,一般来说,DNS轮询是一种常用的做法,我们可以利用DNS轮询将流量第一时间分散到各个数据中心,这里其实用到了分布式的思想,这样做就会不至于让其中的一个数据中心因为顶不住流量而出现宕机的情况,这里我们用的是PowerDNS。

在这种流量规模的系统中,还应该关注另外两个参数QPS及系统响应时间,QPS即Queries Per Second,意思是“每秒查询率”,是系统每秒能够响应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。之所以应该关心这个数值,是因为它是系统整体性能的重要参考标准。

系统响应时间是我们非常关注和重视的另一个方面。由于我们的DSP广告主服务平台会参与RTB(Real Time Bidding)实时竞价,所以整个响应时间越短,效果会越好。RTB实时竞价,是一种利用第三方技术在数以百万计的网站或移动端,针对每一个用户的展示行为进行评估及出价的竞价技术。所以从参与RTB实时竞价开始到完成,整个过程应尽量控制在500~600ms之间,如果此响应时间过大,则会失去竞争优势。

中间层的负载均衡器采取的也是常见架构的做法,用的是Nginx。因为是AWS EC2机器,每台机器能够分配的带宽有限,机器很容易被流量打满。所以我们除了采用常规的开源软件监控流量以外,还自己开发了监控工具来进行监控。

我们选用Nginx作为其中间层的负载均衡,作用有以下几点:

·七层负载均衡,实现各种规则转发。

·管理业务接口。

·灰度发布。我们可以利用Nginx的权重算法,将流量分散到线上的某台测试机器上,如果代码不能顺利通过,也只会影响到这一台测试机器。

·反向代理静态页面缓存,加快用户访问速度。

进来的流量经过这样处理以后,基本上可以分流到各数据中心上面,但有一点要注意,Nginx作为二级负载均衡的压力很大,平时要注意以下两种情况。

·监控Nginx的系统负载及CPU利用率情况。

·监控其带宽总体使用情况。

·后端bidder机器的响应时间。

基于系统响应时间的考虑,平台后期直接删除Nginx负载均衡层,最前端的DNS轮询直接连接后端的bidder机器,bidder机器的响应时间在50ms左右。

2.Web应用服务器

我们的主要业务机器是bidder机器,用于竞标价格。bidder机器这块选用的是Nginx+Lua(ngx_lua模块),那么什么是ngx_lua模块呢?

ngx_lua是Nginx的一个模块,将Lua嵌入到Nginx中,从而可以使用Lua来编写脚本,这样就可以使用Lua编写应用脚本,然后部署到Nginx中运行,即Nginx变成了一个Web容器;这样开发人员就可以使用Lua语言开发高性能的Web应用了。

ngx_lua提供了与Nginx交互的很多API,对于开发人员来说只需要学习这些API就可以进行功能开发了,而对于开发Web应用来说,如果接触过Servlet的话,其开发和Servlet类似,无外乎就是知道接收请求、参数解析、功能处理、返回响应这几步的API是什么样子。

理论上可以使用ngx_lua开发各种复杂的Web应用,不过Lua是一种脚本/动态语言,不适合业务逻辑比较重的场景,适合小巧的应用场景,代码行数保持在几十行到几千行。目前见到的一些应用场景有以下几种。

·Web应用:会进行一些业务逻辑处理,甚至进行耗CPU的模板渲染,一般流程为MySQL/redis/HTTP获取数据→业务处理→产生JSON/XML/模板渲染内容,比如京东的列表页或商品详情页。

·接入网关:实现如数据校验前置、缓存前置、数据过滤、API请求聚合、AB测试、灰度发布、降级、监控等功能。

·Web防火墙:可以进行IP/URL/UserAgent/Referer黑名单、限流等功能。

·缓存服务器:可以对响应内容进行缓存,减少到后端的请求,从而提升性能。

·其他:如静态资源服务器、消息推送服务、缩略图裁剪等。

线上系统主要用AWS的c3.xlarge(4vCPU、14GB)来运行Nginx+Lua,在线上运行时,若并发连接数超过2.4万,则带宽基本就会被打满(虽然AWS没有带宽限制,但是由于多虚拟机共享了物理机的网络性能和I/O性能,因此导致c3.xlarge的带宽限制大约在40MB~50MB之间),CPU利用率在90%左右,系统负载不到4。

为了处理业务高峰期的流量,一般会增添10台左右bidder机器,AWS的AMI(映像复制)功能非常方便,而且Instance可以按照小时收费,这样也可极大地降低运营成本。

我们的网站前台主要是针对客户的,跟常见的CMS系统类似,开发语言主要是PHP,Web框架选用的轻量级CI(Code Igniter)。Hadoop/Spark数据分析这块则主要是Java和Python。由于整个系统涉及的开发语言比较多,因此以Python作为胶水语言,把系统的各个子模块都衔接了起来。另外,运维自动化的主要开发语言也是Python。

参考文档:

http://www.tuicool.com/articles/VjMZF3j

http://jinnianshilongnian.iteye.com/blog/2258111

3.Session

由于DSP广告系统提供的是服务(bidder机器发送的是竞价请求),即无状态的HTTP访问请求,并非传统型的Web网站,所以此系统并不需要关心这个问题,而大型网站肯定必须要关注Session共享的问题,建议大家利用redis缓存服务器来解决此问题,其优势就是快,快速进行大量Session数据的存储和读取,redis缓存服务器完全可以胜任这份工作。而redis的主从方案,可以避免redis的单点问题。

4.数据缓存层

因为DSP系统产生的大量的ip list、domain、关键词等数据需要快速、有效地读取,之前考虑将这些数据放在MySQL数据库上,后面发现存在着速度问题,而且ip list数据量太大,每次导入都是十几亿条,这样我们就需要一个NoSQL的解决方案。在比对测试了Memcached和redis的速度和效率后,最终选择了redis。在最终将redis应用到线上环境时,我们也在软硬件及数据结构方面对redis进行了优化,主要工作有如下几个方面:

·选用了EC2内存型实例(r3.xlarge或r3.2xlarge)来运行redis机器。

·针对redis数据结构进行了重组优化。

·将运行redis集群机器的数量提高到了10台到15台左右(每个数据中心的数据会不一致)。

我们是通过自己开发的一致性哈希算法程序来统一管理redis机器实例的,便于控制,但也有不少的问题:比如支持失败节点自动删除、程序的单点故障等。目前又在尝试使用Twitter的Twemproxy程序来管理这些redis机器。

Twemproxy通过引入一个代理层,可以将其后端的多台redis实例进行统一管理与分配,使应用程序只需要在Twemproxy上进行操作,而不用关心后面具体有多少个真实的redis实例机器。

Twemproxy有很多优势,我们比较关心的有以下几点。

(1)支持失败节点自动删除。

·可以设置重新连接该节点的时间。

·可以设置连接多少次之后删除该节点。

·该方式适合作为Cache存储。

(2)支持设置HashTag

通过HashTag可以自己设定将两个KEY哈希到同一个实例上去。

(3)减少与redis的直接连接数

·保持与redis的长连接。

·可设置代理与后台每个redis连接的数目。

(4)自动分片到后端多个redis实例上

·多种哈希算法:能够使用不同的策略和哈希函数支持一致性哈希。

·可以设置后端实例的权重。

(5)避免单点问题

可以平行部署多个代理层,客户端会自动选择可用的那一个。

引入Twemproxy以后,分布式的redis集群中出现的许多问题都可以解决。

5.MySQL数据库和Amazon Redshift

由于数据读取压力全部在redis集群上面,分散到后端的MySQL上的压力就非常小了,因此对于MySQL,我们用的是最普通的MySQL一主一从,主要存放系统前台的表数据,数据量并不大。不过,目前业务数据库的核心表只是单维度的,后期的业务表可能会向多维度方向转变,这样会导致MySQL的磁盘容量呈现一个暴增趋势,所以现在在考虑使用Amazon Redshift数据仓库服务。

什么是Amazon Redshift?可参考官方资料文档:http://aws.amazon.com/cn/redshift/ 。

Amazon Redshift是一种快速、强大且完全托管的PB级云中数据仓库服务。客户可以以每小时0.25 USD的价格从小做起,无需订立长期合约或预付费,然后以1TB 1000 USD/年的价格再扩展到1PB或以上,这个费用比大多数其他数据仓库解决方案成本的十分之一还要低。传统的数据仓库需要相当数量的时间和资源来进行管理,尤其是大型数据集。另外,内部部署型数据仓库的建立成本、维护及日益增长的自我管理相关的财务成本也非常之高。Amazon Redshift不仅大大降低了数据仓库的成本,而且还能轻而易举地对大量数据进行快速分析。

Amazon Redshift利用基于SQL的常用客户端及商业智能(BI)工具,通过标准的ODBC和JDBC连接,提供了对结构化数据进行快速查询的功能。查询为多个物理资源之间的分布式并行查询。在AWS管理控制台中点击几次或调用一个API即可轻松地对Redshift数据仓库进行扩展或缩减。Amazon Redshift自动修补数据仓库并将其备份,并按照用户定义的保留期存储备份。Amazon Redshift利用复制和连续备份来提高可用性并改善数据持久性,从而能从组件或节点故障中自动恢复。此外,为了保护您的中转数据和静态数据,Amazon Redshift支持Amazon虚拟私有云(Amazon VPC)、SSL、AES-256加密和硬件安全模块(HSM)。

Amazon Redshift有哪些优势呢?

(1)专为数据仓库而优化

Amazon Redshift使用各种创新技术,对于大小在100GB到1PB或更高的数据集,拥有很强的查询能力。它使用列式存储、数据压缩及区域映射,降低了执行查询所需的I/O数量。Amazon Redshift拥有大规模并行处理(MPP)数据仓库架构,可对SQL操作进行并行分布处理,以利用所有可用的资源。基础硬件均为高性能数据处理而设计,使用本地附带的存储空间以最大化处理器与驱动器之间的吞吐量,同时使用10GigE网状网络以最大化节点之间的吞吐量。

(2)可扩展

仅需在AWS管理控制台中点击几次或通过一个简单的API调用,就能在性能或容量需要改变时,轻松地改变云数据仓库中的节点数或节点类型。通过密集存储(DS)节点,可以以非常低的价格使用硬盘(HDD)创建超大型数据仓库。通过密集计算(DC)节点,可以使用高速CPU、大量RAM和固态硬盘(SSD)创建超高性能数据仓库。利用Amazon Redshift,只要使用单个160GB DC1.Large节点即可开始,并能一路扩展到使用16TB DS2.8XLarge节点的1 PB或更多压缩用户数据。调整大小时,Amazon Redshift可将现有的集群置于只读模式,并预配置一个你选定大小的新集群,然后将数据从旧集群并行复制到新集群。在配置新集群的同时,可继续对旧集群进行查询。一旦数据被复制到新集群,Amazon Redshift会自动将查询重新定向至新集群,并移除旧集群。

6.Amazon S3文件系统

我们一般是利用Amazon EMR来运行Hadoop/Spark数据,业务高峰期间还需要开启大量的Spot Instance机器来并行处理数据,数据分析及访问的海量日志均存放在Amazon S3文件系统上面进行分析汇总再交由下端的业务系统来处理。Amazon S3具有高度持久性、可扩展性、安全性、快速且物美价廉的存储服务。借助EMR文件系统,Amazon EMR可以将Amazon S3安全高效地用作Hadoop的对象存储。Amazon EMR对Hadoop进行了大量的改进,因此我们可以无缝地处理Amazon S3中存储的大量数据。

7.DevOps

目前通过自动化运维工具和平台组同事们的努力,已经完成的工作有如下几个方面:

·bidder业务机器的自动增加或删减。

·分布式爬虫程序的Spot Instance自动增加或删减。

·redis的一致性哈希算法程序的逐步完善。

·线上机器的公私钥批量自动更换或增加。

·线上机器的代码、配置文件批量自动同步。

·图片服务做成了CDN模式以便于提供对外服务,增强用户体验。

·强大的预警系统和报警系统。

下一步DevOps要进行的工作:增大自动化配置管理工具Ansible在系统中的应用比重,跟AWS真正地结合起来。选择Ansible主要是因为拥有丰富的相关支持,包括现有的很多组件、模块、开源的Ansible部署和脚本。笔者也尝试了市面上所有的自动化运维和自动化配置工具,发现Ansible是对AWS支持得最好的一个。Ansible的开发过程是写大量的Playbook。现在Ansible支持的有251个模块,特别是对于云服务的支持。像AWS、Docker、OpenStack,部署脚本都放在一个子目录下。这就意味着把别人写的脚本拿过来,或者把别人写定义的Playbook拿过来非常容易。现在关于Ansible的开源脚本数量庞大,大约有3000多个项目,相信这个数字只会越来越多,这也意味着以后的很多DevOps工作会越来越简单容易。

8.压力测试及其他

系统上线前,测试组的同事会用Load Runner对主要业务机器bidder进行大量的压力测试工作。系统上线前及上线后,我们也会密切关注以下方面:

·系统整体的QPS情况,尤其是在业务的高峰时期。

·系统的整体响应时间及bidder机器和其他业务机器的响应时间。

·bidder机器的并发连接总数和带宽的使用情况。

·bidder机器的负载、CPU利用率、内存使用情况。

·redis分布式集群机器的内存使用情况。

·Scrapy分布式爬虫的Spot Instance的运行情况。

·ElasticSearch集群的全文搜索的效率。

经过同事们的共同努力和研究,我们的业务平台已经能承受更大流量的冲击,功能方面也在日趋完善。相信到最后,我们的业务平台一定能设计出最具性价比的方案。