大型网站技术架构:核心原理与案例分析.pdf
http://www.100md.com
2020年1月6日
![]() |
| 第1页 |
![]() |
| 第5页 |
![]() |
| 第15页 |
![]() |
| 第25页 |
![]() |
| 第37页 |
![]() |
| 第177页 |
参见附件(7866KB,303页)。
大型网站技术架构:核心原理与案例分析,这是由前阿里巴巴架构技术专家出版的一本网站架构教程书,为从事相关行业的用户提供各种架构原理和案例分析。

介绍
《大型网站技术架构:核心原理与案例分析》通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计案例,为读者呈现一幅包括技术选型、架构设计、性能优化、Web安全、系统发布、运维监控等在内的大型网站开发全景视图。
作者
李智慧,曾在阿里巴巴担任技术专家,参与阿里巴巴基础技术平台开发和www.alibaba.com架构设计。
目前就职英特尔亚太研发中心从事云计算与大数据方面的研发工作。
目录
第1篇 概述
1.大型网站架构演化
2.大型网站架构模式
3.大型网站核心架构要素
第2篇 架构
4.瞬时响应:网站的高性能架构
5.万无一失:网站的高可用架构
6.永无止境:网站的伸缩性架构
7.随需应变:网站的可扩展架构
8.固若金汤:网站的安全架构
第3篇 案例
9.淘宝网的架构演化案例分析
10.维基百科的高性能架构设计分析
11.海量分布式存储系统Doris 的高可用架构设计分析
12.网购秒杀系统架构设计案例分析
13.大型网站典型故障案例分析
第4篇 架构师
14.架构师领导艺术
15.网站架构师职场攻略
16.漫画网站架构师
附录A 大型网站架构技术一览
附录B Web开发技术发展历程
网站架构师是什么
网站架构,一般认为是根据客户需求分析的结果,准确定位网站目标群体,设定网站整体架构,规划、设计网站栏目及其内容,制定网站开发流程及顺序,以最大限度地进行高效资源分配与管理的设计。其内容有程序架构,呈现架构,和信息架构三种表现。而步骤主要分为硬架构和软架构两步程序。网络架构是现代网络学习和发展的一个必须的基础技术。
大型网站技术架构:核心原理与案例分析截图


大型网站技术架构
核心原理与案例分析
李智慧 著
电子工业出版社内容简介
本书通过梳理大型网站技术发展历程,剖析大型网站技术架构模
式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技
术架构设计案例,为读者呈现一幅包括技术选型、架构设计、性能优
化、Web安全、系统发布、运维监控等在内的大型网站开发全景视图。
本书不仅适用于指导网站工程师、架构师进行网站技术架构设计,也可用于指导产品经理、项目经理、测试运维人员等了解网站技术架构
的基础概念;还可供包括企业系统开发人员在内的各类软件开发从业人
员借鉴,了解大型网站的解决方案和开发理念。
未经许可,不得以任何方式复制或抄袭本书之部分或全部内容。
版权所有,侵权必究。
图书在版编目(CIP)数据
大型网站技术架构:核心原理与案例分析李智慧著.—北京:电子工
业出版社,2013.9
ISBN 978-7-121-21200-0
Ⅰ.①大… Ⅱ.①李… Ⅲ.①网站-建设 Ⅳ.①TP393.092
中国版本图书馆CIP数据核字(2013)第182399号责任编辑:徐津平
印 刷:三河市双峰印刷装订有限公司
装 订:三河市双峰印刷装订有限公司
出版发行:电子工业出版社
北京市海淀区万寿路173信箱 邮编:100036
开 本:720×1000 116 印张:15 字数:240千字
印 次:2013年9月第1次印刷
印 数:4000册 定价:59.00元
凡所购买电子工业出版社图书有缺损问题,请向购买书店调换。若书店
售缺,请与本社发行部联系,联系及邮购电话:(010)88254888。
质量投诉请发邮件至zlts@phei.com.cn,盗版侵权举报请发邮件至
dbqq@phei.com.cn。
服务热线:(010)88258888。好评袭来
这是我看过的最接地气的一本介绍互联网架构的书籍,深入阐述了
大型网站所面临的各种架构问题及解决方案,内容通俗易懂,而且对架
构师的领导艺术进行了介绍,很值得从事互联网的技术人员学习和参
考。
IBM咨询经理 种新华
此书读来亲切,能用不到300页的篇幅将网站架构的过去及未来说
得如此通俗易懂,与作者多年的亲身实践分不开,并由此想到一个问
题:当此书人手一本的时候,阿里、腾讯、京东……的面试官们怎么办
呢?
Oracle资深工程师 付银海
智慧同学,人如其名,在阿里巴巴,人称“教授”,可见其博学多
才。《大型网站技术架构:核心原理与案例分析》一书更是其多年积淀
厚积薄发之作,涵盖构建大型互联网应用所需的关键技术,兼具实用性
和前瞻性,无论是高并发、高性能还是海量数据处理、Web前端架构,都有针对性的解决之道。尤其难得的是此书还对架构师的内涵及技术管
理有比较深刻地阐述,实在是同类书籍中难得一见的。作为互联网应用
的开发者、架构师和创业者的你,一定不要错过本书,本书足以解决你的技术之忧。
拓维信息平台研发总监 陈斌
教授(本书作者在阿里巴巴的昵称)曾在知名的大型互联网公司第
一线浴血多年,经验不可谓不丰富,然而更难得的是他不仅博闻强记,更用行云流水的幽默文风,将现代大型互联网的内部要害一一庖解。也
许各家细节略有不同,但大部分的大型互联网站基本都可以用这样的视
角去解读。相信本书不仅对程序员,甚至对很多架构师也有参考价值,尤其值得关注的是教授在书中颇多技术之外的考量思索,我愿意称之为
互联网基因。
堆糖网技术合伙人 曹文炯
有幸拜读了这本《大型网站技术架构:核心原理与案例分析》,本
书从多个层面说明了如何构建一个高可用、高性能、高可扩展性的网站
系统,并结合了阿里巴巴及其他互联网企业先进的架构实践经验进行案
例分析,讲述非常全面且具指导意义。本书从网站的架构设计、快速开
发、高效部署、业务监控、服务治理、运维管理等多个角度描述了架构
设计的相关重点,涉及的核心技术包括前端优化、CDN、反向代理、缓
存、消息队列、分布式存储、分布式服务、NoSQL存储、搜索、监控、安全等一系列保证大型网站安全可靠运行的关键技术点。本书还提供了
网站如何从小型网站伴随用户成长,逐步扩展到大型网站的架构演进思
路,是互联网架构师们不可多得的一本技术参考书。
中兴通讯总工程师 钱煜明设计和规划一个网站的总体架构涉及方方面面的东西,备选的方案
也很多,如何在五花八门,纷繁复杂的技术中构建最适合用户的网站架
构,变成了一件极具争议和挑战性的工作。一个好的架构可以以最低的
成本,在满足用户需求的同时,满足整个网站的架构灵活性;同样,一
个糟糕的架构可能会让你的客户在花费了大量金钱后,得到一堆笨重、复杂且不切实际的东西,或是由于系统过于复杂,故障不断,或是由于
架构不够灵活,阻碍业务的发展等等。
回顾网站架构的发展历程,我们可以发现任何大型网站架构的发展
都非一蹴而就的,同自然界生物物竞天择的自然进化规律一样,大型网
站的架构发展和演变也基本遵循着类似的规律。我们可能无法想象几年
后网站架构的样子,因为在互联网行业快速变化的当下,你甚至很难准
确地预测未来一年网站的产品演变方向,甚至网站流量规模。于是,产
品设计师和工程师们提得最多的是迭代和演变,这在一个网站系统架构
设计过程中显得尤为重要,因为我们永远无法像传统行业一样,去精确
地估算,并按预先精确设计好的图纸去完成我们的产品。那是不是网站
的架构设计和规划就毫无规律及章法可循了呢?答案显然不是,在互联
网快速发展的今天,随着搜索引擎、电子商务、社交类等互联网产品逐
步应用到每个人的身边,大型网站的架构及很多关键技术的发展,在逐
步走向成熟。在构建一个大型网站过程中可能面临一些问题,人们正在
尝试逐渐总结并积累出一些具有通用性的、经过验证的且成熟的局部解
决方案,这也是本书将呈现给大家的内容。本书中,作者以自己多年大
型互联网网站的架构经验,尝试总结当下这些互联网行业中相对成熟且
经过大量案例检验的技术和方案。
相信通过阅读本书,您可以一窥大型网站架构的全貌。
阿里巴巴技术专家 余俊循序渐进,娓娓道来,语言生动,举重若轻。
阿里云高级专家 李文兆推荐序一
传统的企业应用系统主要面对的技术挑战是处理复杂凌乱、千变万
化的所谓业务逻辑,而大型网站主要面对的技术挑战是处理超大量的用
户访问和海量的数据处理;前者的挑战来自功能性需求,后者的挑战来
自非功能性需求;功能性需求也许还有“人月神话”聊以自慰,通过增加
人手解决问题,而非功能需求大多是实实在在的技术难题,无论有多少
工程师,做不到就是做不到。IT系统应用于企业管理已有超过半个世纪
的历史,人们在这方面积累了大量的知识和经验(架构模式,领域分
析,项目管理),而真正意义上大型网站从出现至今不过短短十多年的
时间,很多技术挑战还在摸索阶段。市面上关于传统企业应用开发的书
籍汗牛充栋,而真正能够深入全面地阐述大型网站技术架构的图书寥寥
无几。所以很多人就很困惑:为什么很多看起来不是很复杂的网站,比
如Facebook、淘宝,都需要大量顶尖高手来开发呢?
值得庆幸的是,作者为我们带来了这本《大型网站技术架构:核心
原理与案例分析》,比较全面地阐述了大型网站的主要技术挑战和解决
方案。宏观层面上,将网站架构的演化发展、架构模式、核心要素一一
道来;微观层面上,将网站架构常用的分布式缓存、负载均衡、消息队
列、分布式服务、甚至网站如何发布运维都逐一进行了阐述。大型网站
的技术之道尽在于此。
作者在阿里巴巴工作期间,一方面参与基础技术平台产品开发,一
方面参与网站架构设计,这些经历使作者能够比较全面地从理论和实践两个视角去看待和描述网站架构。书中的技术内容基本都从为什么
(Why)要这么做和如何去做(How)两个层面进行表述。读者可知其
然并知其所以然。
阅读本书也许不能使你就此掌握大型网站架构设计的屠龙之术,但
至少使你对网站架构的方法和思维方式能有全面了解。
开卷有益,应该指的就是这样的书。
支付宝研究员 潘磊推荐序二
这些年互联网技术蓬勃发展,各种成熟的组件、工具、框架越来越
丰富,各种理论逐渐发展成熟,各大公司公开的理论和实践资料也越来
越多,在各个领域都有比较成熟的解决方案,但是研究领先互联网公司
的架构,无论是Google、Facebook、Amazon还是淘宝、支付宝、腾
讯、百度,都各有其独特的地方。
各个环节都有成熟的产品或者方案,为什么这么多互联网公司的架
构还有如此明显的差异呢?是不是照着Google、Facebook、淘宝的架构
做,就能做好一个“大型的互联网应用”呢?
正如本书中所言:“好的设计绝对不是模仿、不是生搬硬套某个模
式,而是在对问题深刻理解之上的创造与创新,即使是‘微创新’,也是
让人耳目一新的似曾相识。山寨与创新的最大区别不在于是否抄袭、是
否模仿,而在于对问题和需求是否真正理解与把握。”
这些大型的互联网应用是设计出来的?还是演化出来的?在设计的
过程中需要考虑哪些因素?演化过程中都会面临哪些问题,哪些挑战?
本书从性能、可用性、伸缩性、扩展性、安全性几个网站核心架构
要素切入,全面地介绍了这些核心要素面临的问题域、理论基础及应对
方案;对这几个方面进行系统地分析,结合目前成熟的解决方案,以及
作者自己的工作经验,理论联系实际,踏实细致地提出合理的解决方
案,非常值得我们学习和借鉴。作者还通过对淘宝、Wikipedia、分布式存储系统、秒杀系统等案例
的分析,仔细探讨了典型互联网架构的演进过程,剖析了分布式系统设
计和实现中的挑战和解决方案,并研究了极端情况下,秒杀给网站带来
的难以预计的瞬间高并发冲击的应对策略和架构设计。还通过一些实实
在在发生过的故障案例分析,从另一个侧面来说明,我们在做技术架构
时,需要考量的一些关键点,这些分享都是不可多得的血泪经验。
本书观点明确,涉及的问题域有针对性和全面性,对问题的分析过
程清晰,提出的解决方案切实可行,充分结合了目前成功的互联网公司
的架构经验,结合了作者丰富的工作经验,是一本值得行业内人士学习
和关注的好书。
作者李智慧在互联网行业具有丰富的经验,在阿里巴巴工作的几年
中担任架构师,参与过多个重要的项目和产品的架构设计,遇到和处理
了很多复杂的问题,在这方面积累了大量的经验。本书是作者多年的架
构师经历,以及时刻的思考和积累的结晶,一词一句都是经验之谈,都
是智慧的闪亮。
感谢作者耗费精力给我们带来如此精炼而又内容丰富的一本好书。
支付宝资深架构师 王定乾序
我为什么要写这本书
我想写一本关于网站架构方面的书源起于2011年年末至2012年年初
发生的两件事。
2011年末,京东网图书促销,在打5折的基础上再满一百送一百,作为一个爱买书胜过爱读书的人,我对这种促销活动根本没有免疫力,于是兴致勃勃地在活动当天登录www.360buy.com,准备将收藏夹里的
图书一网打尽。
往购物车里尽情地塞了一堆书后,点击“购买”按钮,但是浏览器迟
迟没有响应,预感到京东的服务器可能因为并发访问量过高,超过了系
统的最大负载能力,果然过了一会,浏览器页面显示“Service is too
busy”。我不甘心,返回购物车页面继续点击“购买”按钮,浏览器继续
显示“Service is too busy”。
于是我猜测:能够正常访问购物车,却不能成功购买,问题应该是
出在订单系统,B2C网站生成一个订单需要经历扣减库存、扣减促销资
源、更新用户账户等一系列操作,这些操作大多是数据库事务操作,没
有办法通过缓存等手段来减轻数据库服务器负载压力,如果事前没有设
计好数据库伸缩性架构,那么京东的技术团队将遇到一个大麻烦。
当天晚上,我登录新浪微博,看到京东的大老板刘强东发布了一条微博:“我已经紧急采购了10台服务器,增强网站后台,明天继续促销
一天,一定让大家买到书”。即使在有成熟数据库伸缩性架构设计的前
提下,进行一次数据库扩容也是件棘手的事,而京东只需要一个晚上就
能搞定,让我对京东的技术实力刮目相看。
第二天一上班,我的第一件事就是登录www.360buy.com,点击“购
买”按钮后悲剧地发现页面还是“Service is too busy”。当天晚上,刘强东
又发布了一条微博:“请信息部的同事喝茶”。还配了一张照片:一张大
桌子,只有一杯茶,旁边放了一把刀……
我想京东信息部的同事绝对不是有意要捉弄他们的老板和客户,很
可能是他们错误地判断了系统的瓶颈及伸缩性架构的困难,对老板做出
了过度承诺,而这背后折射出的是他们对网站架构的本质缺乏了解。
另一件事发生在2012年年初,当时的中国铁道部官方售票网站
www.12306.cn在春运期间因为大量用户访问而崩溃,无法有效访问。
12306作为一个运营不久的网站,缺乏大规模并发访问处理的经验,遇
到一些问题其实不奇怪,不管花多少钱,经验教训都需要经历时间和挫
折才能得到。奇怪的是,12306的架构师似乎对这种可能发生的大规模
并发访问产生的问题完全没有一点概念,系统好像根本没有经过任何高
并发场景下的性能评估和性能测试,就那么干脆利落地崩溃了,趴在那
里长时间起不来。
这两件事情促使我想写一本关于网站架构的书,阐述网站技术架构
最基本的驱动力,基础的架构设计原理,以及架构方案选择的价值观。
希望软件工程师们在解决问题之前,能够认真思考自己面对的真正问题
究竟是什么,有哪些技术方案可以选择,其基本原理是什么。所以这本
书里没有高深的算法和聱牙诘屈的公式,也很少有程序代码。读者可以把本书当作网站架构设计的科普书,即使对网站架构没有什么了解,也
能够比较轻松地阅读。
在本书的写作过程中(2012年下半年),没有再看到京东促销宕机
的新闻,12306也逐渐稳定成熟。我们虽然无法猜测京东“信息部的同
事”和12306网站的工程师们付出了多少努力,但能在相对比较短的时间
里解决这些技术问题,也说明了网站架构其实并不难,真正能解决问题
的技术一定是简单的。
本书致力于把这些简单的技术和道理呈现给读者。
如何阅读本书
我自己读书不求甚解,遇到看不懂的地方就跳过去,但是希望作者
对难点和重点能换个角度和方式在后面章节再叙述,以帮助我重新思考
和认识前面不能理解的重要知识。
机械制图的时候,通常使用三视图描述一个机械零件,从正视、侧
视、俯视三个角度对一个零件绘图,从而全面描述一个零件的结构。软
件架构设计中常用的4+1视图模型,也是一种多角度描述软件系统设计
的手段。
本书中,重要的架构原理和技术方案都采用多角度描述的方法。
第1篇,从演化、模式、要素三个维度描述网站整体架构。
第2篇,从性能、可用性、伸缩性、扩展性、安全这五个要素方面
详细描述网站架构核心原理,其中重要的负载均衡、异步处理、分布式
缓存等技术方案又在不同章节从多角度进行描述。第3篇,通过几个具体案例再一次从整体和局部描述网站架构方
法。
第4篇,从架构师做事的角度回顾网站技术架构,读者在阅读前面
技术章节感到枯燥的时候,也可以跳到本篇休闲放松下。
阅读本书过程中有任何问题和建议,请联系作者。新浪微博:@大
型网站技术架构。致谢
2012年五一节前夕,当我拜访博文视点的编辑胡辛征,商谈出版一
本关于大型网站技术架构的图书时,没有想到自己面临的挑战是如此巨
大。
整个图书写作过程就像喝醉了酒:头痛如裂,有很多话想说,但又
不知该从何说起。
我很庆幸,这个过程有你们陪伴、支持、鼓励和帮助,是你们给了
我继续前行的勇气。
感谢易普际的培训顾问周腾飞,策划并鼓励我去写这本书。
感谢阿里巴巴的技术专家余俊和何坤,这本书的大纲和结构就是和
你们在钱塘江畔散步时聊出来的,但很遗憾最后没能成功蛊惑你们和我
一起创作本书。
感谢阿里巴巴高级开发工程师熊红亮、丁夏珍;IBM咨询经理种新
华、架构师吴业勇;百度产品经理王晟;Intel运维工程师Liu Gongmin
给予的建议和鼓励。
感谢博文视点的编辑刘皎、郑柳洁,以及许多我不知道名字的编辑
为本书最终出版付出的努力。
感谢阿里巴巴资深架构师潘磊、王定乾、钱霄、王齐,指引我进入网站架构的知识殿堂。
本书很多内容源自阿里同学们的知识库,原谅我无法一一致谢。
感谢我的妻子方芬香,你给予我一个新的世界,让我如此热爱生
活。目 录
好评袭来
推荐序一
推荐序二
序 我为什么要写这本书
致谢
第1篇 概述
1 大型网站架构演化
1.1 大型网站软件系统的特点
1.2 大型网站架构演化发展历程
1.2.1 初始阶段的网站架构
1.2.2 应用服务和数据服务分离
1.2.3 使用缓存改善网站性能
1.2.4 使用应用服务器集群改善网站的并发处理能力
1.2.5 数据库读写分离
1.2.6 使用反向代理和CDN加速网站响应
1.2.7 使用分布式文件系统和分布式数据库系统
1.2.8 使用NoSQL和搜索引擎
1.2.9 业务拆分
1.2.10 分布式服务
1.3 大型网站架构演化的价值观
1.3.1 大型网站架构技术的核心价值是随网站所需灵活应对
1.3.2 驱动大型网站技术发展的主要力量是网站的业务发展
1.4 网站架构设计误区
1.4.1 一味追随大公司的解决方案1.4.2 为了技术而技术
1.4.3 企图用技术解决所有问题
1.5 小结
2 大型网站架构模式
2.1 网站架构模式
2.1.1 分层
2.1.2 分割
2.1.3 分布式
2.1.4 集群
2.1.5 缓存
2.1.6 异步
2.1.7 冗余
2.1.8 自动化
2.1.9 安全
2.2 架构模式在新浪微博的应用
2.3 小结
3 大型网站核心架构要素
3.1 性能
3.2 可用性
3.3 伸缩性
3.4 扩展性
3.5 安全性
3.6 小结
第2篇 架构
4 瞬时响应:网站的高性能架构
4.1 网站性能测试
4.1.1 不同视角下的网站性能4.1.2 性能测试指标
4.1.3 性能测试方法
4.1.4 性能测试报告
4.1.5 性能优化策略
4.2 Web前端性能优化
4.2.1 浏览器访问优化
4.2.2 CDN加速
4.2.3 反向代理
4.3 应用服务器性能优化
4.3.1 分布式缓存
4.3.2 异步操作
4.3.3 使用集群
4.3.4 代码优化
4.4 存储性能优化
4.4.1 机械硬盘vs. 固态硬盘
4.4.2 B+树 vs. LSM树
4.4.3 RAID vs. HDFS
4.5 小结
5 万无一失:网站的高可用架构
5.1 网站可用性的度量与考核
5.1.1 网站可用性度量
5.1.2 网站可用性考核
5.2 高可用的网站架构
5.3 高可用的应用
5.3.1 通过负载均衡进行无状态服务的失效转移
5.3.2 应用服务器集群的Session管理
5.4 高可用的服务5.5 高可用的数据
5.5.1 CAP原理
5.5.2 数据备份
5.5.3 失效转移
5.6 高可用网站的软件质量保证
5.6.1 网站发布
5.6.2 自动化测试
5.6.3 预发布验证
5.6.4 代码控制
5.6.5 自动化发布
5.6.6 灰度发布
5.7 网站运行监控
5.7.1 监控数据采集
5.7.2 监控管理
5.8 小结
6 永无止境:网站的伸缩性架构
6.1 网站架构的伸缩性设计
6.1.1 不同功能进行物理分离实现伸缩
6.1.2 单一功能通过集群规模实现伸缩
6.2 应用服务器集群的伸缩性设计
6.2.1 HTTP重定向负载均衡
6.2.2 DNS域名解析负载均衡
6.2.3 反向代理负载均衡
6.2.4 IP负载均衡
6.2.5 数据链路层负载均衡
6.2.6 负载均衡算法
6.3 分布式缓存集群的伸缩性设计6.3.1 Memcached分布式缓存集群的访问模型
6.3.2 Memcached分布式缓存集群的伸缩性挑战
6.3.3 分布式缓存的一致性Hash算法
6.4 数据存储服务器集群的伸缩性设计
6.4.1 关系数据库集群的伸缩性设计
6.4.2 NoSQL数据库的伸缩性设计
6.5 小结
7 随需应变:网站的可扩展架构
7.1 构建可扩展的网站架构
7.2 利用分布式消息队列降低系统耦合性
7.2.1 事件驱动架构
7.2.2 分布式消息队列
7.3 利用分布式服务打造可复用的业务平台
7.3.1 Web Service与企业级分布式服务
7.3.2 大型网站分布式服务的需求与特点
7.3.3 分布式服务框架设计
7.4 可扩展的数据结构
7.5 利用开放平台建设网站生态圈
7.6 小结
8 固若金汤:网站的安全架构
8.1 道高一尺魔高一丈的网站应用攻击与防御
8.1.1 XSS攻击
8.1.2 注入攻击
8.1.3 CSRF攻击
8.1.4 其他攻击和漏洞
8.1.5 Web应用防火墙
8.1.6 网站安全漏洞扫描8.2 信息加密技术及密钥安全管理
8.2.1 单向散列加密
8.2.2 对称加密
8.2.3 非对称加密
8.2.4 密钥安全管理
8.3 信息过滤与反垃圾
8.3.1 文本匹配
8.3.2 分类算法
8.3.3 黑名单
8.4 电子商务风险控制
8.4.1 风险
8.4.2 风控
8.5 小结
第3篇 案例
9 淘宝网的架构演化案例分析
9.1 淘宝网的业务发展历程
9.2 淘宝网技术架构演化
9.3 小结
10 维基百科的高性能架构设计分析
10.1 Wikipedia网站整体架构
10.2 Wikipedia性能优化策略
10.2.1 Wikipedia前端性能优化
10.2.2 Wikipedia服务端性能优化
10.2.3 Wikipedia后端性能优化
11 海量分布式存储系统Doris的高可用架构设计分析
11.1 分布式存储系统的高可用架构
11.2 不同故障情况下的高可用解决方案11.2.1 分布式存储系统的故障分类
11.2.2 正常情况下系统访问结构
11.2.3 瞬时故障的高可用解决方案
11.2.4 临时故障的高可用解决方案
11.2.5 永久故障的高可用解决方案
12 网购秒杀系统架构设计案例分析
12.1 秒杀活动的技术挑战
12.2 秒杀系统的应对策略
12.3 秒杀系统架构设计
12.4 小结
13 大型网站典型故障案例分析
13.1 写日志也会引发故障
13.2 高并发访问数据库引发的故障
13.3 高并发情况下锁引发的故障
13.4 缓存引发的故障
13.5 应用启动不同步引发的故障
13.6 大文件读写独占磁盘引发的故障
13.7 滥用生产环境引发的故障
13.8 不规范的流程引发的故障
13.9 不好的编程习惯引发的故障
13.10 小结
第4篇 架构师
14 架构师领导艺术
14.1 关注人而不是产品
14.2 发掘人的优秀
14.3 共享美好蓝图
14.4 共同参与架构14.5 学会妥协
14.6 成就他人
15 网站架构师职场攻略
15.1 发现问题,寻找突破
15.2 提出问题,寻求支持
15.3 解决问题,达成绩效
16 漫话网站架构师
16.1 按作用划分架构师
16.2 按效果划分架构师
16.3 按职责角色划分架构师
16.4 按关注层次划分架构师
16.5 按口碑划分架构师
16.6 非主流方式划分架构师
附录A 大型网站架构技术一览
附录B Web开发技术发展历程
后记第1篇 概述1 大型网站架构演化
如果把上世纪90年代初CERN正式发布Web标准和第一个Web服务
的出现当做互联网站的开始,那么互联网站的发展只经历了短短20多年
的时间。在20多年的时间里,互联网的世界发生了巨大变化,今天,全
球有近一半的人口使用互联网,人们的生活因为互联网而产生了巨大改
变。从信息检索到即时通信,从电子购物到文化娱乐,互联网渗透到生
活的每个角落,而且这种趋势还在加速。因为互联网,我们的世界正变
得越来越小。
同时我们也看到,在互联网跨越式发展的进程中,在电子商务火热
的市场背后却是不堪重负的网站架构,某些B2C网站逢促销必宕机几乎
成为一种规律,而铁道部电子客票官方购票网站的频繁故障和操作延迟
更将这一现象演绎得淋漓尽致。
一边是企业在网站技术上的大量投入,一边却是网站在关键时刻的
频繁宕机;一边是工程师夜以继日地加班工作,一边却是网站故障频发
新功能上线缓慢;一边是互联网业务快速发展多领域挑战传统行业,一
边却是网站安全漏洞频发让网民胆战心惊怨声载道。
如何打造一个高可用、高性能、易扩展、可伸缩且安全的网站?如
何让网站随应用所需灵活变动,即使是山寨他人的产品,也可以山寨的
更高、更快、更强,一年时间用户数从零过亿呢?
1.1 大型网站软件系统的特点
与传统企业应用系统相比,大型互联网应用系统有以下特点。高并发,大流量: 需要面对高并发用户,大流量访问。Google日
均PV数35亿,日均IP访问数3亿;腾讯QQ的最大在线用户数1.4亿
(2011年数据);淘宝2012年“双十一”活动一天交易额超过191亿,活
动开始第一分钟独立访问用户达1000万。
高可用: 系统7×24小时不间断服务。大型互联网站的宕机事件通
常会成为新闻焦点,例如2010年百度域名被黑客劫持导致不能访问,成
为重大新闻热点。
海量数据: 需要存储、管理海量数据,需要使用大量服务器。
Facebook每周上传的照片数目接近10亿,百度收录的网页数目有数百
亿,Google有近百万台服务器为全球用户提供服务。
用户分布广泛,网络情况复杂: 许多大型互联网都是为全球用户
提供服务的,用户分布范围广,各地网络情况千差万别。在国内,还有
各个运营商网络互通难的问题。而中美光缆的数次故障,也让一些对国
外用户依赖较大的网站不得不考虑在海外建立数据中心。
安全环境恶劣: 由于互联网的开放性,使得互联网站更容易受到
攻击,大型网站几乎每天都会被黑客攻击。2011年国内多个重要网站泄
露用户密码,让普通用户也直面一次互联网安全问题。
需求快速变更,发布频繁: 和传统软件的版本发布频率不同,互
联网产品为快速适应市场,满足用户需求,其产品发布频率是极高的。
Office的产品版本以年为单位发布,而一般大型网站的产品每周都有新
版本发布上线,至于中小型网站的发布就更频繁了,有时候一天会发布
几十次。
渐进式发展: 与传统软件产品或企业应用系统一开始就规划好全部的功能和非功能需求不同,几乎所有的大型互联网站都是从一个小网
站开始,渐进地发展起来的。Facebook是伯克扎克同学在哈佛大学的宿
舍里开发的;Google的第一台服务器部署在斯坦福大学的实验室里;阿
里巴巴则是在马云家的客厅里诞生的。好的互联网产品都是慢慢运营出
来的,不是一开始就开发好的,这也正好与网站架构的发展演化过程对
应。
1.2 大型网站架构演化发展历程
大型网站的技术挑战主要来自于庞大的用户,高并发的访问和海量
的数据,任何简单的业务一旦需要处理数以P计的数据和面对数以亿计
的用户,问题就会变得很棘手。大型网站架构主要就是解决这类问题。
1.2.1 初始阶段的网站架构
大型网站都是从小型网站发展而来,网站架构也是一样,是从小型
网站架构逐步演化而来。小型网站最开始时没有太多人访问,只需要一
台服务器就绰绰有余,这时的网站架构如图1.1所示。图1.1 初始阶段的网站架构
应用程序、数据库、文件等所有的资源都在一台服务器上。通常服
务器操作系统使用Linux,应用程序使用PHP开发,然后部署在Apache
上,数据库使用MySQL,汇集各种免费开源软件及一台廉价服务器就
可以开始网站的发展之路了。
1.2.2 应用服务和数据服务分离
随着网站业务的发展,一台服务器逐渐不能满足需求:越来越多的
用户访问导致性能越来越差,越来越多的数据导致存储空间不足。这时
就需要将应用和数据分离。应用和数据分离后整个网站使用三台服务
器:应用服务器、文件服务器和数据库服务器,如图1.2所示。这三台
服务器对硬件资源的要求各不相同,应用服务器需要处理大量的业务逻
辑,因此需要更快更强大的CPU;数据库服务器需要快速磁盘检索和数
据缓存,因此需要更快的硬盘和更大的内存;文件服务器需要存储大量用户上传的文件,因此需要更大的硬盘。
图1.2 应用服务和数据服务分离
应用和数据分离后,不同特性的服务器承担不同的服务角色,网站
的并发处理能力和数据存储空间得到了很大改善,支持网站业务进一步
发展。但是随着用户逐渐增多,网站又一次面临挑战:数据库压力太大
导致访问延迟,进而影响整个网站的性能,用户体验受到影响。这时需
要对网站架构进一步优化。
1.2.3 使用缓存改善网站性能
网站访问特点和现实世界的财富分配一样遵循二八定律:80%的业
务访问集中在20%的数据上。淘宝买家浏览的商品集中在少部分成交数
多、评价良好的商品上;百度搜索关键词集中在少部分热门词汇上;只
有经常登录的用户才会发微博、看微博,而这部分用户也只占总用户数
目的一小部分。既然大部分的业务访问集中在一小部分数据上,那么如果把这一小
部分数据缓存在内存中,是不是就可以减少数据库的访问压力,提高整
个网站的数据访问速度,改善数据库的写入性能了呢?
网站使用的缓存可以分为两种:缓存在应用服务器上的本地缓存和
缓存在专门的分布式缓存服务器上的远程缓存。本地缓存的访问速度更
快一些,但是受应用服务器内存限制,其缓存数据量有限,而且会出现
和应用程序争用内存的情况。远程分布式缓存可以使用集群的方式,部
署大内存的服务器作为专门的缓存服务器,可以在理论上做到不受内存
容量限制的缓存服务,如图1.3所示。图1.3 网站使用缓存
使用缓存后,数据访问压力得到有效缓解,但是单一应用服务器能
够处理的请求连接有限,在网站访问高峰期,应用服务器成为整个网站
的瓶颈。
1.2.4 使用应用服务器集群改善网站的并发处理能力
使用集群是网站解决高并发、海量数据问题的常用手段。当一台服
务器的处理能力、存储空间不足时,不要企图去换更强大的服务器,对
大型网站而言,不管多么强大的服务器,都满足不了网站持续增长的业
务需求。这种情况下,更恰当的做法是增加一台服务器分担原有服务器
的访问及存储压力。
对网站架构而言,只要能通过增加一台服务器的方式改善负载压
力,就可以以同样的方式持续增加服务器不断改善系统性能,从而实现
系统的可伸缩性。应用服务器实现集群是网站可伸缩集群架构设计中较
为简单成熟的一种,如图1.4所示。图1.4 应用服务器集群部署
通过负载均衡调度服务器,可将来自用户浏览器的访问请求分发到
应用服务器集群中的任何一台服务器上,如果有更多的用户,就在集群
中加入更多的应用服务器,使应用服务器的负载压力不再成为整个网站
的瓶颈。
1.2.5 数据库读写分离
网站在使用缓存后,使绝大部分数据读操作访问都可以不通过数据
库就能完成,但是仍有一部分读操作(缓存访问不命中、缓存过期)和
全部的写操作需要访问数据库,在网站的用户达到一定规模后,数据库
因为负载压力过高而成为网站的瓶颈。
目前大部分的主流数据库都提供主从热备功能,通过配置两台数据
库主从关系,可以将一台数据库服务器的数据更新同步到另一台服务器
上。网站利用数据库的这一功能,实现数据库读写分离,从而改善数据库负载压力,如图1.5所示。
图1.5 数据库读写分离
应用服务器在写数据的时候,访问主数据库,主数据库通过主从复
制机制将数据更新同步到从数据库,这样当应用服务器读数据的时候,就可以通过从数据库获得数据。为了便于应用程序访问读写分离后的数
据库,通常在应用服务器端使用专门的数据访问模块,使数据库读写分
离对应用透明。
1.2.6 使用反向代理和CDN加速网站响应
随着网站业务不断发展,用户规模越来越大,由于中国复杂的网络
环境,不同地区的用户访问网站时,速度差别也极大。有研究表明,网
站访问延迟和用户流失率正相关,网站访问越慢,用户越容易失去耐心而离开。为了提供更好的用户体验,留住用户,网站需要加速网站访问
速度。主要手段有使用CDN和反向代理,如图1.6所示。
图1.6 网站使用反向代理和CDN加速访问
CDN和反向代理的基本原理都是缓存,区别在于CDN部署在网络
提供商的机房,使用户在请求网站服务时,可以从距离自己最近的网络
提供商机房获取数据;而反向代理则部署在网站的中心机房,当用户请
求到达中心机房后,首先访问的服务器是反向代理服务器,如果反向代
理服务器中缓存着用户请求的资源,就将其直接返回给用户。
使用CDN和反向代理的目的都是尽早返回数据给用户,一方面加快
用户访问速度,另一方面也减轻后端服务器的负载压力。
1.2.7 使用分布式文件系统和分布式数据库系统
任何强大的单一服务器都满足不了大型网站持续增长的业务需求。数据库经过读写分离后,从一台服务器拆分成两台服务器,但是随着网
站业务的发展依然不能满足需求,这时需要使用分布式数据库。文件系
统也是一样,需要使用分布式文件系统,如图1.7所示。
图1.7 使用分布式文件和分布式数据库系统
分布式数据库是网站数据库拆分的最后手段,只有在单表数据规模
非常庞大的时候才使用。不到不得已时,网站更常用的数据库拆分手段
是业务分库,将不同业务的数据库部署在不同的物理服务器上。
1.2.8 使用NoSQL和搜索引擎
随着网站业务越来越复杂,对数据存储和检索的需求也越来越复
杂,网站需要采用一些非关系数据库技术如NoSQL和非数据库查询技术
如搜索引擎,如图1.8所示。图1.8 使用NoSQL系统和搜索引擎
NoSQL和搜索引擎都是源自互联网的技术手段,对可伸缩的分布式
特性具有更好的支持。应用服务器则通过一个统一数据访问模块访问各
种数据,减轻应用程序管理诸多数据源的麻烦。
1.2.9 业务拆分
大型网站为了应对日益复杂的业务场景,通过使用分而治之的手段
将整个网站业务分成不同的产品线,如大型购物交易网站就会将首页、商铺、订单、买家、卖家等拆分成不同的产品线,分归不同的业务团队
负责。
具体到技术上,也会根据产品线划分,将一个网站拆分成许多不同
的应用,每个应用独立部署维护。应用之间可以通过一个超链接建立关
系(在首页上的导航链接每个都指向不同的应用地址),也可以通过消
息队列进行数据分发,当然最多的还是通过访问同一个数据存储系统来构成一个关联的完整系统,如图1.9所示。
图1.9 应用拆分
1.2.10 分布式服务
随着业务拆分越来越小,存储系统越来越庞大,应用系统的整体复
杂度呈指数级增加,部署维护越来越困难。由于所有应用要和所有数据
库系统连接,在数万台服务器规模的网站中,这些连接的数目是服务器
规模的平方,导致存数据库接资源不足,拒绝服务。
既然每一个应用系统都需要执行许多相同的业务操作,比如用户管
理、商品管理等,那么可以将这些共用的业务提取出来,独立部署。由
这些可复用的业务连接数据库,提供共用业务服务,而应用系统只需要
管理用户界面,通过分布式服务调用共用业务服务完成具体业务操作,如图1.10所示。图1.10 分布式服务
大型网站的架构演化到这里,基本上大多数的技术问题都得以解
决,诸如跨数据中心的实时数据同步和具体网站业务相关的问题也都可
以通过组合改进现有技术架构来解决。
但事物发展到一定阶段,就会拥有自身的发展冲动,摆脱其初衷,向着使自己更强大的方向发展。既然大型网站架构解决了海量数据的管
理和高并发事务的处理,那么就可以把这些解决方案应用到网站自身以
外的业务上去。我们看到目前许多大型网站都开始建设云计算平台,将
计算作为一种基础资源出售,中小网站不需要再关心技术架构问题,只
需要按需付费,就可以使网站随着业务的增长逐渐获得更大的存储空间
和更多的计算资源。
1.3 大型网站架构演化的价值观
这个世界没有哪个网站从诞生起就是大型网站;也没有哪个网站第
一次发布就拥有庞大的用户,高并发的访问,海量的数据;大型网站都
是从小型网站发展而来。网站的价值在于它能为用户提供什么价值,在于网站能做什么,而不在于它是怎么做的,所以在网站还很小的时候就
去追求网站的架构是舍本逐末,得不偿失的。小型网站最需要做的就是
为用户提供好的服务来创造价值,得到用户的认可,活下去,野蛮生
长。
所以我们看到,一方面是随着互联网的高速发展,越来越多新的软
件技术和产品从互联网公司诞生,挑战传统软件巨头的江湖地位。另一
方面却是中小网站十几年如一日地使用LAMP技术(Linux+Apache+
MySQL+PHP)开发自己的网站,因为LAMP既便宜又简单,而且对付
一个中小型网站绰绰有余。
1.3.1 大型网站架构技术的核心价值是随网站所需灵活应对
大型网站架构技术的核心价值不是从无到有搭建一个大型网站,而
是能够伴随小型网站业务的逐步发展,慢慢地演化成一个大型网站。在
这个漫长的技术演化过程中,不需要放弃什么,不需要推翻什么,不需
要剧烈的革命,就那么润物细无声地把一个只有一台服务器,几百个用
户的小网站演化成一个几十万台服务器,数十亿用户的大网站。今天我
们看到的大型网站,Google,Facebook,Taobao,Baidu莫不遵循这样
的技术演化路线。
1.3.2 驱动大型网站技术发展的主要力量是网站的业务发展
创新的业务发展模式对网站架构逐步提出更高要求,才使得创新的
网站架构得以发展成熟。是业务成就了技术,是事业成就了人,而不是
相反。所以网站架构师应该对成就自己技术成绩的网站事业心存感恩,并努力提高技术回馈业务,才能在快速发展的互联网领域保持持续进
步。不过我们也看到有些传统企业投身互联网,在业务问题还没有理清
楚的时候就从外面挖来许多技术高手,仿照成功的互联网公司打造技术
平台,这无疑是南辕北辙,缘木求鱼。而这些技术高手离开了它们熟悉
的环境和工作模式,也是张飞拿着绣花针使不上劲来。
1.4 网站架构设计误区
在大型网站架构发展过程中有如下几个容易出现的误区。
1.4.1 一味追随大公司的解决方案
由于大公司巨大成功的光环效应,再加上从大公司挖来的技术高手
的影响,网站在讨论架构决策时,最有说服力的一句话就成了“淘宝就
是这么搞的”或者“Facebook就是这么搞的”。
大公司的经验和成功模式固然重要,值得学习借鉴,但如果因此而
变得盲从,就失去了坚持自我的勇气,在架构演化的道路上迟早会迷
路。
1.4.2 为了技术而技术
网站技术是为业务而存在的,除此毫无意义。在技术选型和架构设
计中,脱离网站业务发展的实际,一味追求时髦的新技术,可能会将网
站技术发展引入崎岖小道,架构之路越走越难。
1.4.3 企图用技术解决所有问题
最典型的例子就是2012年年初12306故障事件后,软件开发技术界
的反应。各路专业和非专业人士众说纷纭地帮12306的技术架构出谋划策,甚至有人提议帮12306写一个开源的网站,解决其大规模并发访问的问
题。
12306真正的问题其实不在于它的技术架构,而在于它的业务架
构:12306根本就不应该在几亿中国人一票难求的情况下以窗口售票的
模式在网上售票(零点开始出售若干天后的车票)。12306需要重构的
不仅是它的技术架构,更重要的是它的业务架构:调整业务需求,换一
种方式卖票,而不要去搞促销秒杀这种噱头式的游戏。
后来证明12306确实是朝这个方向发展的:在售票方式上引入了排
队机制、整点售票调整为分时段售票。其实如果能控制住并发访问的
量,很多棘手的技术问题也就不是什么问题了。
技术是用来解决业务问题的,而业务的问题,也可以通过业务的手
段去解决。
1.5 小结
时至今日,大型网站的架构演化方案已经非常成熟,各种技术方案
也逐渐产品化。许多小型网站已经慢慢不需要再经历大型网站经历过的
架构演化之路就可以逐步发展壮大,因为现在越来越多的网站从建立之
初就是搭建在大型网站提供的云计算服务基础之上,所需要的一切技术
资源:计算、存储、网络都可以按需购买,线性伸缩,不需要自己一点
一点地拼凑各种资源,综合使用各种技术方案逐步去完善自己的网站架
构了。
所以能亲身经历一个网站从小到大的架构演化过程的网站架构师越来越少,虽然过去有这种经历的架构师也很少(从小型网站发展成大型
网站的机会本来就极少),但是将来可能真就没有了。
但也正因为网站架构技术演化过程难以重现,所以网站架构师更应
该对这个过程深刻了解,理解已成熟的网站架构技术方案的来龙去脉和
历史渊源,在技术选型和架构决策时才能有的放矢,直击要害。2 大型网站架构模式
关于什么是模式,这个来自建筑学的词汇是这样定义的:“每一个
模式描述了一个在我们周围不断重复发生的问题及该问题解决方案的
核心。这样,你就能一次又一次地使用该方案而不必做重复工作”。
模式的关键在于模式的可重复性,问题与场景的可重复性带来解决方案
的可重复使用。
我们的现实生活中充斥着几乎千篇一律的人生架构模
式:读重点学校,选热门专业,进稳定高收入的政府部门和
企业,找门当户对的配偶,生一个听话的孩子继续这个模
式……但是人生不同于软件,精彩的人生绝不会来自于复
制。
也许互联网产品不是随便复制就能成功的,创新的产品更能为用户
创造价值。但是网站架构却有一些共同的模式,这些模式已经被许多大
型网站一再验证,通过对这些模式的学习,我们可以掌握大型网站架构
的一般思路和解决方案,以指导我们的架构设计。
2.1 网站架构模式
为了解决大型网站面临的高并发访问、海量数据处理、高可靠运行
等一系列问题与挑战,大型互联网公司在实践中提出了许多解决方案,以实现网站高性能、高可用、易伸缩、可扩展、安全等各种技术架构目标。这些解决方案又被更多网站重复使用,从而逐渐形成大型网站架构
模式。
2.1.1 分层
分层是企业应用系统中最常见的一种架构模式,将系统在横向维度
上切分成几个部分,每个部分负责一部分相对比较单一的职责,然后通
过上层对下层的依赖和调用组成一个完整的系统。
分层结构在计算机世界中无处不在,网络的7层通信协议是一种分
层结构;计算机硬件、操作系统、应用软件也可以看作是一种分层结
构。在大型网站架构中也采用分层结构,将网站软件系统分为应用层、服务层、数据层,如表2.1所示。
表2.1 网站分层架构
通过分层,可以更好地将一个庞大的软件系统切分成不同的部分,便于分工合作开发和维护;各层之间具有一定的独立性,只要维持调用
接口不变,各层可以根据具体问题独立演化发展而不需要其他层必须做
出相应调整。
但是分层架构也有一些挑战,就是必须合理规划层次边界和接口,在开发过程中,严格遵循分层架构的约束,禁止跨层次的调用(应用层
直接调用数据层)及逆向调用(数据层调用服务层,或者服务层调用应
用层)。在实践中,大的分层结构内部还可以继续分层,如应用层可以再细
分为视图层(美工负责)和业务逻辑层(工程师负责);服务层也可以
细分为数据接口层(适配各种输入和输出的数据格式)和逻辑处理层。
分层架构是逻辑上的,在物理部署上,三层结构可以部署在同一个
物理机器上,但是随着网站业务的发展,必然需要对已经分层的模块分
离部署,即三层结构分别部署在不同的服务器上,使网站拥有更多的计
算资源以应对越来越多的用户访问。
所以虽然分层架构模式最初的目的是规划软件清晰的逻辑结构便于
开发维护,但在网站的发展过程中,分层结构对网站支持高并发向分布
式方向发展至关重要。因此在网站规模还很小的时候就应该采用分层的
架构,这样将来网站做大时才能有更好地应对。
2.1.2 分割
如果说分层是将软件在横向方面进行切分,那么分割就是在纵向方
面对软件进行切分。
网站越大,功能越复杂,服务和数据处理的种类也越多,将这些不
同的功能和服务分割开来,包装成高内聚低耦合的模块单元,一方面有
助于软件的开发和维护;另一方面,便于不同模块的分布式部署,提高
网站的并发处理能力和功能扩展能力。
大型网站分割的粒度可能会很小。比如在应用层,将不同业务进行
分割,例如将购物、论坛、搜索、广告分割成不同的应用,由独立的团
队负责,部署在不同的服务器上;在同一个应用内部,如果规模庞大业
务复杂,会继续进行分割,比如购物业务,可以进一步分割成机票酒店
业务、3C业务,小商品业务等更细小的粒度。而即使在这个粒度上,还是可以继续分割成首页、搜索列表、商品详情等模块,这些模块不管在
逻辑上还是物理部署上,都可以是独立的。同样在服务层也可以根据需
要将服务分割成合适的模块。
2.1.3 分布式
对于大型网站,分层和分割的一个主要目的是为了切分后的模块便
于分布式部署,即将不同模块部署在不同的服务器上,通过远程调用协
同工作。分布式意味着可以使用更多的计算机完成同样的功能,计算机
越多,CPU、内存、存储资源也就越多,能够处理的并发访问和数据量
就越大,进而能够为更多的用户提供服务。
但分布式在解决网站高并发问题的同时也带来了其他问题。首先,分布式意味着服务调用必须通过网络,这可能会对性能造成比较严重的
影响;其次,服务器越多,服务器宕机的概率也就越大,一台服务器宕
机造成的服务不可用可能会导致很多应用不可访问,使网站可用性降
低;另外,数据在分布式的环境中保持数据一致性也非常困难,分布式
事务也难以保证,这对网站业务正确性和业务流程有可能造成很大影
响;分布式还导致网站依赖错综复杂,开发管理维护困难。因此分布式
设计要根据具体情况量力而行,切莫为了分布式而分布式。
在网站应用中,常用的分布式方案有以下几种。
分布式应用和服务: 将分层和分割后的应用和服务模块分布式部
署,除了可以改善网站性能和并发性、加快开发和发布速度、减少数据
库连接资源消耗外;还可以使不同应用复用共同的服务,便于业务功能
扩展。
分布式静态资源: 网站的静态资源如JS,CSS,Logo图片等资源独立分布式部署,并采用独立的域名,即人们常说的动静分离。静态资
源分布式部署可以减轻应用服务器的负载压力;通过使用独立域名加快
浏览器并发加载的速度;由负责用户体验的团队进行开发维护有利于网
站分工合作,使不同技术工种术业有专攻。
分布式数据和存储: 大型网站需要处理以P为单位的海量数据,单
台计算机无法提供如此大的存储空间,这些数据需要分布式存储。除了
对传统的关系数据库进行分布式部署外,为网站应用而生的各种NoSQL
产品几乎都是分布式的。
分布式计算: 严格说来,应用、服务、实时数据处理都是计算,网站除了要处理这些在线业务,还有很大一部分用户没有直观感受的后
台业务要处理,包括搜索引擎的索引构建、数据仓库的数据分析统计
等。这些业务的计算规模非常庞大,目前网站普遍使用Hadoop及其
MapReduce分布式计算框架进行此类批处理计算,其特点是移动计算而
不是移动数据,将计算程序分发到数据所在的位置以加速计算和分布式
计算。
此外,还有可以支持网站线上服务器配置实时更新的分布式配置;
分布式环境下实现并发和协同的分布式锁; 支持云存储的分布式文件
系统等。
2.1.4 集群
使用分布式虽然已经将分层和分割后的模块独立部署,但是对于用
户访问集中的模块(比如网站的首页),还需要将独立部署的服务器集
群化,即多台服务器部署相同应用构成一个集群,通过负载均衡设备共
同对外提供服务。因为服务器集群有更多服务器提供相同服务,因此可以提供更好的
并发特性,当有更多用户访问的时候,只需要向集群中加入新的机器即
可。同时因为一个应用由多台服务器提供,当某台服务器发生故障时,负载均衡设备或者系统的失效转移机制会将请求转发到集群中其他服务
器上,使服务器故障不影响用户使用。所以在网站应用中,即使是访问
量很小的分布式应用和服务,也至少要部署两台服务器构成一个小的集
群,目的就是提高系统的可用性。
2.1.5 缓存
缓存就是将数据存放在距离计算最近的位置以加快处理速度。缓存
是改善软件性能的第一手段,现代CPU越来越快的一个重要因素就是使
用了更多的缓存,在复杂的软件设计中,缓存几乎无处不在。大型网站
架构设计在很多方面都使用了缓存设计。
CDN: 即内容分发网络,部署在距离终端用户最近的网络服务
商,用户的网络请求总是先到达他的网络服务商那里,在这里缓存网站
的一些静态资源(较少变化的数据),可以就近以最快速度返回给用
户,如视频网站和门户网站会将用户访问量大的热点内容缓存在CDN。
反向代理: 反向代理属于网站前端架构的一部分,部署在网站的
前端,当用户请求到达网站的数据中心时,最先访问到的就是反向代理
服务器,这里缓存网站的静态资源,无需将请求继续转发给应用服务器
就能返回给用户。
本地缓存: 在应用服务器本地缓存着热点数据,应用程序可以在
本机内存中直接访问数据,而无需访问数据库。
分布式缓存: 大型网站的数据量非常庞大,即使只缓存一小部分,需要的内存空间也不是单机能承受的,所以除了本地缓存,还需要
分布式缓存,将数据缓存在一个专门的分布式缓存集群中,应用程序通
过网络通信访问缓存数据。
使用缓存有两个前提条件,一是数据访问热点不均衡,某些数据会
被更频繁的访问,这些数据应该放在缓存中;二是数据在某个时间段内
有效,不会很快过期,否则缓存的数据就会因已经失效而产生脏读,影
响结果的正确性。网站应用中,缓存除了可以加快数据访问速度,还可
以减轻后端应用和数据存储的负载压力,这一点对网站数据库架构至关
重要,网站数据库几乎都是按照有缓存的前提进行负载能力设计的。
2.1.6 异步
计算机软件发展的一个重要目标和驱动力是降低软件耦合性。事物
之间直接关系越少,就越少被彼此影响,越可以独立发展。大型网站架
构中,系统解耦合的手段除了前面提到的分层、分割、分布等,还有一
个重要手段是异步,业务之间的消息传递不是同步调用,而是将一个业
务操作分成多个阶段,每个阶段之间通过共享数据的方式异步执行进行
协作。
在单一服务器内部可通过多线程共享内存队列的方式实现异步,处
在业务操作前面的线程将输出写入到队列,后面的线程从队列中读取数
据进行处理;在分布式系统中,多个服务器集群通过分布式消息队列实
现异步,分布式消息队列可以看作内存队列的分布式部署。
异步架构是典型的生产者消费者模式,两者不存在直接调用,只要
保持数据结构不变,彼此功能实现可以随意变化而不互相影响,这对网
站扩展新功能非常便利。除此之外,使用异步消息队列还有如下特性。提高系统可用性。 消费者服务器发生故障,数据会在消息队列服
务器中存储堆积,生产者服务器可以继续处理业务请求,系统整体表现
无故障。消费者服务器恢复正常后,继续处理消息队列中的数据。
加快网站响应速度。 处在业务处理前端的生产者服务器在处理完
业务请求后,将数据写入消息队列,不需要等待消费者服务器处理就可
以返回,响应延迟减少。
消除并发访问高峰。 用户访问网站是随机的,存在访问高峰和低
谷,即使网站按照一般访问高峰进行规划和部署,也依然会出现突发事
件,比如购物网站的促销活动,微博上的热点事件,都会造成网站并发
访问突然增大,这可能会造成整个网站负载过重,响应延迟,严重时甚
至会出现服务宕机的情况。使用消息队列将突然增加的访问请求数据放
入消息队列中,等待消费者服务器依次处理,就不会对整个网站负载造
成太大压力。
但需要注意的是,使用异步方式处理业务可能会对用户体验、业务
流程造成影响,需要网站产品设计方面的支持。
2.1.7 冗余
网站需要7724小时连续运行,但是服务器随时可能出现故障,特别
是服务器规模比较大时,出现某台服务器宕机是必然事件。要想保证在
服务器宕机的情况下网站依然可以继续服务,不丢失数据,就需要一定
程度的服务器冗余运行,数据冗余备份,这样当某台服务器宕机时,可
以将其上的服务和数据访问转移到其他机器上。
访问和负载很小的服务也必须部署至少两台服务器构成一个集群,其目的就是通过冗余实现服务高可用。数据库除了定期备份,存档保存,实现冷备份 外,为了保证在线业务高可用,还需要对数据库进行
主从分离,实时同步实现热备份 。
为了抵御地震、海啸等不可抗力导致的网站完全瘫痪,某些大型网
站会对整个数据中心进行备份,全球范围内部署灾备数据中心 。网站
程序和数据实时同步到多个灾备数据中心。
2.1.8 自动化
在无人值守的情况下网站可以正常运行,一切都可以自动化是网站
的理想状态。目前大型网站的自动化架构设计主要集中在发布运维方
面。
发布对网站都是头等大事,许多网站故障出在发布环节,网站工程
师经常加班也是因为发布不顺利。通过减少人为干预,使发布过程自动
化 可有效减少故障。发布过程包括诸多环节。自动化代码管理 ,代码
版本控制、代码分支创建合并等过程自动化,开发工程师只要提交自己
参与开发的产品代号,系统就会自动为其创建开发分支,后期会自动进
行代码合并;自动化测试 ,代码开发完成,提交测试后,系统自动将
代码部署到测试环境,启动自动化测试用例进行测试,向相关人员发送
测试报告,向系统反馈测试结果;自动化安全检测 ,安全检测工具通
过对代码进行静态安全扫描及部署到安全测试环境进行安全攻击测试,评估其安全性;最后进行自动化部署 ,将工程代码自动部署到线上生
产环境。
此外,网站在运行过程中可能会遇到各种问题:服务器宕机、程序
Bug、存储空间不足、突然爆发的访问高峰。网站需要对线上生产环境
进行自动化监控 ,对服务器进行心跳检测,并监控其各项性能指标和应用程序的关键数据指标。如果发现异常、超出预设的阈值,就进行自
动化报警 ,向相关人员发送报警信息,警告故障可能会发生。在检测
到故障发生后,系统会进行自动化失效转移 ,将失效的服务器从集群
中隔离出去,不再处理系统中的应用请求。待故障消除后,系统进行自
动化失效恢复 ,重新启动服务,同步数据保证数据的一致性。在网站
遇到访问高峰,超出网站最大处理能力时,为了保证整个网站的安全可
用,还会进行自动化降级 ,通过拒绝部分请求及关闭部分不重要的服
务将系统负载降至一个安全的水平,必要时,还需要自动化分配资源
,将空闲资源分配给重要的服务,扩大其部署规模。
2.1.9 安全
互联网的开放特性使得其从诞生起就面对巨大的安全挑战,网站在
安全架构方面也积累了许多模式:通过密码 和手机校验码 进行身份认
证;登录、交易等操作需要对网络通信进行加密 ,网站服务器上存储
的敏感数据如用户信息等也进行加密处理;为了防止机器人程序滥用网
络资源攻击网站,网站使用验证码 进行识别;对于常见的用于攻击 网
站的XSS攻击、SQL注入、进行编码转换等相应处理;对于垃圾信息、敏感信息进行过滤 ;对交易转账等重要操作根据交易模式和交易信息
进行风险控制 。
2.2 架构模式在新浪微博的应用
短短几年时间新浪微博的用户数就从零增长到数亿,明星用户的粉
丝数达数千万,围绕着新浪微博正在发展一个集社交、媒体、游戏、电
商等多位一体的生态系统。
同大多数网站一样,新浪微博也是从一个小网站发展起来的。简单的LAMP(Linux+Apache+MySQL+PHP)架构,支撑起最初的新浪
微博,应用程序用PHP开发,所有的数据,包括微博、用户、关系都存
储在MySQL数据库中。
这样简单的架构无法支撑新浪微博快速发展的业务需求,随着访问
用户的逐渐增加,系统不堪重负。新浪微博的架构在较短时间内几经重
构,最后形成现在的架构,如图2.1所示。
图2.1 新浪微博的系统架构
(图片来源:http:timyang.netarchitectureweibo)
系统分为三个层次 ,最下层是基础服务层,提供数据库、缓存、存储、搜索等数据服务,以及其他一些基础技术服务,这些服务支撑了
新浪微博的海量数据和高并发访问,是整个系统的技术基础。
中间层是平台服务和应用服务层,新浪微博的核心服务是微博、关
系和用户,它们是新浪微博业务大厦的支柱。这些服务被分割为独立的
服务模块,通过依赖调用和共享基础数据构成新浪微博的业务基础。最上层是API和新浪微博的业务层,各种客户端(包括Web网站)
和第三方应用,通过调用API集成到新浪微博的系统中,共同组成一个
生态系统。
这些被分层和分割后的业务模块与基础技术模块分布式 部署,每
个模块都部署在一组独立的服务器集群上 ,通过远程调用的方式进行
依赖访问。新浪微博在早期还使用过一种叫作MPSS(MultiPort Single
Server,单服务器多端口)的分布式集群部署方案,在集群中的多台服
务器上,每台都部署多个服务,每个服务使用不同的端口对外提供服
务,通过这种方式使得有限的服务器可以部署更多的服务实例,改善服
务的负载均衡和可用性。现在网站应用中常见的将物理机虚拟化成多个
虚拟机后,在虚拟机上部署应用的方案跟新浪微博的MPSS方案异曲同
工,只是更加简单,还能在不同虚拟机上使用相同的端口号。
在新浪微博的早期架构中,微博发布使用同步推模式,用户发表微
博后系统会立即将这条微博插入到数据库所有粉丝的订阅列表中,当用
户量比较大时,特别是明星用户发布微博时,会引起大量的数据库写操
作,超出数据库负载,系统性能急剧下降,用户响应延迟加剧。后来新
浪微博改用异步 推拉结合的模式,用户发表微博后系统将微博写入消
息队列后立即返回,用户响应迅速,消息队列消费者任务将微博推送给
所有当前在线粉丝的订阅列表中,非在线用户登录后再根据关注列表拉
取微博订阅列表。
由于微博频繁刷新,新浪微博使用多级缓存 策略,热门微博和明
星用户的微博缓存在所有的微博服务器上,在线用户的微博和近期微博
缓存在分布式缓存集群中,对于微博操作中最常见的“刷微博”操作,几
乎全部都是缓存访问操作,可以获得很好的系统性能。为了提高系统的整体可用性和性能,新浪微博启用了多个数据中
心。这些数据中心既是地区用户访问中心,用户可以就近访问最近的数
据中心以加快访问速度,改善系统性能;同时也是数据冗余 复制的灾
备中心,所有的用户和微博数据通过远程消息系统在不同的数据中心之
间同步,提高系统可用性。
同时,新浪微博还开发了一系列自动化 工具,包括自动化监控,自动化发布,自动化故障修复等,这些自动化工具还在持续开发中,以
改善运维水平提高系统可用性。
由于微博的开放特性,新浪微博也遇到了一系列的安全 挑战,垃
圾内容、僵尸粉、微博攻击从未停止,除了使用一般网站常见的安全策
略,新浪微博在开放平台上使用多级安全审核的策略以保护系统和用
户。
2.3 小结
在程序设计与架构设计领域,模式正变得越来越受人关注,许多人
寄希望通过模式一劳永逸地解决自己的问题。正确使用模式可以更好地
利用业界和前人的思想与实践,用更少的时间开发出更好的系统,使设
计者的水平也达到更高的境界。但是模式受其适用场景限制,对系统的
要求和约束也很多,不恰当地使用模式只会画虎不成反类犬,不但没有
解决原来的老问题,反而带来了更棘手的新问题。
好的设计绝对不是模仿,不是生搬硬套某个模式,而是对问题深刻
理解之上的创造与创新,即使是“微创新”,也是让人耳目一新的似曾相
识。山寨与创新的最大区别不在于是否抄袭,是否模仿,而在于对问题
和需求是否真正理解与把握。3 大型网站核心架构要素
关于什么是架构 ,一种比较通俗的说法是“最高层次的规划,难以
改变的决定” ,这些规划和决定奠定了事物未来发展的方向和最终的蓝
图。
从这个意义上说,人生规划也是一种架构。选什么学
校、学什么专业、进什么公司、找什么对象,过什么样的生
活,都是自己人生的架构。
具体到软件架构 ,维基百科是这样定义的:“有关软件整体结构与
组件的抽象描述,用于指导大型软件系统各个方面的设计” 。系统的
各个重要组成部分及其关系构成了系统的架构,这些组成部分可以是具
体的功能模块,也可以是非功能的设计与决策,他们相互关系组成一个
整体,共同构成了软件系统的架构。
一般说来,除了当前的系统功能需求外,软件架构还需要关注性
能、可用性、伸缩性、扩展性和安全性这5个架构要素,架构设计过程
中需要平衡这5个要素之间的关系以实现需求和架构目标,也可以通过
考察这些架构要素来衡量一个软件架构设计的优劣,判断其是否满足期
望。
3.1 性能
性能是网站的一个重要指标,除非是没得选择(比如只能到www.12306.cn这一个网站上买火车票),否则用户无法忍受一个响应缓
慢的网站。一个打开缓慢的网站会导致严重的用户流失,很多时候网站
性能问题是网站架构升级优化的触发器。可以说性能是网站架构设计的
一个重要方面,任何软件架构设计方案都必须考虑可能会带来的性能问
题。
也正是因为性能问题几乎无处不在,所以优化网站性能的手段也非
常多,从用户浏览器到数据库,影响用户请求的所有环节都可以进行性
能优化。
在浏览器端,可以通过浏览器缓存、使用页面压缩、合理布局页
面、减少Cookie传输等手段改善性能。
还可以使用CDN,将网站静态内容分发至离用户最近的网络服务商
机房,使用户通过最短访问路径获取数据。可以在网站机房部署反向代
理服务器,缓存热点文件,加快请求响应速度,减轻应用服务器负载压
力。
在应用服务器端,可以使用服务器本地缓存和分布式缓存,通过缓
存在内存中的热点数据处理用户请求,加快请求处理过程,减轻数据库
负载压力。
也可以通过异步操作将用户请求发送至消息队列等待后续任务处
理,而当前请求直接返回响应给用户。
在网站有很多用户高并发请求的情况下,可以将多台应用服务器组
成一个集群共同对外服务,提高整体处理能力,改善性能。
在代码层面,也可以通过使用多线程、改善内存管理等手段优化性能。
在数据库服务器端,索引、缓存、SQL优化等性能优化手段都已经
比较成熟。而方兴未艾的NoSQL数据库通过优化数据模型、存储结构、伸缩特性等手段在性能方面的优势也日趋明显。
衡量网站性能有一系列指标,重要的有响应时间、TPS、系统性能
计数器等,通过测试这些指标以确定系统设计是否达到目标。这些指标
也是网站监控的重要参数,通过监控这些指标可以分析系统瓶颈,预测
网站容量,并对异常指标进行报警,保障系统可用性。
对于网站而言,性能符合预期仅仅是必要条件,因为无法预知网站
可能会面临的访问压力,所以必须要考察系统在高并发访问情况下,超
出负载设计能力的情况下可能会出现的性能问题。网站需要长时间持续
运行,还必须保证系统在持续运行且访问压力不均匀的情况下保持稳定
的性能特性。
3.2 可用性
对于大型网站而言,特别是知名网站,网站宕掉、服务不可用是一
个重大的事故,轻则影响网站声誉,重则可能会摊上官司。对于电子商
务类网站,网站不可用还意味着损失金钱和用户。因此几乎所有网站都
承诺7724可用,但事实上任何网站都不可能达到完全的7724可用,总会
有一些故障时间,扣除这些故障时间,就是网站的总可用时间,这个时
间可以换算成网站的可用性指标,以此衡量网站的可用性,一些知名大
型网站可以做到4个9以上的可用性,也就是可用性超过99.99%。
因为网站使用的服务器硬件通常是普通的商用服务器,这些服务器的设计目标本身并不保证高可用,也就是说,很有可能会出现服务器硬
件故障,也就是俗称的服务器宕机。大型网站通常都会有上万台服务
器,每天都必定会有一些服务器宕机,因此网站高可用架构设计的前提
是必然会出现服务器宕机,而高可用设计的目标就是当服务器宕机的时
候,服务或者应用依然可用。
网站高可用的主要手段是冗余,应用部署在多台服务器上同时提供
访问,数据存储在多台服务器上互相备份,任何一台服务器宕机都不会
影响应用的整体可用,也不会导致数据丢失。
对于应用服务器而言,多台应用服务器通过负载均衡设备组成一个
集群共同对外提供服务,任何一台服务器宕机,只需把请求切换到其他
服务器就可实现应用的高可用,但是一个前提条件是应用服务器上不能
保存请求的会话信息。否则服务器宕机,会话丢失,即使将用户请求转
发到其他服务器上也无法完成业务处理。
对于存储服务器,由于其上存储着数据,需要对数据进行实时备
份,当服务器宕机时需要将数据访问转移到可用的服务器上,并进行数
据恢复以保证继续有服务器宕机的时候数据依然可用。
除了运行环境,网站的高可用还需要软件开发过程的质量保证。通
过预发布验证、自动化测试、自动化发布、灰度发布等手段,减少将故
障引入线上环境的可能,避免故障范围扩大。
衡量一个系统架构设计是否满足高可用的目标,就是假设系统中任
何一台或者多台服务器宕机时,以及出现各种不可预期的问题时,系统
整体是否依然可用。
3.3 伸缩性大型网站需要面对大量用户的高并发访问和存储海量数据,不可能
只用一台服务器就处理全部用户请求,存储全部数据。网站通过集群的
方式将多台服务器组成一个整体共同提供服务。所谓伸缩性是指通过不
断向集群中加入服务器的手段来缓解不断上升的用户并发访问压力和不
断增长的数据存储需求。
衡量架构伸缩性的主要标准就是是否可以用多台服务器构建集群,是否容易向集群中添加新的服务器。加入新的服务器后是否可以提供和
原来的服务器无差别的服务。集群中可容纳的总的服务器数量是否有限
制。
对于应用服务器集群,只要服务器上不保存数据,所有服务器都是
对等的,通过使用合适的负载均衡设备就可以向集群中不断加入服务
器。
对于缓存服务器集群,加入新的服务器可能会导致缓存路由失效,进而导致集群中大部分缓存数据都无法访问。虽然缓存的数据可以通过
数据库重新加载,但是如果应用已经严重依赖缓存,可能会导致整个网
站崩溃。需要改进缓存路由算法保证缓存数据的可访问性。
关系数据库虽然支持数据复制,主从热备等机制,但是很难做到大
规模集群的可伸缩性,因此关系数据库的集群伸缩性方案必须在数据库
之外实现,通过路由分区等手段将部署有多个数据库的服务器组成一个
集群。
至于大部分NoSQL数据库产品,由于其先天就是为海量数据而生,因此其对伸缩性的支持通常都非常好,可以做到在较少运维参与的情况
下实现集群规模的线性伸缩。3.4 扩展性
不同于其他架构要素主要关注非功能性需求,网站的扩展性架构直
接关注网站的功能需求。网站快速发展,功能不断扩展,如何设计网站
的架构使其能够快速响应需求变化,是网站可扩展架构主要的目的。
衡量网站架构扩展性好坏的主要标准就是在网站增加新的业务产品
时,是否可以实现对现有产品透明无影响,不需要任何改动或者很少改
动既有业务功能就可以上线新产品。不同产品之间是否很少耦合,一个
产品改动对其他产品无影响,其他产品和功能不需要受牵连进行改动。
网站可伸缩架构的主要手段是事件驱动架构和分布式服务。
事件驱动架构在网站通常利用消息队列实现,将用户请求和其他业
务事件构造成消息发布到消息队列,消息的处理者作为消费者从消息队
列中获取消息进行处理。通过这种方式将消息产生和消息处理分离开
来,可以透明地增加新的消息生产者任务或者新的消息消费者任务。
分布式服务则是将业务和可复用服务分离开来,通过分布式服务框
架调用。新增产品可以通过调用可复用的服务实现自身的业务逻辑,而
对现有产品没有任何影响。可复用服务升级变更的时候,也可以通过提
供多版本服务对应用实现透明升级,不需要强制应用同步变更。
大型网站为了保持市场地位,还会吸引第三方开发者,调用网站服
务,使用网站数据开发周边产品,扩展网站业务。第三方开发者使用网
站服务的主要途径是大型网站提供的开放平台接口。
3.5 安全性互联网是开放的,任何人在任何地方都可以访问网站。网站的安全
架构就是保护网站不受恶意访问和攻击,保护网站的重要数据不被窃
取。
衡量网站安全架构的标准就是针对现存和潜在的各种攻击与窃密手
段,是否有可靠的应对策略。
3.6 小结
性能、可用性、伸缩性、扩展性和安全性是网站架构最核心的几个
要素,这几个问题解决了,大型网站架构设计的大部分挑战也就克服
了。因此本书第二篇即按这五个架构要素进行组织。
本章既可以看作本书第二篇的前情提要,同时也可以当做第二篇的
总结和归纳,阅读本章过程中如果有任何困惑都不必纠结,请直接跳
过,等读完全书后可以再回头重新回顾。第2篇 架构4 瞬时响应:网站的高性能架构
什么叫高性能的网站?
两个网站性能架构设计方案:A方案和B方案,A方案在小于100个
并发用户访问时,每个请求的响应时间是1秒,当并发请求达到200的时
候,请求的响应时间将骤增到10秒。B方案不管是100个并发用户访问还
是200个并发用户访问,每个请求的响应时间都差不多是1.5秒。哪个方
案的性能好?如果老板说“我们要改善网站的性能”,他指的是什么?
同类型的两个网站,X网站服务器平均每个请求的处理时间是500
毫秒,Y网站服务器平均每个请求的处理时间是1000毫秒,为什么用户
却反映Y网站的速度快呢?
网站性能是客观的指标,可以具体体现到响应时间、吞吐量等技术
指标,同时也是主观的感受,而感受则是一种与具体参与者相关的微妙
的东西,用户的感受和工程师的感受不同,不同的用户感受也不同。
4.1 网站性能测试
性能测试是性能优化的前提和基础,也是性能优化结果的检查和度
量标准。不同视角下的网站性能有不同的标准,也有不同的优化手段。
4.1.1 不同视角下的网站性能
软件工程师说到网站性能的时候,通常和用户说的不一样。
1.用户视角的网站性能从用户角度,网站性能就是用户在浏览器上直观感受到的网站响应
速度快还是慢。用户感受到的时间,包括用户计算机和网站服务器通信
的时间、网站服务器处理的时间、用户计算机浏览器构造请求解析响应
数据的时间,如图4.1所示。
图4.1 用户视角的网站性能
不同计算机的性能差异,不同浏览器解析HTML速度的差异,不同
网络运营商提供的互联网宽带服务的差异,这些差异最终导致用户感受
到的响应延迟可能会远远大于网站服务器处理请求需要的时间。
在实践中,使用一些前端架构优化手段,通过优化页面HTML式
样、利用浏览器端的并发和异步特性、调整浏览器缓存策略、使用CDN
服务、反向代理等手段,使浏览器尽快地显示用户感兴趣的内容、尽可
能近地获取页面内容,即使不优化应用程序和架构,也可以很大程度地
改善用户视角下的网站性能。
2.开发人员视角的网站性能
开发人员关注的主要是应用程序本身及其相关子系统的性能,包括
响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标。主要
的优化手段有使用缓存加速数据读取,使用集群提高吞吐能力,使用异
步消息加快请求响应及实现削峰,使用代码优化手段改善程序性能。
3.运维人员视角的网站性能运维人员更关注基础设施性能和资源利用率,如网络运营商的带宽
能力、服务器硬件的配置、数据中心网络架构、服务器和网络带宽的资
源利用率等。主要优化手段有建设优化骨干网、使用高性价比定制服务
器、利用虚拟化技术优化资源利用等。
4.1.2 性能测试指标
不同视角下有不同的性能标准,不同的标准有不同的性能测试指
标,从开发和测试人员的视角,网站性能测试的主要指标有响应时间、并发数、吞吐量、性能计数器等。
1.响应时间
指应用执行一个操作需要的时间,包括从发出请求开始到收到最后
响应数据所需要的时间。响应时间是系统最重要的性能指标,直观地反
映了系统的“快慢”。表4.1列出了一些常用的系统操作需要的响应时间。
表4.1 常用系统操作响应时间表
(部分数据来源:http:www.eecs.berkeley.edu~rcsresearchinteractive_latency.html)
测试程序通过模拟应用程序,记录收到响应和发出请求之间的时间差来计算系统响应时间。但是记录及获取系统时间这个操作也需要花费
一定的时间,如果测试目标操作本身需要花费的时间极少,比如几微
秒,那么测试程序就无法测试得到系统的响应时间。实践中通常采用的
办法是重复请求,比如一个请求操作重复执行一万次,测试一万次执行
需要的总响应时间之和,然后除以一万,得到单次请求的响应时间。
2.并发数
指系统能够同时处理请求的数目,这个数字也反映了系统的负载特
性。对于网站而言,并发数即网站并发用户数,指同时提交请求的用户
数目。
与网站并发用户数相对应的还有网站在线用户数(当前登录网站的
用户总数)和网站系统用户数(可能访问系统的总用户数,对多数网站
而言就是注册用户数)。其数量比较关系为:
网站系统用户数>>网站在线用户数>>网站并发用户数
在网站产品设计初期,产品经理和运营人员就需要规划不同发展阶
段的网站系统用户数,并以此为基础,根据产品特性和运营手段,推算
在线用户数和并发用户数。这些指标将成为系统非功能设计的重要依
据。
现实中,经常看到某些网站,特别是电商类网站,市场推广人员兴
致勃勃地打广告打折促销,用户兴致勃勃地去抢购,结果活动刚一开
始,就因为并发用户数超过网站最大负载而响应缓慢,急性子的用户不
停刷新浏览器,导致系统并发数更高,最后以服务器系统崩溃,用户浏
览器显示“Service is too busy”而告终。出现这种情况,有可能是网站技术准备不充分导致,也有可能是运营人员错误地评估并发用户数导致。
测试程序通过多线程模拟并发用户的办法来测试系统的并发处理能
力,为了真实模拟用户行为,测试程序并不是启动多线程然后不停地发
送请求,而是在两次请求之间加入一个随机等待时间,这个时间被称作
思考时间。
3.吞吐量
指单位时间内系统处理的请求数量,体现系统的整体处理能力。对
于网站,可以用“请求数秒”或是“页面数秒”来衡量,也可以用“访问人
数天”或是“处理的业务数小时”等来衡量。TPS(每秒事务数)是吞吐
量的一个常用量化指标,此外还有HPS(每秒HTTP请求数)、QPS(每
秒查询数)等。
在系统并发数由小逐渐增大的过程中(这个过程也伴随着服务器系
统资源消耗逐渐增大),系统吞吐量先是逐渐增加,达到一个极限后,随着并发数的增加反而下降,达到系统崩溃点后,系统资源耗尽,吞吐
量为零。
而这个过程中,响应时间则是先保持小幅上升,到达吞吐量极限
后,快速上升,到达系统崩溃点后,系统失去响应。系统吞吐量、系统
并发数及响应时间之间的关系将在本章后面内容中介绍。
系统吞吐量和系统并发数,以及响应时间的关系可以形象地理解为
高速公路的通行状况:吞吐量是每天通过收费站的车辆数目(可以换算
成收费站收取的高速费),并发数是高速公路上的正在行驶的车辆数
目,响应时间是车速。车辆很少时,车速很快,但是收到的高速费也相应较少;随着高速公路上车辆数目的增多,车速略受影响,但是收到的
高速费增加很快;随着车辆的继续增加,车速变得越来越慢,高速公路
越来越堵,收费不增反降;如果车流量继续增加,超过某个极限后,任
何偶然因素都会导致高速全部瘫痪,车走不动,费当然也收不着,而高
速公路成了停车场(资源耗尽)。
网站性能优化的目的,除了改善用户体验的响应时间,还要尽量提
高系统吞吐量,最大限度利用服务器资源。
4.性能计数器
它是描述服务器或操作系统性能的一些数据指标。包括System
Load、对象与线程数、内存使用、CPU使用、磁盘与网络IO等指标。
这些指标也是系统监控的重要参数,对这些指标设置报警阈值,当监控
系统发现性能计数器超过阈值时,就向运维和开发人员报警,及时发现
处理系统异常。
System Load即系统负载,指当前正在被CPU执行和等待被CPU执行
的进程数目总和,是反映系统忙闲程度的重要指标。多核CPU的情况
下,完美情况是所有CPU都在使用,没有进程在等待处理,所以Load的
理想值是CPU的数目。当Load值低于CPU数目的时候,表示CPU有空
闲,资源存在浪费;当Load值高于CPU数目的时候,表示进程在排队等
待CPU调度,表示系统资源不足,影响应用程序的执行性能。在Linux
系统中使用top命令查看,该值是三个浮点数,表示最近1分钟,10分
钟,15分钟的运行队列平均进程数。如图4.2所示。
图4.2 在Linux命令行查看系统负载4.1.3 性能测试方法
性能测试是一个总称,具体可细分为性能测试、负载测试、压力测
试、稳定性测试。
性能测试
以系统设计初期规划的性能指标为预期目标,对系统不断施加压
力,验证系统在资源可接受范围内,是否能达到性能预期。
负载测试
对系统不断地增加并发请求以增加系统压力,直到系统的某项或多
项性能指标达到安全临界值,如某种资源已经呈饱和状态,这时继续对
系统施加压力,系统的处理能力不但不能提高,反而会下降。
压力测试
超过安全负载的情况下,对系统继续施加压力,直到系统崩溃或不
能再处理任何请求,以此获得系统最大压力承受能力。
稳定性测试
被测试系统在特定硬件、软件、网络环境条件下,给系统加载一定
业务压力,使系统运行一段较长时间,以此检测系统是否稳定。在不同
生产环境、不同时间点的请求压力是不均匀的,呈波浪特性,因此为了
更好地模拟生产环境,稳定性测试也应不均匀地对系统施加压力。
性能测试是一个不断对系统增加访问压力,以获得系统性能指标、最大负载能力、最大压力承受能力的过程。所谓的增加访问压力,在系统测试环境中,就是不断增加测试程序的并发请求数,一般说来,性能
测试遵循如图4.3所示的抛物线规律。
图4.3 性能测试曲线
图4.3中的横坐标表示消耗的系统资源,纵坐标表示系统处理能力
(吞吐量)。在开始阶段,随着并发请求数目的增加,系统使用较少的
资源就达到较好的处理能力(a~b段),这一段是网站的日常运行区间
,网站的绝大部分访问负载压力都集中在这一段区间,被称作性能测
试,测试目标是评估系统性能是否符合需求及设计目标;随着压力的持
续增加,系统处理能力增加变缓,直到达到一个最大值(c点),这是
系统的最大负载点 ,这一段被称作负载测试。测试目标是评估当系统
因为突发事件超出日常访问压力的情况下,保证系统正常运行情况下能
够承受的最大访问负载压力;超过这个点后,再增加压力,系统的处理
能力反而下降,而资源消耗却更多,直到资源消耗达到极限(d点),这个点可以看作是系统的崩溃点 ,超过这个点继续加大并发请求数
目,系统不能再处理任何请求,这一段被称作压力测试,测试目标是评
估可能导致系统崩溃的最大访问负载压力。
性能测试反应的是系统在实际生产环境中使用时,随着用户并发访
问数量的增加,系统的处理能力。与性能曲线相对应的是用户访问的等待时间(系统响应时间),如图4.4所示。
图4.4 并发用户访问响应时间曲线
在日常运行区间,可以获得最好的用户响应时间,随着并发用户数
的增加,响应延迟越来越大,直到系统崩溃,用户失去响应。
4.1.4 性能测试报告
测试结果报告应能够反映上述性能测试曲线的规律,阅读者可以得
到系统性能是否满足设计目标和业务要求、系统最大负载能力、系统最
大压力承受能力等重要信息,表4.2是一个简单示例。
表4.2 性能测试结果报告
4.1.5 性能优化策略如果性能测试结果不能满足设计或业务需求,那么就需要寻找系统
瓶颈,分而治之,逐步优化。
1.性能分析
大型网站结构复杂,用户从浏览器发出请求直到数据库完成操作事
务,中间需要经过很多环节,如果测试或者用户报告网站响应缓慢,存
在性能问题,必须对请求经历的各个环节进行分析,排查可能出现性能
瓶颈的地方,定位问题。
排查一个网站的性能瓶颈和排查一个程序的性能瓶颈的手法基本相
同:检查请求处理的各个环节的日志,分析哪个环节响应时间不合理、超过预期;然后检查监控数据,分析影响性能的主要因素是内存、磁
盘、网络、还是CPU,是代码问题还是架构设计不合理,或者系统资源
确实不足。
2.性能优化
定位产生性能问题的具体原因后,就需要进行性能优化,根据网站
分层架构,可分为Web前端性能优化、应用服务器性能优化、存储服务
器性能优化3大类。
4.2 Web前端性能优化
一般说来Web前端指网站业务逻辑之前的部分,包括浏览器加载、网站视图模型、图片服务、CDN服务等,主要优化手段有优化浏览器访
问、使用反向代理、CDN等。4.2.1 浏览器访问优化
1.减少http请求
HTTP协议是无状态的应用层协议,意味着每次HTTP请求都需要建
立通信链路、进行数据传输,而在服务器端,每个HTTP都需要启动独
立的线程去处理。这些通信和服务的开销都很昂贵,减少HTTP请求的
数目可有效提高访问性能。
减少HTTP的主要手段是合并CSS、合并JavaScript、合并图片。将
浏览器一次访问需要的JavaScript、CSS合并成一个文件,这样浏览器就
只需要一次请求。图片也可以合并,多张图片合并成一张,如果每张图
片都有不同的超链接,可通过CSS偏移响应鼠标点击操作,构造不同的
URL。
2.使用浏览器缓存
对一个网站而言,CSS、JavaScript、Logo、图标这些静态资源文件
更新的频率都比较低,而这些文件又几乎是每次HTTP请求都需要的,如果将这些文件缓存在浏览器中,可以极好地改善性能。通过设置
HTTP头中Cache-Control和Expires的属性,可设定浏览器缓存,缓存时
间可以是数天,甚至是几个月。
在某些时候,静态资源文件变化需要及时应用到客户端浏览器,这
种情况,可通过改变文件名实现,即更新JavaScript文件并不是更新
JavaScript文件内容,而是生成一个新的JS文件并更新HTML文件中的引
用。使用浏览器缓存策略的网站在更新静态资源时,应采用批量更新的
方法,比如需要更新10个图标文件,不宜把10个文件一次全部更新,而
是应一个文件一个文件逐步更新,并有一定的间隔时间,以免用户浏览
器突然大量缓存失效,集中更新缓存,造成服务器负载骤增、网络堵塞
的情况。
3.启用压缩
在服务器端对文件进行压缩,在浏览器端对文件解压缩,可有效减
少通信传输的数据量。文本文件的压缩效率可达80%以上,因此
HTML、CSS、JavaScript文件启用GZip压缩可达到较好的效果。但是压
缩对服务器和浏览器产生一定的压力,在通信带宽良好,而服务器资源
不足的情况下要权衡考虑。
4.CSS放在页面最上面、JavaScript放在页面最下面
浏览器会在下载完全部CSS之后才对整个页面进行渲染,因此最好
的做法是将CSS放在页面最上面,让浏览器尽快下载CSS。JavaScript则
相反,浏览器在加载JavaScript后立即执行,有可能会阻塞整个页面,造
成页面显示缓慢,因此JavaScript最好放在页面最下面。但如果页面解析
时就需要用到JavaScript,这时放在底部就不合适了。
5.减少Cookie传输
一方面,Cookie包含在每次请求和响应中,太大的Cookie会严重影
响数据传输,因此哪些数据需要写入Cookie需要慎重考虑,尽量减少
Cookie中传输的数据量。另一方面,对于某些静态资源的访问,如
CSS、Script等,发送Cookie没有意义,可以考虑静态资源使用独立域名访问,避免请求静态资源时发送Cookie,减少Cookie传输的次数。
4.2.2 CDN加速
CDN(Content Distribute Network,内容分发网络)的本质仍然是
一个缓存,而且将数据缓存在离用户最近的地方,使用户以最快速度获
取数据,即所谓网络访问第一跳,如图4.5所示。
图4.5 利用CDN的网站架构
由于CDN部署在网络运营商的机房,这些运营商又是终端用户的网
络服务提供商,因此用户请求路由的第一跳就到达了CDN服务器,当
CDN中存在浏览器请求的资源时,从CDN直接返回给浏览器,最短路
径返回响应,加快用户访问速度,减少数据中心负载压力。
CDN能够缓存的一般是静态资源,如图片、文件、CSS、Script脚本、静态网页等,但是这些文件访问频度很高,将其缓存在CDN可极大
改善网页的打开速度。
4.2.3 反向代理
传统代理服务器位于浏览器一侧,代理浏览器将HTTP请求发送到
互联网上,而反向代理服务器位于网站机房一侧,代理网站Web服务器
接收HTTP请求。如图4.6所示。
图4.6 利用反向代理的网站架构
和传统代理服务器可以保护浏览器安全一样,反向代理服务器也具
有保护网站安全的作用,来自互联网的访问请求必须经过代理服务器,相当于在Web服务器和可能的网络攻击之间建立了一个屏障。
除了安全功能,代理服务器也可以通过配置缓存功能加速Web请
求。当用户第一次访问静态内容的时候,静态内容就被缓存在反向代理
服务器上,这样当其他用户访问该静态内容的时候,就可以直接从反向
代理服务器返回,加速Web请求响应速度,减轻Web服务器负载压力。事实上,有些网站会把动态内容也缓存在代理服务器上,比如维基百科
及某些博客论坛网站,把热门词条、帖子、博客缓存在反向代理服务器
上加速用户访问速度,当这些动态内容有变化时,通过内部通知机制通
知反向代理缓存失效,反向代理会重新加载最新的动态内容再次缓存起
来。
此外,反向代理也可以实现负载均衡的功能,而通过负载均衡构建
的应用集群可以提高系统总体处理能力,进而改善网站高并发情况下的
性能。
4.3 应用服务器性能优化
应用服务器就是处理网站业务的服务器,网站的业务代码都部署在
这里,是网站开发最复杂,变化最多的地方,优化手段主要有缓存、集
群、异步等。
4.3.1 分布式缓存
回顾网站架构演化历程,当网站遇到性能瓶颈时,第一个想到的解
决方案就是使用缓存。在整个网站应用中,缓存几乎无所不在,既存在
于浏览器,也存在于应用服务器和数据库服务器;既可以对数据缓存,也可以对文件缓存,还可以对页面片段缓存。合理使用缓存,对网站性
能优化意义重大。
网站性能优化第一定律:优先考虑使用缓存优化性能。1.缓存的基本原理
缓存指将数据存储在相对较高访问速度的存储介质中,以供系统处
理。一方面缓存访问速度快,可以减少数据访问的时间,另一方面如果
缓存的数据是经过计算处理得到的,那么被缓存的数据无需重复计算即
可直接使用,因此缓存还起到减少计算时间的作用。
缓存的本质是一个内存Hash表,网站应用中,数据缓存以一对
Key、Value的形式存储在内存Hash表中。Hash表数据读写的时间复杂度
为O(1),图4.7为一对KV在Hash表中的存储。
图4.7 Hash表存储例子
计算KV对中Key的HashCode对应的Hash表索引,可快速访问Hash
表中的数据。许多语言支持获得任意对象的HashCode,可以把
HashCode理解为对象的唯一标示符,Java语言中Hashcode方法包含在根
对象Object中,其返回值是一个Int。然后通过Hashcode计算Hash表的索
引下标,最简单的是余数法,使用Hash表数组长度对Hashcode求余,余
数即为Hash表索引,使用该索引可直接访问得到Hash表中存储的KV
对。Hash表是软件开发中常用到的一种数据结构,其设计思想在很多场
景下都可以应用。
缓存主要用来存放那些读写比很高、很少变化的数据,如商品的类目信息,热门词的搜索列表信息,热门商品信息等。应用程序读取数据
时,先到缓存中读取,如果读取不到或数据已失效,再访问数据库,并
将数据写入缓存,如图4.8所示。
图4.8 使用缓存存取数据
网站数据访问通常遵循二八定律,即80%的访问落在20%的数据
上,因此利用Hash表和内存的高速访问特性,将这20%的数据缓存起
来,可很好地改善系统性能,提高数据读取速度,降低存储访问压力。
2.合理使用缓存
使用缓存对提高系统性能有很多好处,但是不合理使用缓存非但不
能提高系统的性能,还会成为系统的累赘,甚至风险。实践中,缓存滥
用的情景屡见不鲜——过分依赖低可用的缓存系统、不恰当地使用缓存
的数据访问特性等。
频繁修改的数据
如果缓存中保存的是频繁修改的数据,就会出现数据写入缓存后,应用还来不及读取缓存,数据就已失效的情形,徒增系统负担。一般说来,数据的读写比在2:1以上,即写入一次缓存,在数据更新前至少读
取两次,缓存才有意义。实践中,这个读写比通常非常高,比如新浪微
博的热门微博,缓存以后可能会被读取数百万次。
没有热点的访问
缓存使用内存作为存储,内存资源宝贵而有限,不可能将所有数据
都缓存起来,只能将最新访问的数据缓存起来,而将历史数据清理出缓
存。如果应用系统访问数据没有热点,不遵循二八定律,即大部分数据
访问并没有集中在小部分数据上,那么缓存就没有意义,因为大部分数
据还没有被再次访问就已经被挤出缓存了。
数据不一致与脏读
一般会对缓存的数据设置失效时间,一旦超过失效时间,就要从数
据库中重新加载。因此应用要容忍一定时间的数据不一致,如卖家已经
编辑了商品属性,但是需要过一段时间才能被买家看到。在互联网应用
中,这种延迟通常是可以接受的,但是具体应用仍需慎重对待。还有一
种策略是数据更新时立即更新缓存,不过这也会带来更多系统开销和事
务一致性的问题。
缓存可用性
缓存是为提高数据读取性能的,缓存数据丢失或者缓存不可用不会
影响到应用程序的处理——它可以从数据库直接获取数据。但是随着业
务的发展,缓存会承担大部分数据访问的压力,数据库已经习惯了有缓
存的日子,所以当缓存服务崩溃时,数据库会因为完全不能承受如此大
的压力而宕机,进而导致整个网站不可用。这种情况被称作缓存雪崩,发生这种故障,甚至不能简单地重启缓存服务器和数据库服务器来恢复网站访问。
实践中,有的网站通过缓存热备等手段提高缓存可用性:当某台缓
存服务器宕机时,将缓存访问切换到热备服务器上。但是这种设计显然
有违缓存的初衷,缓存根本就不应该被当做一个可靠的数据源来使用。
通过分布式缓存服务器集群,将缓存数据分布到集群多台服务器上
可在一定程度上改善缓存的可用性。当一台缓存服务器宕机的时候,只
有部分缓存数据丢失,重新从数据库加载这部分数据不会对数据库产生
很大影响。
产品在设计之初就需要一个明确的定位:什么是产品要
实现的功能,什么不是产品提供的特性。在产品漫长的生命
周期中,会有形形色色的困难和诱惑来改变产品的发展方
向,左右摇摆、什么都想做的产品,最后有可能成为一个失
去生命力的四不像。
缓存预热
缓存中存放的是热点数据,热点数据又是缓存系统利用LRU(最近
最久未用算法)对不断访问的数据筛选淘汰出来的,这个过程需要花费
较长的时间。新启动的缓存系统如果没有任何数据,在重建缓存数据的
过程中,系统的性能和数据库负载都不太好,那么最好在缓存系统启动
时就把热点数据加载好,这个缓存预加载手段叫作缓存预热(warm
up)。对于一些元数据如城市地名列表、类目信息,可以在启动时加载
数据库中全部数据到缓存进行预热。缓存穿透
如果因为不恰当的业务、或者恶意攻击持续高并发地请求某个不存
在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成很大压力,甚至崩溃。一个简单的对策是将不存在的数
据也缓存起来(其value值为null)。
3.分布式缓存架构
分布式缓存指缓存部署在多个服务器组成的集群中,以集群方式提
供缓存服务,其架构方式有两种,一种是以JBoss Cache为代表的需要更
新同步的分布式缓存,一种是以Memcached为代表的不互相通信的分布
式缓存。
JBoss Cache的分布式缓存在集群中所有服务器中保存相同的缓存数
据,当某台服务器有缓存数据更新的时候,会通知集群中其他机器更新
缓存数据或清除缓存数据,如图4.9所示。JBoss Cache通常将应用程序
和缓存部署在同一台服务器上,应用程序可从本地快速获取缓存数据,但是这种方式带来的问题是缓存数据的数量受限于单一服务器的内存空
间,而且当集群规模较大的时候,缓存更新信息需要同步到集群所有机
器,其代价惊人。因而这种方案更多见于企业应用系统中,而很少在大
型网站使用。图4.9 需要更新同步的JBoss Cache
大型网站需要缓存的数据量一般都很庞大,可能会需要数TB的内
存做缓存,这时候就需要另一种分布式缓存,如图4.10所示。
Memcached采用一种集中式的缓存集群管理,也被称作互不通信的分布
式架构方式。缓存与应用分离部署,缓存系统部署在一组专门的服务器
上,应用程序通过一致性Hash等路由算法选择缓存服务器远程访问缓存
数据,缓存服务器之间不通信,缓存集群的规模可以很容易地实现扩
容,具有良好的可伸缩性。
图4.10 不互相通信的Memcached
Memcached的伸缩性设计参考本书第6章内容。
4.Memcached
Memcached曾一度是网站分布式缓存的代名词,被大量网站使用。其简单的设计、优异的性能、互不通信的服务器集群、海量数据可伸缩
的架构令网站架构师们趋之若鹜。
简单的通信协议
远程通信设计需要考虑两方面的要素,一是通信协议,即选择TCP
协议还是UDP协议,抑或HTTP协议;一是通信序列化协议,数据传输
的两端,必须使用彼此可识别的数据序列化方式才能使通信得以完成,如XML、JSON等文本序列化协议,或者Google Protobuffer等二进制序
列化协议。Memcached使用TCP协议(UDP也支持)通信,其序列化协
议则是一套基于文本的自定义协议,非常简单,以一个命令关键字开
头,后面是一组命令操作数。例如读取一个数据的命令协议是get
。Memcached以后,许多NoSQL产品都借鉴了或直接支持这套协
议。
丰富的客户端程序
Memcached通信协议非常简单,只要支持该协议的客户端都可以和
Memcached服务器通信,因此Memcached发展出非常丰富的客户端程
序,几乎支持所有主流的网站编程语言,Java、CC++C、Perl、Python、PHP、Ruby等,因此在混合使用多种编程语言的网站,Memcached更是如鱼得水。
高性能的网络通信
Memcached服务端通信模块基于Libevent,一个支持事件触发的网
络通信程序库。Libevent的设计和实现有许多值得改善的地方,但它在
稳定的长连接方面的表现却正是Memcached需要的。高效的内存管理
内存管理中一个令人头痛的问题就是内存碎片管理。操作系统、虚
拟机垃圾回收在这方面想了许多办法:压缩、复制等。Memcached使用
了一个非常简单的办法——固定空间分配。Memcached将内存空间分为
一组slab,每个slab里又包含一组chunk,同一个slab里的每个chunk的大
小是固定的,拥有相同大小chunk的slab被组织在一起,叫作slab_class,如图4.11所示。存储数据时根据数据的Size大小,寻找一个大于Size的最
小chunk将数据写入。这种内存管理方式避免了内存碎片管理的问题,内存的分配和释放都是以chunk为单位的。和其他缓存一样,Memcached采用LRU算法释放最近最久未被访问的数据占用的空间,释
放的chunk被标记为未用,等待下一个合适大小数据的写入。
图4.11 Memcached内存管理
当然这种方式也会带来内存浪费的问题。数据只能存入一个比它大
的chunk里,而一个chunk只能存一个数据,其他空间被浪费了。如果启
动参数配置不合理,浪费会更加惊人,发现没有缓存多少数据,内存空
间就用尽了。互不通信的服务器集群架构
如上所述,正是这个特性使得Memcached从JBoss Cache、OSCache
等众多分布式缓存产品中脱颖而出,满足网站对海量缓存数据的需求。
而其客户端路由算法一致性Hash更成为数据存储伸缩性架构设计的经典
范式(参考本书第6章)。事实上,正是集群内服务器互不通信使得集
群可以做到几乎无限制的线性伸缩,这也正是目前流行的许多大数据技
术的基本架构特点。
虽然近些年许多NoSQL产品层出不穷,在数据持久化、支持复杂数
据结构、甚至性能方面有许多产品优于Memcached,但Memcached由于
其简单、稳定、专注的特点,仍然在分布式缓存领域占据着重要地位。
4.3.2 异步操作
使用消息队列将调用异步化,可改善网站的扩展性(参考本书第7
章内容)。事实上,使用消息队列还可改善网站系统的性能,如图4.12
和图4.13所示。
图4.12 不使用消息队列服务器
图4.13 使用消息队列服务器
在不使用消息队列的情况下,用户的请求数据直接写入数据库,在高并发的情况下,会对数据库造成巨大的压力,同时也使得响应延迟加
剧。在使用消息队列后,用户请求的数据发送给消息队列后立即返回,再由消息队列的消费者进程(通常情况下,该进程通常独立部署在专门
的服务器集群上)从消息队列中获取数据,异步写入数据库。由于消息
队列服务器处理速度远快于数据库(消息队列服务器也比数据库具有更
好的伸缩性),因此用户的响应延迟可得到有效改善。
消息队列具有很好的削峰作用——即通过异步处理,将短时间高并
发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。在
电子商务网站促销活动中,合理使用消息队列,可有效抵御促销活动刚
开始大量涌入的订单对系统造成的冲击。如图4.14所示。
图4.14 使用消息队列消除并发访问高峰
需要注意的是,由于数据写入消息队列后立即返回给用户,数据在
后续的业务校验、写数据库等操作可能失败,因此在使用消息队列进行
业务异步处理后,需要适当修改业务流程进行配合,如订单提交后,订
单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队
列的订单消费者进程真正处理完该订单,甚至商品出库后,再通过电子
邮件或SMS消息通知用户订单成功,以免交易纠纷。任何可以晚点做的事情都应该晚点再做。
4.3.3 使用集群
在网站高并发访问的场景下,使用负载均衡技术为一个应用构建一
个由多台服务器组成的服务器集群,将并发访问请求分发到多台服务器
上处理,避免单一服务器因负载压力过大而响应缓慢,使用户请求具有
更好的响应延迟特性,如图4.15所示。
图4.15 利用负载均衡技术改善性能
三台Web服务器共同处理来自用户浏览器的访问请求,这样每台
Web服务器需要处理的http请求只有总并发请求数的三分之一,根据性
能测试曲线,使服务器的并发请求数目控制在最佳运行区间,获得最佳
的访问请求延迟。
4.3.4 代码优化网站的业务逻辑实现代码主要部署在应用服务器上,需要处理复杂
的并发事务。合理优化业务代码,可以很好地改善网站性能。不同编程
语言的代码优化手段有很多,这里我们概要地关注比较重要的几个方
面。
1.多线程
多用户并发访问是网站的基本需求,大型网站的并发用户数会达到
数万,单台服务器的并发用户也会达到数百。CGI编程时代,每个用户
请求都会创建一个独立的系统进程去处理。由于线程比进程更轻量,更
少占有系统资源,切换代价更小,所以目前主要的Web应用服务器都采
用多线程的方式响应并发用户请求,因此网站开发天然就是多线程编
程。
从资源利用的角度看,使用多线程的原因主要有两个:IO阻塞与多
CPU。当前线程进行IO处理的时候,会被阻塞释放CPU以等待IO操作完
成,由于IO操作(不管是磁盘IO还是网络IO)通常都需要较长的时间,这时CPU可以调度其他的线程进行处理。前面我们提到,理想的系统
Load是既没有进程(线程)等待也没有CPU空闲,利用多线程IO阻塞与
执行交替进行,可最大限度地利用CPU资源。使用多线程的另一个原因
是服务器有多个CPU,在这个连手机都有四核CPU的时代,除了最低配
置的虚拟机,一般数据中心的服务器至少16核CPU,要想最大限度地使
用这些CPU,必须启动多线程。
网站的应用程序一般都被Web服务器容器管理,用户请求的多线程
也通常被Web服务器容器管理,但不管是Web容器管理的线程,还是应
用程序自己创建的线程,一台服务器上启动多少线程合适呢?假设服务
器上执行的都是相同类型任务,针对该类任务启动的线程数有个简化的估算公式可供参考:
启动线程数=[任务执行时间(任务执行时间(IO等待时
间)]]CPU内核数
最佳启动线程数和CPU内核数量成正比,和IO阻塞时间成反比。如
果任务都是CPU计算型任务,那么线程数最多不超过CPU内核数,因为
启动再多线程,CPU也来不及调度;相反如果是任务需要等待磁盘操
作,网络响应,那么多启动线程有助于提高任务并发度,提高系统吞吐
能力,改善系统性能。
多线程编程一个需要注意的问题是线程安全问题,即多线程并发对
某个资源进行修改,导致数据混乱。这也是缺乏经验的网站工程师最容
易犯错的地方,而线程安全Bug又难以测试和重现,网站故障中,许多
所谓偶然发生的“灵异事件”都和多线程并发问题有关。对网站而言,不
管有没有进行多线程编程,工程师写的每一行代码都会被多线程执行,因为用户请求是并发提交的,也就是说,所有的资源——对象、内存、文件、数据库,乃至另一个线程都可能被多线程并发访问。
编程上,解决线程安全的主要手段有如下几点。
将对象设计为无状态对象: 所谓无状态对象是指对象本身不存储
状态信息(对象无成员变量,或者成员变量也是无状态对象),这样多
线程并发访问的时候就不会出现状态不一致,Java Web开发中常用的
Servlet对象就设计为无状态对象,可以被应用服务器多线程并发调用处
理用户请求。而Web开发中常用的贫血模型对象都是些无状态对象。不
过从面向对象设计的角度看,无状态对象是一种不良设计。
使用局部对象: 即在方法内部创建对象,这些对象会被每个进入该方法的线程创建,除非程序有意识地将这些对象传递给其他线程,否
则不会出现对象被多线程并发访问的情形。
并发访问资源时使用锁: 即多线程访问资源的时候,通过锁的方
式使多线程并发操作转化为顺序操作,从而避免资源被并发修改。随着
操作系统和编程语言的进步,出现各种轻量级锁,使得运行期线程获取
锁和释放锁的代价都变得更小,但是锁导致线程同步顺序执行,可能会
对系统性能产生严重影响。
2.资源复用
系统运行时,要尽量减少那些开销很大的系统资源的创建和销毁,比如数据库连接、网络通信连接、线程、复杂对象等。从编程角度,资
源复用主要有两种模式:单例(Singleton)和对象池(Object Pool)。
单例虽然是GoF经典设计模式中较多被诟病的一个模式,但由于目
前Web开发中主要使用贫血模式,从Service到Dao都是些无状态对象,无需重复创建,使用单例模式也就自然而然了。事实上,Java开发常用
的对象容器Spring默认构造的对象都是单例(需要注意的是Spring的单
例是Spring容器管理的单例,而不是用单例模式构造的单例)。
对象池模式通过复用对象实例,减少对象创建和资源消耗。对于数
据库连接对象,每次创建连接,数据库服务端都需要创建专门的资源以
应对,因此频繁创建关闭数据库连接,对数据库服务器而言是灾难性
的,同时频繁创建关闭连接也需要花费较长的时间。因此在实践中,应
用程序的数据库连接基本都使用连接池(Connection Pool)的方式。数
据库连接对象创建好以后,将连接对象放入对象池容器中,应用程序要
连接的时候,就从对象池中获取一个空闲的连接使用,使用完毕再将该对象归还到对象池中即可,不需要创建新的连接。
前面说过,对于每个Web请求(HTTP Request),Web应用服务器
都需要创建一个独立的线程去处理,这方面,应用服务器也采用线程池
(Thread Pool)的方式。这些所谓的连接池、线程池,本质上都是对象
池,即连接、线程都是对象,池管理方式也基本相同。
3.数据结构
早期关于程序的一个定义是,程序就是数据结构早算法,数据结构
对于编程的重要性不言而喻。在不同场景中合理使用恰当的数据结构,灵活组合各种数据结构改善数据读写和计算特性可极大优化程序的性
能。
前面缓存部分已经描述过Hash表的基本原理,Hash表的读写性能在
很大程度上依赖HashCode的随机性,即HashCode越随机散列,Hash表
的冲突就越少,读写性能也就越高,目前比较好的字符串Hash散列算法
有Time33算法,即对字符串逐字符迭代乘以33,求得Hash值,算法原型
为:
hash(i) = hash(i 1) 33 + str[i]
Time33虽然可以较好地解决冲突,但是有可能相似字符串的
HashCode也比较接近,如字符串“AA”的HashCode是2210,字符
串“AB”的HashCode是2211。这在某些应用场景是不能接受的,这种情
况下,一个可行的方案是对字符串取信息指纹,再对信息指纹求
HashCode,由于字符串微小的变化就可以引起信息指纹的巨大不同,因
此可以获得较好的随机散列,如图4.16所示。图4.16 通过MD5计算HashCode
4.垃圾回收
如果Web应用运行在JVM等具有垃圾回收功能的环境中,那么垃圾
回收可能会对系统的性能特性产生巨大影响。理解垃圾回收机制有助于
程序优化和参数调优,以及编写内存安全的代码。
以JVM为例,其内存主要可划分为堆(heap)和堆栈(stack)。堆
栈用于存储线程上下文信息,如方法参数、局部变量等。堆则是存储对
象的内存空间,对象的创建和释放、垃圾回收就在这里进行。通过对对
象生命周期的观察,发现大部分对象的生命周期都极其短暂,这部分对
象产生的垃圾应该被更快地收集,以释放内存,这就是JVM分代垃圾回
收,其基本原理如图4.17所示。
图4.17 JVM分代垃圾回收机制
在JVM分代垃圾回收机制中,将应用程序可用的堆空间分为年轻代
(Young Generation)和年老代(Old Generation),又将年轻代分为
Eden区(Eden Space)、From区和To区,新建对象总是在Eden区中被创
建,当Eden区空间已满,就触发一次Young GC(Garbage Collection,垃圾回收),将还被使用的对象复制到From区,这样整个Eden区都是
未被使用的空间,可供继续创建对象,当Eden区再次用完,再触发一次
Young GC,将Eden区和From区还在被使用的对象复制到To区,下一次Young GC则是将Eden区和To区还被使用的对象复制到From区。因此,经过多次Young GC,某些对象会在From区和To区多次复制,如果超过
某个阈值对象还未被释放,则将该对象复制到Old Generation。如果Old
Generation空间也已用完,那么就会触发Full GC,即所谓的全量回收,全量回收会对系统性能产生较大影响,因此应根据系统业务特点和对象
生命周期,合理设置Young Generation和Old Generation大小,尽量减少
Full GC。事实上,某些Web应用在整个运行期间可以做到从不进行Full
GC。
4.4 存储性能优化
在网站应用中,海量的数据读写对磁盘访问造成巨大压力,虽然可
以通过Cache解决一部分数据读压力,但是很多时候,磁盘仍然是系统
最严重的瓶颈。而且磁盘中存储的数据是网站最重要的资产,磁盘的可
用性和容错性也至关重要。
4.4.1 机械硬盘vs. 固态硬盘
机械硬盘是目前最常用的一种硬盘,通过马达驱动磁头臂,带动磁
头到指定的磁盘位置访问数据,由于每次访问数据都需要移动磁头臂,因此机械硬盘在数据连续访问(要访问的数据存储在连续的磁盘空间
上)和随机访问(要访问的数据存储在不连续的磁盘空间)时,由于移
动磁头臂的次数相差巨大,性能表现差别也非常大。机械硬盘结构如图
4.18所示。图4.18 机械硬盘结构图(图片来自互联网)
固态硬盘又称作SSD或Flash硬盘,这种硬盘没有机械装置,数据存
储在可持久记忆的硅晶体上,因此可以像内存一样快速随机访问。而且
SSD具有更小的功耗和更少的磁盘震动与噪声。SSD硬盘如图4.19所
示。
图4.19 SSD硬盘(图片来自互联网)
在网站应用中,大部分应用访问数据都是随机的,这种情况下SSD
具有更好的性能表现。但是目前SSD硬盘还不太成熟,可靠性、性价比
有待提升,因此SSD的使用还在摸索阶段。但是相信随着SSD工艺水平
的提高,逐步替代传统机械硬盘是迟早的事。4.4.2 B+树 vs. LSM树
本书前面提到,由于传统的机械磁盘具有快速顺序读写、慢速随机
读写的访问特性,这个特性对磁盘存储结构和算法的选择影响甚大。
为了改善数据访问特性,文件系统或数据库系统通常会对数据排序
后存储,加快数据检索速度,这就需要保证数据在不断更新、插入、删
除后依然有序,传统关系数据库的做法是使用B+树,如图4.20所示。
图4.20 B+树原理示意图
B+树是一种专门针对磁盘存储而优化的N叉排序树,以树节点为
单位存储在磁盘中,从根开始查找所需数据所在的节点编号和磁盘位
置,将其加载到内存中然后继续查找,直到找到所需的数据。
目前数据库多采用两级索引的B+树,树的层次最多三层。因此可
能需要5次磁盘访问才能更新一条记录(三次磁盘访问获得数据索引及
行ID,然后再进行一次数据文件读操作及一次数据文件写操作)。
但是由于每次磁盘访问都是随机的,而传统机械硬盘在数据随机访
问时性能较差,每次数据访问都需要多次访问磁盘影响数据访问性能。目前许多NoSQL产品采用LSM树作为主要数据结构,如图4.21所
示。
图4.21 LSM树原理示意图(图片来源互联网)
LSM树可以看作是一个N阶合并树。数据写操作(包括插入、修
改、删除)都在内存中进行,并且都会创建一个新记录(修改会记录新
的数据值,而删除会记录一个删除标志),这些数据在内存中仍然还是
一棵排序树,当数据量超过设定的内存阈值后,会将这棵排序树和磁盘
上最新的排序树合并。当这棵排序树的数据量也超过设定阈值后,和磁
盘上下一级的排序树合并。合并过程中,会用最新更新的数据覆盖旧的
数据(或者记录为不同版本)。
在需要进行读操作时,总是从内存中的排序树开始搜索,如果没有
找到,就从磁盘上的排序树顺序查找。
在LSM树上进行一次数据更新不需要磁盘访问,在内存即可完成,速度远快于B+树。当数据访问以写操作为主,而读操作则集中在最近
写入的数据上时,使用LSM树可以极大程度地减少磁盘的访问次数,加
快访问速度。
作为存储结构,B+树不是关系数据库所独有的,NoSQL数据库也
可以使用B+树。同理,关系数据库也可以使用LSM,而且随着SSD硬盘的日趋成熟及大容量持久存储的内存技术的出现,相信B+树这一“古
老”的存储结构会再次焕发青春。
4.4.3 RAID vs. HDFS
RAID(廉价磁盘冗余阵列)技术主要是为了改善磁盘的访问延
迟,增强磁盘的可用性和容错能力。目前服务器级别的计算机都支持插
入多块磁盘(8块或者更多),通过使用RAID技术,实现数据在多块磁
盘上的并发读写和数据备份。
常用RAID技术有以下几种,如图4.22所示。
图4.22 常用RAID技术原理图
假设服务器有N 块磁盘。
RAID0
数据在从内存缓冲区写入磁盘时,根据磁盘数量将数据分成N 份,这些数据同时并发写入N 块磁盘,使得数据整体写入速度是一块磁盘的
N 倍。读取时也一样,因此RAID0具有极快的数据读写速度,但是
RAID0不做数据备份,N 块磁盘中只要有一块损坏,数据完整性就被破
坏,所有磁盘的数据都会损坏。
RAID1数据在写入磁盘时,将一份数据同时写入两块磁盘,这样任何一块
磁盘损坏都不会导致数据丢失,插入一块新磁盘就可以通过复制数据的
方式自动修复,具有极高的可靠性。
RAID10
结合RAID0和RAID1两种方案,将所有磁盘平均分成两份,数据同
时在两份磁盘写入,相当于RAID1,但是在每一份磁盘里面的N 2块磁
盘上,利用RAID0技术并发读写,既提高可靠性又改善性能,不过
RAID10的磁盘利用率较低,有一半的磁盘用来写备份数据。
RAID3
一般情况下,一台服务器上不会出现同时损坏两块磁盘的情况,在
只损坏一块磁盘的情况下,如果能利用其他磁盘的数据恢复损坏磁盘的
数据,这样在保证可靠性和性能的同时,磁盘利用率也得到大幅提升。
在数据写入磁盘的时候,将数据分成N -1份,并发写入N -1块磁
盘,并在第N 块磁盘记录校验数据,任何一块磁盘损坏(包括校验数据
磁盘),都可以利用其他N -1块磁盘的数据修复。
但是在数据修改较多的场景中,修改任何磁盘数据都会导致第N 块
磁盘重写校验数据,频繁写入的后果是第N 块磁盘比其他磁盘容易损
坏,需要频繁更换,所以RAID3很少在实践中使用。
RAID5
相比RAID3,方案RAID5被更多地使用。
RAID5和RAID3很相似,但是校验数据不是写入第N 块磁盘,而是螺旋式地写入所有磁盘中。这样校验数据的修改也被平均到所有磁盘
上,避免RAID3频繁写坏一块磁盘的情况。
RAID6
如果数据需要很高的可靠性,在出现同时损坏两块磁盘的情况下
(或者运维管理水平比较落后,坏了一块磁盘但是迟迟没有更换,导致
又坏了一块磁盘),仍然需要修复数据,这时候可以使用RAID6。
RAID6和RAID5类似,但是数据只写入N -2块磁盘,并螺旋式地在
两块磁盘中写入校验信息(使用不同算法生成)。
在相同磁盘数目(N )的情况下,各种RAID技术的比较如表4.3所
示。
表4.3 几种RAID技术比较
RAID技术可以通过硬件实现,比如专用的RAID卡或者主板直接支
持,也可以通过软件实现。RAID技术在传统关系数据库及文件系统中
应用比较广泛,但是在大型网站比较喜欢使用的NoSQL,以及分布式文
件系统中,RAID技术却遭到冷落。
例如在HDFS(Hadoop 分布式文件系统)中,系统在整个存储集群
的多台服务器上进行数据并发读写和备份,可以看作在服务器集群规模
上实现了类似RAID的功能,因此不需要磁盘RAID。HDFS以块(Block)为单位管理文件内容,一个文件被分割成若干
个Block,当应用程序写文件时,每写完一个Block,HDFS就将其自动
复制到另外两台机器上,保证每个Block有三个副本,即使有两台服务
器宕机,数据依然可以访问,相当于实现了RAID1的数据复制功能。
当对文件进行处理计算时,通过MapReduce并发计算任务框架,可
以启动多个计算子任务(MapReduce Task),同时读取文件的多个
Block,并发处理,相当于实现了RAID0的并发访问功能。
HDFS架构如图4.23所示。
图4.23 HDFS架构原理图
在HDFS中有两种重要的服务器角色:NameNode(名字服务节点)
和DataNode(数据存储节点)。NameNode在整个HDFS中只部署一个实
例,提供元数据服务,相当于操作系统中的文件分配表(FAT),管理
文件名Block的分配,维护整个文件系统的目录树结构。DataNode则部
署在HDFS集群中其他所有服务器上,提供真正的数据存储服务。和操作系统一样,HDFS对数据存储空间的管理以数据块(Block)
为单位,只是比操作系统中的数据块(512字节)要大得多,默认为
64MB。HDFS将DataNode上的磁盘空间分成N 个这样的块,供应用程序
使用。
应用程序(Client)需要写文件时,首先访问NameNode,请求分配
数据块,NameNode根据管理的DataNode服务器的磁盘空间,按照一定
的负载均衡策略,分配若干数据块供Client使用。
当Client写完一个数据块时,HDFS会将这个数据块再复制两份存储
在其他DataNode服务器上,HDFS默认同一份数据有三个副本,保证数
据可靠性。因此在HDFS中,即使DataNode服务器有多块磁盘,也不需
要使用RAID进行数据备份,而是在整个集群上进行数据复制,而且系
统一旦发现某台服务器宕机,会自动利用其他机器上的数据将这台服务
器上存储的数据块自动再备份一份,从而获得更高的数据可靠性。
HDFS配合MapReduce等并行计算框架进行大数据处理时,可以在
整个集群上并发读写访问所有的磁盘,无需RAID支持。
4.5 小结
网站性能优化技术是在网站性能遇到问题时的解决方案。而网站的
性能问题很多是在用户高并发访问时产生的,所以网站性能优化的主要
工作是改善高并发用户访问情况下的网站响应速度。本章开篇所举的例
子,当老板说“我们要改善网站性能”的时候,他期望的是在A方案的基
础上,不管是100个并发访问还是200个并发访问,响应时间都能达到1
秒。而架构师能做到的,则是利用分布式的方案改善网站并发特性,由
于分布式不可避免地会带来架构复杂、网络通信延迟等问题,所以最终设计出来的可能是B方案:缩短高并发访问响应延迟的同时,却延长了
原来低并发访问时的响应延迟。架构师对这种可能性要心中有数,合理
调整相关各方对性能优化的心理预期。
网站性能对最终用户而言是一种主观感受,性能优化的最终目的就
是改善用户的体验,使他们感觉网站很快。离开这个目的,追求技术上
的所谓高性能,是舍本逐末,没有多大意义。而用户体验的快或是慢,可以通过技术手段改善,也可以通过优化交互体验改善。
即使在技术层面,性能优化也需要全面考虑,综合权衡:性能提升
一倍,但服务器数量也需要增加一倍;或者响应时间缩短,同时数据一
致性也下降,这样的优化是否可以接受?这类问题的答案不是技术团队
能回答的。归根结底,技术是为业务服务的,技术选型和架构决策依赖
业务规划乃至企业战略规划,离开业务发展的支撑和驱动,技术走不
远,甚至还会迷路。
前沿技术总是出现在前沿业务领域。近几年,以Google
为首的互联网企业领跑IT前沿技术潮流,是因为互联网企业
的业务发展远超传统IT企业领域,面临更多挑战,对IT系统
提出了更高的要求。
新技术的出现又会驱动企业开展新的业务。亚马逊等互
联网公司利用自己的技术优势进军企业级市场,以技术驱动
业务,开展云计算、SaaS等新兴IT业务,逐步蚕食IBM、HP、Oracle、微软等传统软件巨头的市场。5 万无一失:网站的高可用架构
2011年4月12日,亚马逊云计算服务EC2(Elastic Computer Cloud)
发生故障,其ESB(Elastic Block Storage)服务不可用,故障持续了数
天,最终还是有部分数据未能恢复。这一故障导致美国许多使用亚马逊
云服务的知名网站(如:Foursquare,Quora)受到影响,并引发了人们
对使用云计算安全性、可靠性的大规模讨论。
2010年1月12日,百度被黑客攻击,其DNS域名被劫持,导致百度
全站长达数小时不可访问。该事件一时成为新闻焦点,各种媒体争相报
道。
网站的可用性(Availability)描述网站可有效访问的特性(不同于
另一个网站运营指标:Usability,通常也被译作可用性,但是后者强调
的是网站的有用性,即对最终用户的使用价值),相比于网站的其他非
功能特性,网站的可用性更牵动人们的神经,大型网站的不可用事故直
接影响公司形象和利益,许多互联网公司都将网站可用性列入工程师的
绩效考核,与奖金升迁等利益挂钩。
5.1 网站可用性的度量与考核
网站的页面能完整呈现在最终用户面前,需要经过很多个环节,任
何一个环节出了问题,都可能导致网站页面不可访问。DNS会被劫持、CDN服务可能会挂掉、网站服务器可能会宕机、网络交换机可能会失
效、硬盘会损坏、网卡会松掉、甚至机房会停电、空调会失灵、程序会
有Bug、黑客会攻击、促销会引来大量访问、第三方合作伙伴的服务会
不可用……要保证一个网站永远完全可用几乎是一件不可能完成的使命。
5.1.1 网站可用性度量
网站不可用也被称作网站故障,业界通常用多少个9来衡量网站的
可用性,如QQ的可用性是4个9,即QQ服务99.99%可用,这意味着QQ
服务要保证其在所有运行时间中,只有0.01%的时间不可用,也就是一
年中大约最多53分钟不可用。
网站不可用时间(故障时间)=故障修复时间点网故障发现(报
告)时间点
网站年度可用性指标=(11网站不可用时间年度总时间)年100%
对于大多数网站而言,2个9是基本可用,网站年度不可用时间小于
88小时;3个9是较高可用,网站年度不可用时间小于9小时;4个9是具
有自动恢复能力的高可用,网站年度不可用时间小于53分钟;5个9是极
高可用性,网站年度不可用时间小于5分钟。
由于可用性影响因素很多,对于网站整体而言,达到4个9,乃至5
个9的可用性,除了过硬的技术、大量的设备资金投入和工程师的责任
心,还要有个好运气。
常使用Twitter的用户或多或少遇到过那个著名的服务不可用的鲸鱼
页面,事实上,Twitter网站的可用性不足2个9。
5.1.2 网站可用性考核
可用性指标是网站架构设计的重要指标,对外是服务承诺,对内是
考核指标。从管理层面,可用性指标是网站或者产品的整体考核指标,具体到每个工程师的考核,更多的是使用故障分。
所谓故障分是指对网站故障进行分类加权计算故障责任的方法。表
5.1为某网站故障分类权重表。
表5.1 网站故障分类权重表示例
故障分的计算公式为:
故障分=故障时间(分钟)( 故障权重
在年初或者考核季度的开始,会根据网站产品的可用性指标计算总
的故障分,然后根据团队和个人的职责角色分摊故障分,这个可用性指
标和故障分是管理预期。在实际发生故障的时候,根据故障分类和责任
划分将故障产生的故障分分配给责任者承担。等年末或者考核季度末的
时候,个人及团队实际承担的故障分如果超过了年初分摊的故障分,绩
效考核就会受到影响。
一个简化的故障处理流程如图5.1所示。图5.1 网站故障处理流程示例
有时候一个故障责任可能由多个部门或团队来承担,故障分也会相
应按责任分摊到不同的团队和个人。
不同于其他架构指标,网站可用性更加看得见摸得着,跟技术、运
营、相关各方的绩效考核息息相关,因此在架构设计与评审会议上,关
于系统可用性的讨论与争执总是最花费时间与精力的部分。
当然,不同的公司有不同的企业文化和市场策略,这些因素也会影
响到系统可用性的架构决策,崇尚创新和风险的企业会对可用性要求稍
低一些;业务快速增长的网站忙于应对指数级增长的用户,也会降低可
用性的标准;服务于收费用户的网站则会比服务于免费用户的网站对可
用性更加敏感,服务不可用或关键用户数据丢失可能会导致收费用户的投诉甚至引来官司。
5.2 高可用的网站架构
通常企业级应用系统为提高系统可用性,会采用较昂贵的软硬件设
备,如IBM的小型机乃至中型机大型机及专有操作系统、Oracle数据
库、EMC存储设备等。互联网公司更多地采用PC级服务器、开源的数
据库和操作系统,这些廉价的设备在节约成本的同时也降低了可用性,特别是服务器硬件设备,低价的商业级服务器一年宕机一次是一个大概
率事件,而那些高强度频繁读写的普通硬盘,损坏的概率则要更高一
些。
既然硬件故障是常态,网站的高可用架构设计的主要目的就是保证
服务器硬件故障时服务依然可用、数据依然保存并能够被访问。
实现上述高可用架构的主要手段是数据和服务的冗余备份及失效转
移,一旦某些服务器宕机,就将服务切换到其他可用的服务器上,如果
磁盘损坏,则从备份的磁盘读取数据。
一个典型的网站设计通常遵循如图5.2所示的基本分层架构模型。
图5.2 网站架构基本分层模型
典型的分层模型是三层,即应用层、服务层、数据层;各层之间具有相对独立性,应用层主要负责具体业务逻辑处理;服务层负责提供可
复用的服务;数据层负责数据的存储与访问。中小型网站在具体部署
时,通常将应用层和服务层部署在一起,而数据层则另外部署,如图
5.3所示(事实上,这也是网站架构演化的第一步)。
图5.3 应用和数据分离部署的网站架构
在复杂的大型网站架构中,划分的粒度会更小、更详细,结构更加
复杂,服务器规模更加庞大,但通常还是能够把这些服务器划分到这三
层中。如图5.4所示。
图5.4 分层后按模块分割的网站架构模型不同的业务产品会部署在不同的服务器集群上,如某网站的文库、贴吧、百科等属于不同的产品,部署在各自独立的服务器集群上,互不
相干。这些产品又会依赖一些共同的复用业务,如注册登录服务、Session管理服务、账户管理服务等,这些可复用的业务服务也各自部署
在独立的服务器集群上。至于数据层,数据库服务、文件服务、缓存服
务、搜索服务等数据存储与访问服务都部署在各自独立的服务器集群
上。
大型网站的分层架构及物理服务器的分布式部署使得位
于不同层次的服务器具有不同的可用性特点。关闭服务或者
服务器宕机时产生的影响也不相同,高可用的解决方案也差
异甚大。
位于应用层的服务器通常为了应对高并发的访问请求,会通过负载
均衡设备将一组服务器组成一个集群共同对外提供服务,当负载均衡设
备通过心跳检测等手段监控到某台应用服务器不可用时,就将其从集群
列表中剔除,并将请求分发到集群中其他可用的服务器上,使整个集群
保持可用,从而实现应用高可用。
位于服务层的服务器情况和应用层的服务器类似,也是通过集群方
式实现高可用,只是这些服务器被应用层通过分布式服务调用框架访
问,分布式服务调用框架会在应用层客户端程序中实现软件负载均衡,并通过服务注册中心对提供服务的服务器进行心跳检测,发现有服务不
可用,立即通知客户端程序修改服务访问列表,剔除不可用的服务器。
位于数据层的服务器情况比较特殊,数据服务器上存储着数据,为了保证服务器宕机时数据不丢失,数据访问服务不中断,需要在数据写
入时进行数据同步复制,将数据写入多台服务器上,实现数据冗余备
份。当数据服务器宕机时,应用程序将访问切换到有备份数据的服务器
上。
网站升级的频率一般都非常高,大型网站一周发布一次,中小型网
站一天发布几次。每次网站发布都需要关闭服务,重新部署系统,整个
过程相当于服务器宕机。因此网站的可用性架构设计不但要考虑实际的
硬件故障引起的宕机,还要考虑网站升级发布引起的宕机,而后者更加
频繁,不能因为系统可以接受偶尔的停机故障就降低可用性设计的标
准。
5.3 高可用的应用
应用层主要处理网站应用的业务逻辑,因此有时也称作业务逻辑
层,应用的一个显著特点是应用的无状态性。
所谓无状态的应用是指应用服务器不保存业务的上下文信息,而仅
根据每次请求提交的数据进行相应的业务逻辑处理,多个服务实例(服
务器)之间完全对等,请求提交到任意服务器,处理结果都是完全一样
的。
5.3.1 通过负载均衡进行无状态服务的失效转移
不保存状态的应用给高可用的架构设计带来了巨大便利,既然服务
器不保存请求的状态,那么所有的服务器完全对等,当任意一台或多台
服务器宕机,请求提交给集群中其他任意一台可用机器处理,这样对终
端用户而言,请求总是能够成功的,整个系统依然可用。对于应用服务器集群,实现这种服务器可用状态实时监测、自动转移失败任务的机制
是负载均衡。
负载均衡,顾名思义,主要使用在业务量和数据量较高的情况下,当单台服务器不足以承担所有的负载压力时,通过负载均衡手段,将流
量和数据分摊到一个集群组成的多台服务器上,以提高整体的负载处理
能力。目前,不管是开源免费的负载均衡软件还是昂贵的负载均衡硬
件,都提供失效转移功能。在网站应用中,当集群中的服务是无状态对
等时,负载均衡可以起到事实上高可用的作用,如图5.5所示。
图5.5 利用负载均衡服务器实现高可用的应用服务
当Web服务器集群中的服务器都可用时,负载均衡服务器会把用户
发送的访问请求分发到任意一台服务器上进行处理,而当服务器
10.0.0.1宕机时,负载均衡服务器通过心跳检测机制发现该服务器失去
响应,就会把它从服务器列表中删除,而将请求发送到其他服务器上,这些服务器是完全一样的,请求在任何一台服务器中处理都不会影响最
终的结果。由于负载均衡在应用层实际上起到了系统高可用的作用,因此即使
某个应用访问量非常少,只用一台服务器提供服务就绰绰有余,但如果
需要保证该服务高可用,也必须至少部署两台服务器,使用负载均衡技
术构建一个小型的集群。
5.3.2 应用服务器集群的Session管理
应用服务器的高可用架构设计主要基于服务无状态这一特性,但是
事实上,业务总是有状态的,在交易类的电子商务网站,需要有购物车
记录用户的购买信息,用户每次购买请求都是向购物车中增加商品;在
社交类的网站中,需要记录用户的当前登录状态、最新发布的消息及好
友状态等,用户每次刷新页面都需要更新这些信息。
Web应用中将这些多次请求修改使用的上下文对象称作会话
(Session),单机情况下,Session可由部署在服务器上的Web容器(如
JBoss)管理。在使用负载均衡的集群环境中,由于负载均衡服务器可
能会将请求分发到集群任何一台应用服务器上,所以保证每次请求依然
能够获得正确的Session比单机时要复杂很多。
集群环境下,Session管理主要有以下几种手段。
1.Session复制
Session复制是早期企业应用系统使用较多的一种服务器集群Session
管理机制。应用服务器开启Web容器的Session复制功能,在集群中的几
台服务器之间同步Session对象,使得每台服务器上都保存所有用户的
Session信息,这样任何一台机器宕机都不会导致Session数据的丢失,而
服务器使用Session时,也只需要在本机获取即可。如图5.6所示。图5.6 使用Session复制实现应用服务器共享Session
这种方案虽然简单,从本机读取Session信息也很快速,但只能使用
在集群规模比较小的情况下。当集群规模较大时,集群服务器间需要大
量的通信进行Session复制,占用服务器和网络的大量资源,系统不堪负
担。而且由于所有用户的Session信息在每台服务器上都有备份,在大量
用户访问的情况下,甚至会出现服务器内存不够Session使用的情况。
而大型网站的核心应用集群就是数千台服务器,同时在线用户可达
千万,因此并不适用这种方案。
2.Session绑定
Session绑定可以利用负载均衡的源地址Hash算法实现,负载均衡服
务器总是将来源于同一IP的请求分发到同一台服务器上(也可以根据
Cookie信息将同一个用户的请求总是分发到同一台服务器上,当然这时
负载均衡服务器必须工作在HTTP协议层上,关于负载均衡算法的更多信息请参考本书第6章内容。这样在整个会话期间,用户所有的请求都
在同一台服务器上处理,即Session绑定在某台特定服务器上,保证
Session总能在这台服务器上获取。这种方法又被称作会话黏滞,如图
5.7所示。
图5.7 利用负载均衡的会话黏滞机制将请求绑定到特定服务器
但是Session绑定的方案显然不符合我们对系统高可用的需求,因为
一旦某台服务器宕机,那么该机器上的Session也就不复存在了,用户请
求切换到其他机器后因为没有Session而无法完成业务处理。因此虽然大
部分负载均衡服务器都提供源地址负载均衡算法,但很少有网站利用这
个算法进行Session管理。
3.利用Cookie记录Session
早期的企业应用系统使用CS(客户端服务器)架构,一种管理
Session 的方式是将Session记录在客户端,每次请求服务器的时候,将Session放在请求中发送给服务器,服务器处理完请求后再将修改过的
Session响应给客户端。
网站没有客户端,但是可以利用浏览器支持的Cookie记录Session,如图5.8所示。
图5.8 利用Cookie记录Session信息
利用Cookie记录Session也有一些缺点,比如受Cookie大小限制,能
记录的信息有限;每次请求响应都需要传输Cookie,影响性能;如果用
户关闭Cookie,访问就会不正常。但是由于Cookie的简单易用,可用性
高,支持应用服务器的线性伸缩,而大部分应用需要记录的Session信息
又比较小。因此事实上,许多网站都或多或少地使用Cookie记录
Session。
4.Session服务器那么有没有可用性高、伸缩性好、性能也不错,对信息大小又没有
限制的服务器集群Session管理方案呢?
答案就是Session服务器。利用独立部署的Session服务器(集群)统
一管理Session,应用服务器每次读写Session时,都访问Session服务器,如图5.9所示。
图5.9 利用Session服务器共享Session
这种解决方案事实上是将应用服务器的状态分离,分为无状态的应
用服务器和有状态的Session服务器,然后针对这两种服务器的不同特性
分别设计其架构。
对于有状态的Session服务器,一种比较简单的方法是利用分布式缓
存、数据库等,在这些产品的基础上进行包装,使其符合Session的存储
和访问要求。如果业务场景对Session管理有比较高的要求,比如利用
Session服务集成单点登录(SSO)、用户服务等功能,则需要开发专门
的Session服务管理平台。5.4 高可用的服务
可复用的服务模块为业务产品提供基础公共服务,大型网站中这些
服务通常都独立分布式部署,被具体应用远程调用。可复用的服务和应
用一样,也是无状态的服务,因此可以使用类似负载均衡的失效转移策
略实现高可用的服务。
除此之外,具体实践中,还有以下几点高可用的服务策略。
1.分级管理
运维上将服务器进行分级管理,核心应用和服务优先使用更好的硬
件,在运维响应速度上也格外迅速。显然,用户及时付款购物比能不能
评价商品更重要,所以订单、支付服务比评价服务有更高优先级。
同时在服务部署上也进行必要的隔离,避免故障的连锁反应。低优
先级的服务通过启动不同的线程或者部署在不同的虚拟机上进行隔离,而高优先级的服务则需要部署在不同的物理机上,核心服务和数据甚至
需要部署在不同地域的数据中心。
2.超时设置
由于服务端宕机、线程死锁等原因,可能导致应用程序对服务端的
调用失去响应,进而导致用户请求长时间得不到响应,同时还占用应用
程序的资源,不利于及时将访问请求转移到正常的服务器上。
在应用程序中设置服务调用的超时时间,一旦超时,通信框架就抛
出异常,应用程序根据服务调度策略,可选择继续重试或将请求转移到
提供相同服务的其他服务器上。3.异步调用
应用对服务的调用通过消息队列等异步方式完成,避免一个服务失
败导致整个应用请求失败的情况。如提交一个新用户注册请求,应用需
要调用三个服务:将用户信息写入数据库,发送账户注册成功邮件,开
通对应权限。如果采用同步服务调用 ......
核心原理与案例分析
李智慧 著
电子工业出版社内容简介
本书通过梳理大型网站技术发展历程,剖析大型网站技术架构模
式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技
术架构设计案例,为读者呈现一幅包括技术选型、架构设计、性能优
化、Web安全、系统发布、运维监控等在内的大型网站开发全景视图。
本书不仅适用于指导网站工程师、架构师进行网站技术架构设计,也可用于指导产品经理、项目经理、测试运维人员等了解网站技术架构
的基础概念;还可供包括企业系统开发人员在内的各类软件开发从业人
员借鉴,了解大型网站的解决方案和开发理念。
未经许可,不得以任何方式复制或抄袭本书之部分或全部内容。
版权所有,侵权必究。
图书在版编目(CIP)数据
大型网站技术架构:核心原理与案例分析李智慧著.—北京:电子工
业出版社,2013.9
ISBN 978-7-121-21200-0
Ⅰ.①大… Ⅱ.①李… Ⅲ.①网站-建设 Ⅳ.①TP393.092
中国版本图书馆CIP数据核字(2013)第182399号责任编辑:徐津平
印 刷:三河市双峰印刷装订有限公司
装 订:三河市双峰印刷装订有限公司
出版发行:电子工业出版社
北京市海淀区万寿路173信箱 邮编:100036
开 本:720×1000 116 印张:15 字数:240千字
印 次:2013年9月第1次印刷
印 数:4000册 定价:59.00元
凡所购买电子工业出版社图书有缺损问题,请向购买书店调换。若书店
售缺,请与本社发行部联系,联系及邮购电话:(010)88254888。
质量投诉请发邮件至zlts@phei.com.cn,盗版侵权举报请发邮件至
dbqq@phei.com.cn。
服务热线:(010)88258888。好评袭来
这是我看过的最接地气的一本介绍互联网架构的书籍,深入阐述了
大型网站所面临的各种架构问题及解决方案,内容通俗易懂,而且对架
构师的领导艺术进行了介绍,很值得从事互联网的技术人员学习和参
考。
IBM咨询经理 种新华
此书读来亲切,能用不到300页的篇幅将网站架构的过去及未来说
得如此通俗易懂,与作者多年的亲身实践分不开,并由此想到一个问
题:当此书人手一本的时候,阿里、腾讯、京东……的面试官们怎么办
呢?
Oracle资深工程师 付银海
智慧同学,人如其名,在阿里巴巴,人称“教授”,可见其博学多
才。《大型网站技术架构:核心原理与案例分析》一书更是其多年积淀
厚积薄发之作,涵盖构建大型互联网应用所需的关键技术,兼具实用性
和前瞻性,无论是高并发、高性能还是海量数据处理、Web前端架构,都有针对性的解决之道。尤其难得的是此书还对架构师的内涵及技术管
理有比较深刻地阐述,实在是同类书籍中难得一见的。作为互联网应用
的开发者、架构师和创业者的你,一定不要错过本书,本书足以解决你的技术之忧。
拓维信息平台研发总监 陈斌
教授(本书作者在阿里巴巴的昵称)曾在知名的大型互联网公司第
一线浴血多年,经验不可谓不丰富,然而更难得的是他不仅博闻强记,更用行云流水的幽默文风,将现代大型互联网的内部要害一一庖解。也
许各家细节略有不同,但大部分的大型互联网站基本都可以用这样的视
角去解读。相信本书不仅对程序员,甚至对很多架构师也有参考价值,尤其值得关注的是教授在书中颇多技术之外的考量思索,我愿意称之为
互联网基因。
堆糖网技术合伙人 曹文炯
有幸拜读了这本《大型网站技术架构:核心原理与案例分析》,本
书从多个层面说明了如何构建一个高可用、高性能、高可扩展性的网站
系统,并结合了阿里巴巴及其他互联网企业先进的架构实践经验进行案
例分析,讲述非常全面且具指导意义。本书从网站的架构设计、快速开
发、高效部署、业务监控、服务治理、运维管理等多个角度描述了架构
设计的相关重点,涉及的核心技术包括前端优化、CDN、反向代理、缓
存、消息队列、分布式存储、分布式服务、NoSQL存储、搜索、监控、安全等一系列保证大型网站安全可靠运行的关键技术点。本书还提供了
网站如何从小型网站伴随用户成长,逐步扩展到大型网站的架构演进思
路,是互联网架构师们不可多得的一本技术参考书。
中兴通讯总工程师 钱煜明设计和规划一个网站的总体架构涉及方方面面的东西,备选的方案
也很多,如何在五花八门,纷繁复杂的技术中构建最适合用户的网站架
构,变成了一件极具争议和挑战性的工作。一个好的架构可以以最低的
成本,在满足用户需求的同时,满足整个网站的架构灵活性;同样,一
个糟糕的架构可能会让你的客户在花费了大量金钱后,得到一堆笨重、复杂且不切实际的东西,或是由于系统过于复杂,故障不断,或是由于
架构不够灵活,阻碍业务的发展等等。
回顾网站架构的发展历程,我们可以发现任何大型网站架构的发展
都非一蹴而就的,同自然界生物物竞天择的自然进化规律一样,大型网
站的架构发展和演变也基本遵循着类似的规律。我们可能无法想象几年
后网站架构的样子,因为在互联网行业快速变化的当下,你甚至很难准
确地预测未来一年网站的产品演变方向,甚至网站流量规模。于是,产
品设计师和工程师们提得最多的是迭代和演变,这在一个网站系统架构
设计过程中显得尤为重要,因为我们永远无法像传统行业一样,去精确
地估算,并按预先精确设计好的图纸去完成我们的产品。那是不是网站
的架构设计和规划就毫无规律及章法可循了呢?答案显然不是,在互联
网快速发展的今天,随着搜索引擎、电子商务、社交类等互联网产品逐
步应用到每个人的身边,大型网站的架构及很多关键技术的发展,在逐
步走向成熟。在构建一个大型网站过程中可能面临一些问题,人们正在
尝试逐渐总结并积累出一些具有通用性的、经过验证的且成熟的局部解
决方案,这也是本书将呈现给大家的内容。本书中,作者以自己多年大
型互联网网站的架构经验,尝试总结当下这些互联网行业中相对成熟且
经过大量案例检验的技术和方案。
相信通过阅读本书,您可以一窥大型网站架构的全貌。
阿里巴巴技术专家 余俊循序渐进,娓娓道来,语言生动,举重若轻。
阿里云高级专家 李文兆推荐序一
传统的企业应用系统主要面对的技术挑战是处理复杂凌乱、千变万
化的所谓业务逻辑,而大型网站主要面对的技术挑战是处理超大量的用
户访问和海量的数据处理;前者的挑战来自功能性需求,后者的挑战来
自非功能性需求;功能性需求也许还有“人月神话”聊以自慰,通过增加
人手解决问题,而非功能需求大多是实实在在的技术难题,无论有多少
工程师,做不到就是做不到。IT系统应用于企业管理已有超过半个世纪
的历史,人们在这方面积累了大量的知识和经验(架构模式,领域分
析,项目管理),而真正意义上大型网站从出现至今不过短短十多年的
时间,很多技术挑战还在摸索阶段。市面上关于传统企业应用开发的书
籍汗牛充栋,而真正能够深入全面地阐述大型网站技术架构的图书寥寥
无几。所以很多人就很困惑:为什么很多看起来不是很复杂的网站,比
如Facebook、淘宝,都需要大量顶尖高手来开发呢?
值得庆幸的是,作者为我们带来了这本《大型网站技术架构:核心
原理与案例分析》,比较全面地阐述了大型网站的主要技术挑战和解决
方案。宏观层面上,将网站架构的演化发展、架构模式、核心要素一一
道来;微观层面上,将网站架构常用的分布式缓存、负载均衡、消息队
列、分布式服务、甚至网站如何发布运维都逐一进行了阐述。大型网站
的技术之道尽在于此。
作者在阿里巴巴工作期间,一方面参与基础技术平台产品开发,一
方面参与网站架构设计,这些经历使作者能够比较全面地从理论和实践两个视角去看待和描述网站架构。书中的技术内容基本都从为什么
(Why)要这么做和如何去做(How)两个层面进行表述。读者可知其
然并知其所以然。
阅读本书也许不能使你就此掌握大型网站架构设计的屠龙之术,但
至少使你对网站架构的方法和思维方式能有全面了解。
开卷有益,应该指的就是这样的书。
支付宝研究员 潘磊推荐序二
这些年互联网技术蓬勃发展,各种成熟的组件、工具、框架越来越
丰富,各种理论逐渐发展成熟,各大公司公开的理论和实践资料也越来
越多,在各个领域都有比较成熟的解决方案,但是研究领先互联网公司
的架构,无论是Google、Facebook、Amazon还是淘宝、支付宝、腾
讯、百度,都各有其独特的地方。
各个环节都有成熟的产品或者方案,为什么这么多互联网公司的架
构还有如此明显的差异呢?是不是照着Google、Facebook、淘宝的架构
做,就能做好一个“大型的互联网应用”呢?
正如本书中所言:“好的设计绝对不是模仿、不是生搬硬套某个模
式,而是在对问题深刻理解之上的创造与创新,即使是‘微创新’,也是
让人耳目一新的似曾相识。山寨与创新的最大区别不在于是否抄袭、是
否模仿,而在于对问题和需求是否真正理解与把握。”
这些大型的互联网应用是设计出来的?还是演化出来的?在设计的
过程中需要考虑哪些因素?演化过程中都会面临哪些问题,哪些挑战?
本书从性能、可用性、伸缩性、扩展性、安全性几个网站核心架构
要素切入,全面地介绍了这些核心要素面临的问题域、理论基础及应对
方案;对这几个方面进行系统地分析,结合目前成熟的解决方案,以及
作者自己的工作经验,理论联系实际,踏实细致地提出合理的解决方
案,非常值得我们学习和借鉴。作者还通过对淘宝、Wikipedia、分布式存储系统、秒杀系统等案例
的分析,仔细探讨了典型互联网架构的演进过程,剖析了分布式系统设
计和实现中的挑战和解决方案,并研究了极端情况下,秒杀给网站带来
的难以预计的瞬间高并发冲击的应对策略和架构设计。还通过一些实实
在在发生过的故障案例分析,从另一个侧面来说明,我们在做技术架构
时,需要考量的一些关键点,这些分享都是不可多得的血泪经验。
本书观点明确,涉及的问题域有针对性和全面性,对问题的分析过
程清晰,提出的解决方案切实可行,充分结合了目前成功的互联网公司
的架构经验,结合了作者丰富的工作经验,是一本值得行业内人士学习
和关注的好书。
作者李智慧在互联网行业具有丰富的经验,在阿里巴巴工作的几年
中担任架构师,参与过多个重要的项目和产品的架构设计,遇到和处理
了很多复杂的问题,在这方面积累了大量的经验。本书是作者多年的架
构师经历,以及时刻的思考和积累的结晶,一词一句都是经验之谈,都
是智慧的闪亮。
感谢作者耗费精力给我们带来如此精炼而又内容丰富的一本好书。
支付宝资深架构师 王定乾序
我为什么要写这本书
我想写一本关于网站架构方面的书源起于2011年年末至2012年年初
发生的两件事。
2011年末,京东网图书促销,在打5折的基础上再满一百送一百,作为一个爱买书胜过爱读书的人,我对这种促销活动根本没有免疫力,于是兴致勃勃地在活动当天登录www.360buy.com,准备将收藏夹里的
图书一网打尽。
往购物车里尽情地塞了一堆书后,点击“购买”按钮,但是浏览器迟
迟没有响应,预感到京东的服务器可能因为并发访问量过高,超过了系
统的最大负载能力,果然过了一会,浏览器页面显示“Service is too
busy”。我不甘心,返回购物车页面继续点击“购买”按钮,浏览器继续
显示“Service is too busy”。
于是我猜测:能够正常访问购物车,却不能成功购买,问题应该是
出在订单系统,B2C网站生成一个订单需要经历扣减库存、扣减促销资
源、更新用户账户等一系列操作,这些操作大多是数据库事务操作,没
有办法通过缓存等手段来减轻数据库服务器负载压力,如果事前没有设
计好数据库伸缩性架构,那么京东的技术团队将遇到一个大麻烦。
当天晚上,我登录新浪微博,看到京东的大老板刘强东发布了一条微博:“我已经紧急采购了10台服务器,增强网站后台,明天继续促销
一天,一定让大家买到书”。即使在有成熟数据库伸缩性架构设计的前
提下,进行一次数据库扩容也是件棘手的事,而京东只需要一个晚上就
能搞定,让我对京东的技术实力刮目相看。
第二天一上班,我的第一件事就是登录www.360buy.com,点击“购
买”按钮后悲剧地发现页面还是“Service is too busy”。当天晚上,刘强东
又发布了一条微博:“请信息部的同事喝茶”。还配了一张照片:一张大
桌子,只有一杯茶,旁边放了一把刀……
我想京东信息部的同事绝对不是有意要捉弄他们的老板和客户,很
可能是他们错误地判断了系统的瓶颈及伸缩性架构的困难,对老板做出
了过度承诺,而这背后折射出的是他们对网站架构的本质缺乏了解。
另一件事发生在2012年年初,当时的中国铁道部官方售票网站
www.12306.cn在春运期间因为大量用户访问而崩溃,无法有效访问。
12306作为一个运营不久的网站,缺乏大规模并发访问处理的经验,遇
到一些问题其实不奇怪,不管花多少钱,经验教训都需要经历时间和挫
折才能得到。奇怪的是,12306的架构师似乎对这种可能发生的大规模
并发访问产生的问题完全没有一点概念,系统好像根本没有经过任何高
并发场景下的性能评估和性能测试,就那么干脆利落地崩溃了,趴在那
里长时间起不来。
这两件事情促使我想写一本关于网站架构的书,阐述网站技术架构
最基本的驱动力,基础的架构设计原理,以及架构方案选择的价值观。
希望软件工程师们在解决问题之前,能够认真思考自己面对的真正问题
究竟是什么,有哪些技术方案可以选择,其基本原理是什么。所以这本
书里没有高深的算法和聱牙诘屈的公式,也很少有程序代码。读者可以把本书当作网站架构设计的科普书,即使对网站架构没有什么了解,也
能够比较轻松地阅读。
在本书的写作过程中(2012年下半年),没有再看到京东促销宕机
的新闻,12306也逐渐稳定成熟。我们虽然无法猜测京东“信息部的同
事”和12306网站的工程师们付出了多少努力,但能在相对比较短的时间
里解决这些技术问题,也说明了网站架构其实并不难,真正能解决问题
的技术一定是简单的。
本书致力于把这些简单的技术和道理呈现给读者。
如何阅读本书
我自己读书不求甚解,遇到看不懂的地方就跳过去,但是希望作者
对难点和重点能换个角度和方式在后面章节再叙述,以帮助我重新思考
和认识前面不能理解的重要知识。
机械制图的时候,通常使用三视图描述一个机械零件,从正视、侧
视、俯视三个角度对一个零件绘图,从而全面描述一个零件的结构。软
件架构设计中常用的4+1视图模型,也是一种多角度描述软件系统设计
的手段。
本书中,重要的架构原理和技术方案都采用多角度描述的方法。
第1篇,从演化、模式、要素三个维度描述网站整体架构。
第2篇,从性能、可用性、伸缩性、扩展性、安全这五个要素方面
详细描述网站架构核心原理,其中重要的负载均衡、异步处理、分布式
缓存等技术方案又在不同章节从多角度进行描述。第3篇,通过几个具体案例再一次从整体和局部描述网站架构方
法。
第4篇,从架构师做事的角度回顾网站技术架构,读者在阅读前面
技术章节感到枯燥的时候,也可以跳到本篇休闲放松下。
阅读本书过程中有任何问题和建议,请联系作者。新浪微博:@大
型网站技术架构。致谢
2012年五一节前夕,当我拜访博文视点的编辑胡辛征,商谈出版一
本关于大型网站技术架构的图书时,没有想到自己面临的挑战是如此巨
大。
整个图书写作过程就像喝醉了酒:头痛如裂,有很多话想说,但又
不知该从何说起。
我很庆幸,这个过程有你们陪伴、支持、鼓励和帮助,是你们给了
我继续前行的勇气。
感谢易普际的培训顾问周腾飞,策划并鼓励我去写这本书。
感谢阿里巴巴的技术专家余俊和何坤,这本书的大纲和结构就是和
你们在钱塘江畔散步时聊出来的,但很遗憾最后没能成功蛊惑你们和我
一起创作本书。
感谢阿里巴巴高级开发工程师熊红亮、丁夏珍;IBM咨询经理种新
华、架构师吴业勇;百度产品经理王晟;Intel运维工程师Liu Gongmin
给予的建议和鼓励。
感谢博文视点的编辑刘皎、郑柳洁,以及许多我不知道名字的编辑
为本书最终出版付出的努力。
感谢阿里巴巴资深架构师潘磊、王定乾、钱霄、王齐,指引我进入网站架构的知识殿堂。
本书很多内容源自阿里同学们的知识库,原谅我无法一一致谢。
感谢我的妻子方芬香,你给予我一个新的世界,让我如此热爱生
活。目 录
好评袭来
推荐序一
推荐序二
序 我为什么要写这本书
致谢
第1篇 概述
1 大型网站架构演化
1.1 大型网站软件系统的特点
1.2 大型网站架构演化发展历程
1.2.1 初始阶段的网站架构
1.2.2 应用服务和数据服务分离
1.2.3 使用缓存改善网站性能
1.2.4 使用应用服务器集群改善网站的并发处理能力
1.2.5 数据库读写分离
1.2.6 使用反向代理和CDN加速网站响应
1.2.7 使用分布式文件系统和分布式数据库系统
1.2.8 使用NoSQL和搜索引擎
1.2.9 业务拆分
1.2.10 分布式服务
1.3 大型网站架构演化的价值观
1.3.1 大型网站架构技术的核心价值是随网站所需灵活应对
1.3.2 驱动大型网站技术发展的主要力量是网站的业务发展
1.4 网站架构设计误区
1.4.1 一味追随大公司的解决方案1.4.2 为了技术而技术
1.4.3 企图用技术解决所有问题
1.5 小结
2 大型网站架构模式
2.1 网站架构模式
2.1.1 分层
2.1.2 分割
2.1.3 分布式
2.1.4 集群
2.1.5 缓存
2.1.6 异步
2.1.7 冗余
2.1.8 自动化
2.1.9 安全
2.2 架构模式在新浪微博的应用
2.3 小结
3 大型网站核心架构要素
3.1 性能
3.2 可用性
3.3 伸缩性
3.4 扩展性
3.5 安全性
3.6 小结
第2篇 架构
4 瞬时响应:网站的高性能架构
4.1 网站性能测试
4.1.1 不同视角下的网站性能4.1.2 性能测试指标
4.1.3 性能测试方法
4.1.4 性能测试报告
4.1.5 性能优化策略
4.2 Web前端性能优化
4.2.1 浏览器访问优化
4.2.2 CDN加速
4.2.3 反向代理
4.3 应用服务器性能优化
4.3.1 分布式缓存
4.3.2 异步操作
4.3.3 使用集群
4.3.4 代码优化
4.4 存储性能优化
4.4.1 机械硬盘vs. 固态硬盘
4.4.2 B+树 vs. LSM树
4.4.3 RAID vs. HDFS
4.5 小结
5 万无一失:网站的高可用架构
5.1 网站可用性的度量与考核
5.1.1 网站可用性度量
5.1.2 网站可用性考核
5.2 高可用的网站架构
5.3 高可用的应用
5.3.1 通过负载均衡进行无状态服务的失效转移
5.3.2 应用服务器集群的Session管理
5.4 高可用的服务5.5 高可用的数据
5.5.1 CAP原理
5.5.2 数据备份
5.5.3 失效转移
5.6 高可用网站的软件质量保证
5.6.1 网站发布
5.6.2 自动化测试
5.6.3 预发布验证
5.6.4 代码控制
5.6.5 自动化发布
5.6.6 灰度发布
5.7 网站运行监控
5.7.1 监控数据采集
5.7.2 监控管理
5.8 小结
6 永无止境:网站的伸缩性架构
6.1 网站架构的伸缩性设计
6.1.1 不同功能进行物理分离实现伸缩
6.1.2 单一功能通过集群规模实现伸缩
6.2 应用服务器集群的伸缩性设计
6.2.1 HTTP重定向负载均衡
6.2.2 DNS域名解析负载均衡
6.2.3 反向代理负载均衡
6.2.4 IP负载均衡
6.2.5 数据链路层负载均衡
6.2.6 负载均衡算法
6.3 分布式缓存集群的伸缩性设计6.3.1 Memcached分布式缓存集群的访问模型
6.3.2 Memcached分布式缓存集群的伸缩性挑战
6.3.3 分布式缓存的一致性Hash算法
6.4 数据存储服务器集群的伸缩性设计
6.4.1 关系数据库集群的伸缩性设计
6.4.2 NoSQL数据库的伸缩性设计
6.5 小结
7 随需应变:网站的可扩展架构
7.1 构建可扩展的网站架构
7.2 利用分布式消息队列降低系统耦合性
7.2.1 事件驱动架构
7.2.2 分布式消息队列
7.3 利用分布式服务打造可复用的业务平台
7.3.1 Web Service与企业级分布式服务
7.3.2 大型网站分布式服务的需求与特点
7.3.3 分布式服务框架设计
7.4 可扩展的数据结构
7.5 利用开放平台建设网站生态圈
7.6 小结
8 固若金汤:网站的安全架构
8.1 道高一尺魔高一丈的网站应用攻击与防御
8.1.1 XSS攻击
8.1.2 注入攻击
8.1.3 CSRF攻击
8.1.4 其他攻击和漏洞
8.1.5 Web应用防火墙
8.1.6 网站安全漏洞扫描8.2 信息加密技术及密钥安全管理
8.2.1 单向散列加密
8.2.2 对称加密
8.2.3 非对称加密
8.2.4 密钥安全管理
8.3 信息过滤与反垃圾
8.3.1 文本匹配
8.3.2 分类算法
8.3.3 黑名单
8.4 电子商务风险控制
8.4.1 风险
8.4.2 风控
8.5 小结
第3篇 案例
9 淘宝网的架构演化案例分析
9.1 淘宝网的业务发展历程
9.2 淘宝网技术架构演化
9.3 小结
10 维基百科的高性能架构设计分析
10.1 Wikipedia网站整体架构
10.2 Wikipedia性能优化策略
10.2.1 Wikipedia前端性能优化
10.2.2 Wikipedia服务端性能优化
10.2.3 Wikipedia后端性能优化
11 海量分布式存储系统Doris的高可用架构设计分析
11.1 分布式存储系统的高可用架构
11.2 不同故障情况下的高可用解决方案11.2.1 分布式存储系统的故障分类
11.2.2 正常情况下系统访问结构
11.2.3 瞬时故障的高可用解决方案
11.2.4 临时故障的高可用解决方案
11.2.5 永久故障的高可用解决方案
12 网购秒杀系统架构设计案例分析
12.1 秒杀活动的技术挑战
12.2 秒杀系统的应对策略
12.3 秒杀系统架构设计
12.4 小结
13 大型网站典型故障案例分析
13.1 写日志也会引发故障
13.2 高并发访问数据库引发的故障
13.3 高并发情况下锁引发的故障
13.4 缓存引发的故障
13.5 应用启动不同步引发的故障
13.6 大文件读写独占磁盘引发的故障
13.7 滥用生产环境引发的故障
13.8 不规范的流程引发的故障
13.9 不好的编程习惯引发的故障
13.10 小结
第4篇 架构师
14 架构师领导艺术
14.1 关注人而不是产品
14.2 发掘人的优秀
14.3 共享美好蓝图
14.4 共同参与架构14.5 学会妥协
14.6 成就他人
15 网站架构师职场攻略
15.1 发现问题,寻找突破
15.2 提出问题,寻求支持
15.3 解决问题,达成绩效
16 漫话网站架构师
16.1 按作用划分架构师
16.2 按效果划分架构师
16.3 按职责角色划分架构师
16.4 按关注层次划分架构师
16.5 按口碑划分架构师
16.6 非主流方式划分架构师
附录A 大型网站架构技术一览
附录B Web开发技术发展历程
后记第1篇 概述1 大型网站架构演化
如果把上世纪90年代初CERN正式发布Web标准和第一个Web服务
的出现当做互联网站的开始,那么互联网站的发展只经历了短短20多年
的时间。在20多年的时间里,互联网的世界发生了巨大变化,今天,全
球有近一半的人口使用互联网,人们的生活因为互联网而产生了巨大改
变。从信息检索到即时通信,从电子购物到文化娱乐,互联网渗透到生
活的每个角落,而且这种趋势还在加速。因为互联网,我们的世界正变
得越来越小。
同时我们也看到,在互联网跨越式发展的进程中,在电子商务火热
的市场背后却是不堪重负的网站架构,某些B2C网站逢促销必宕机几乎
成为一种规律,而铁道部电子客票官方购票网站的频繁故障和操作延迟
更将这一现象演绎得淋漓尽致。
一边是企业在网站技术上的大量投入,一边却是网站在关键时刻的
频繁宕机;一边是工程师夜以继日地加班工作,一边却是网站故障频发
新功能上线缓慢;一边是互联网业务快速发展多领域挑战传统行业,一
边却是网站安全漏洞频发让网民胆战心惊怨声载道。
如何打造一个高可用、高性能、易扩展、可伸缩且安全的网站?如
何让网站随应用所需灵活变动,即使是山寨他人的产品,也可以山寨的
更高、更快、更强,一年时间用户数从零过亿呢?
1.1 大型网站软件系统的特点
与传统企业应用系统相比,大型互联网应用系统有以下特点。高并发,大流量: 需要面对高并发用户,大流量访问。Google日
均PV数35亿,日均IP访问数3亿;腾讯QQ的最大在线用户数1.4亿
(2011年数据);淘宝2012年“双十一”活动一天交易额超过191亿,活
动开始第一分钟独立访问用户达1000万。
高可用: 系统7×24小时不间断服务。大型互联网站的宕机事件通
常会成为新闻焦点,例如2010年百度域名被黑客劫持导致不能访问,成
为重大新闻热点。
海量数据: 需要存储、管理海量数据,需要使用大量服务器。
Facebook每周上传的照片数目接近10亿,百度收录的网页数目有数百
亿,Google有近百万台服务器为全球用户提供服务。
用户分布广泛,网络情况复杂: 许多大型互联网都是为全球用户
提供服务的,用户分布范围广,各地网络情况千差万别。在国内,还有
各个运营商网络互通难的问题。而中美光缆的数次故障,也让一些对国
外用户依赖较大的网站不得不考虑在海外建立数据中心。
安全环境恶劣: 由于互联网的开放性,使得互联网站更容易受到
攻击,大型网站几乎每天都会被黑客攻击。2011年国内多个重要网站泄
露用户密码,让普通用户也直面一次互联网安全问题。
需求快速变更,发布频繁: 和传统软件的版本发布频率不同,互
联网产品为快速适应市场,满足用户需求,其产品发布频率是极高的。
Office的产品版本以年为单位发布,而一般大型网站的产品每周都有新
版本发布上线,至于中小型网站的发布就更频繁了,有时候一天会发布
几十次。
渐进式发展: 与传统软件产品或企业应用系统一开始就规划好全部的功能和非功能需求不同,几乎所有的大型互联网站都是从一个小网
站开始,渐进地发展起来的。Facebook是伯克扎克同学在哈佛大学的宿
舍里开发的;Google的第一台服务器部署在斯坦福大学的实验室里;阿
里巴巴则是在马云家的客厅里诞生的。好的互联网产品都是慢慢运营出
来的,不是一开始就开发好的,这也正好与网站架构的发展演化过程对
应。
1.2 大型网站架构演化发展历程
大型网站的技术挑战主要来自于庞大的用户,高并发的访问和海量
的数据,任何简单的业务一旦需要处理数以P计的数据和面对数以亿计
的用户,问题就会变得很棘手。大型网站架构主要就是解决这类问题。
1.2.1 初始阶段的网站架构
大型网站都是从小型网站发展而来,网站架构也是一样,是从小型
网站架构逐步演化而来。小型网站最开始时没有太多人访问,只需要一
台服务器就绰绰有余,这时的网站架构如图1.1所示。图1.1 初始阶段的网站架构
应用程序、数据库、文件等所有的资源都在一台服务器上。通常服
务器操作系统使用Linux,应用程序使用PHP开发,然后部署在Apache
上,数据库使用MySQL,汇集各种免费开源软件及一台廉价服务器就
可以开始网站的发展之路了。
1.2.2 应用服务和数据服务分离
随着网站业务的发展,一台服务器逐渐不能满足需求:越来越多的
用户访问导致性能越来越差,越来越多的数据导致存储空间不足。这时
就需要将应用和数据分离。应用和数据分离后整个网站使用三台服务
器:应用服务器、文件服务器和数据库服务器,如图1.2所示。这三台
服务器对硬件资源的要求各不相同,应用服务器需要处理大量的业务逻
辑,因此需要更快更强大的CPU;数据库服务器需要快速磁盘检索和数
据缓存,因此需要更快的硬盘和更大的内存;文件服务器需要存储大量用户上传的文件,因此需要更大的硬盘。
图1.2 应用服务和数据服务分离
应用和数据分离后,不同特性的服务器承担不同的服务角色,网站
的并发处理能力和数据存储空间得到了很大改善,支持网站业务进一步
发展。但是随着用户逐渐增多,网站又一次面临挑战:数据库压力太大
导致访问延迟,进而影响整个网站的性能,用户体验受到影响。这时需
要对网站架构进一步优化。
1.2.3 使用缓存改善网站性能
网站访问特点和现实世界的财富分配一样遵循二八定律:80%的业
务访问集中在20%的数据上。淘宝买家浏览的商品集中在少部分成交数
多、评价良好的商品上;百度搜索关键词集中在少部分热门词汇上;只
有经常登录的用户才会发微博、看微博,而这部分用户也只占总用户数
目的一小部分。既然大部分的业务访问集中在一小部分数据上,那么如果把这一小
部分数据缓存在内存中,是不是就可以减少数据库的访问压力,提高整
个网站的数据访问速度,改善数据库的写入性能了呢?
网站使用的缓存可以分为两种:缓存在应用服务器上的本地缓存和
缓存在专门的分布式缓存服务器上的远程缓存。本地缓存的访问速度更
快一些,但是受应用服务器内存限制,其缓存数据量有限,而且会出现
和应用程序争用内存的情况。远程分布式缓存可以使用集群的方式,部
署大内存的服务器作为专门的缓存服务器,可以在理论上做到不受内存
容量限制的缓存服务,如图1.3所示。图1.3 网站使用缓存
使用缓存后,数据访问压力得到有效缓解,但是单一应用服务器能
够处理的请求连接有限,在网站访问高峰期,应用服务器成为整个网站
的瓶颈。
1.2.4 使用应用服务器集群改善网站的并发处理能力
使用集群是网站解决高并发、海量数据问题的常用手段。当一台服
务器的处理能力、存储空间不足时,不要企图去换更强大的服务器,对
大型网站而言,不管多么强大的服务器,都满足不了网站持续增长的业
务需求。这种情况下,更恰当的做法是增加一台服务器分担原有服务器
的访问及存储压力。
对网站架构而言,只要能通过增加一台服务器的方式改善负载压
力,就可以以同样的方式持续增加服务器不断改善系统性能,从而实现
系统的可伸缩性。应用服务器实现集群是网站可伸缩集群架构设计中较
为简单成熟的一种,如图1.4所示。图1.4 应用服务器集群部署
通过负载均衡调度服务器,可将来自用户浏览器的访问请求分发到
应用服务器集群中的任何一台服务器上,如果有更多的用户,就在集群
中加入更多的应用服务器,使应用服务器的负载压力不再成为整个网站
的瓶颈。
1.2.5 数据库读写分离
网站在使用缓存后,使绝大部分数据读操作访问都可以不通过数据
库就能完成,但是仍有一部分读操作(缓存访问不命中、缓存过期)和
全部的写操作需要访问数据库,在网站的用户达到一定规模后,数据库
因为负载压力过高而成为网站的瓶颈。
目前大部分的主流数据库都提供主从热备功能,通过配置两台数据
库主从关系,可以将一台数据库服务器的数据更新同步到另一台服务器
上。网站利用数据库的这一功能,实现数据库读写分离,从而改善数据库负载压力,如图1.5所示。
图1.5 数据库读写分离
应用服务器在写数据的时候,访问主数据库,主数据库通过主从复
制机制将数据更新同步到从数据库,这样当应用服务器读数据的时候,就可以通过从数据库获得数据。为了便于应用程序访问读写分离后的数
据库,通常在应用服务器端使用专门的数据访问模块,使数据库读写分
离对应用透明。
1.2.6 使用反向代理和CDN加速网站响应
随着网站业务不断发展,用户规模越来越大,由于中国复杂的网络
环境,不同地区的用户访问网站时,速度差别也极大。有研究表明,网
站访问延迟和用户流失率正相关,网站访问越慢,用户越容易失去耐心而离开。为了提供更好的用户体验,留住用户,网站需要加速网站访问
速度。主要手段有使用CDN和反向代理,如图1.6所示。
图1.6 网站使用反向代理和CDN加速访问
CDN和反向代理的基本原理都是缓存,区别在于CDN部署在网络
提供商的机房,使用户在请求网站服务时,可以从距离自己最近的网络
提供商机房获取数据;而反向代理则部署在网站的中心机房,当用户请
求到达中心机房后,首先访问的服务器是反向代理服务器,如果反向代
理服务器中缓存着用户请求的资源,就将其直接返回给用户。
使用CDN和反向代理的目的都是尽早返回数据给用户,一方面加快
用户访问速度,另一方面也减轻后端服务器的负载压力。
1.2.7 使用分布式文件系统和分布式数据库系统
任何强大的单一服务器都满足不了大型网站持续增长的业务需求。数据库经过读写分离后,从一台服务器拆分成两台服务器,但是随着网
站业务的发展依然不能满足需求,这时需要使用分布式数据库。文件系
统也是一样,需要使用分布式文件系统,如图1.7所示。
图1.7 使用分布式文件和分布式数据库系统
分布式数据库是网站数据库拆分的最后手段,只有在单表数据规模
非常庞大的时候才使用。不到不得已时,网站更常用的数据库拆分手段
是业务分库,将不同业务的数据库部署在不同的物理服务器上。
1.2.8 使用NoSQL和搜索引擎
随着网站业务越来越复杂,对数据存储和检索的需求也越来越复
杂,网站需要采用一些非关系数据库技术如NoSQL和非数据库查询技术
如搜索引擎,如图1.8所示。图1.8 使用NoSQL系统和搜索引擎
NoSQL和搜索引擎都是源自互联网的技术手段,对可伸缩的分布式
特性具有更好的支持。应用服务器则通过一个统一数据访问模块访问各
种数据,减轻应用程序管理诸多数据源的麻烦。
1.2.9 业务拆分
大型网站为了应对日益复杂的业务场景,通过使用分而治之的手段
将整个网站业务分成不同的产品线,如大型购物交易网站就会将首页、商铺、订单、买家、卖家等拆分成不同的产品线,分归不同的业务团队
负责。
具体到技术上,也会根据产品线划分,将一个网站拆分成许多不同
的应用,每个应用独立部署维护。应用之间可以通过一个超链接建立关
系(在首页上的导航链接每个都指向不同的应用地址),也可以通过消
息队列进行数据分发,当然最多的还是通过访问同一个数据存储系统来构成一个关联的完整系统,如图1.9所示。
图1.9 应用拆分
1.2.10 分布式服务
随着业务拆分越来越小,存储系统越来越庞大,应用系统的整体复
杂度呈指数级增加,部署维护越来越困难。由于所有应用要和所有数据
库系统连接,在数万台服务器规模的网站中,这些连接的数目是服务器
规模的平方,导致存数据库接资源不足,拒绝服务。
既然每一个应用系统都需要执行许多相同的业务操作,比如用户管
理、商品管理等,那么可以将这些共用的业务提取出来,独立部署。由
这些可复用的业务连接数据库,提供共用业务服务,而应用系统只需要
管理用户界面,通过分布式服务调用共用业务服务完成具体业务操作,如图1.10所示。图1.10 分布式服务
大型网站的架构演化到这里,基本上大多数的技术问题都得以解
决,诸如跨数据中心的实时数据同步和具体网站业务相关的问题也都可
以通过组合改进现有技术架构来解决。
但事物发展到一定阶段,就会拥有自身的发展冲动,摆脱其初衷,向着使自己更强大的方向发展。既然大型网站架构解决了海量数据的管
理和高并发事务的处理,那么就可以把这些解决方案应用到网站自身以
外的业务上去。我们看到目前许多大型网站都开始建设云计算平台,将
计算作为一种基础资源出售,中小网站不需要再关心技术架构问题,只
需要按需付费,就可以使网站随着业务的增长逐渐获得更大的存储空间
和更多的计算资源。
1.3 大型网站架构演化的价值观
这个世界没有哪个网站从诞生起就是大型网站;也没有哪个网站第
一次发布就拥有庞大的用户,高并发的访问,海量的数据;大型网站都
是从小型网站发展而来。网站的价值在于它能为用户提供什么价值,在于网站能做什么,而不在于它是怎么做的,所以在网站还很小的时候就
去追求网站的架构是舍本逐末,得不偿失的。小型网站最需要做的就是
为用户提供好的服务来创造价值,得到用户的认可,活下去,野蛮生
长。
所以我们看到,一方面是随着互联网的高速发展,越来越多新的软
件技术和产品从互联网公司诞生,挑战传统软件巨头的江湖地位。另一
方面却是中小网站十几年如一日地使用LAMP技术(Linux+Apache+
MySQL+PHP)开发自己的网站,因为LAMP既便宜又简单,而且对付
一个中小型网站绰绰有余。
1.3.1 大型网站架构技术的核心价值是随网站所需灵活应对
大型网站架构技术的核心价值不是从无到有搭建一个大型网站,而
是能够伴随小型网站业务的逐步发展,慢慢地演化成一个大型网站。在
这个漫长的技术演化过程中,不需要放弃什么,不需要推翻什么,不需
要剧烈的革命,就那么润物细无声地把一个只有一台服务器,几百个用
户的小网站演化成一个几十万台服务器,数十亿用户的大网站。今天我
们看到的大型网站,Google,Facebook,Taobao,Baidu莫不遵循这样
的技术演化路线。
1.3.2 驱动大型网站技术发展的主要力量是网站的业务发展
创新的业务发展模式对网站架构逐步提出更高要求,才使得创新的
网站架构得以发展成熟。是业务成就了技术,是事业成就了人,而不是
相反。所以网站架构师应该对成就自己技术成绩的网站事业心存感恩,并努力提高技术回馈业务,才能在快速发展的互联网领域保持持续进
步。不过我们也看到有些传统企业投身互联网,在业务问题还没有理清
楚的时候就从外面挖来许多技术高手,仿照成功的互联网公司打造技术
平台,这无疑是南辕北辙,缘木求鱼。而这些技术高手离开了它们熟悉
的环境和工作模式,也是张飞拿着绣花针使不上劲来。
1.4 网站架构设计误区
在大型网站架构发展过程中有如下几个容易出现的误区。
1.4.1 一味追随大公司的解决方案
由于大公司巨大成功的光环效应,再加上从大公司挖来的技术高手
的影响,网站在讨论架构决策时,最有说服力的一句话就成了“淘宝就
是这么搞的”或者“Facebook就是这么搞的”。
大公司的经验和成功模式固然重要,值得学习借鉴,但如果因此而
变得盲从,就失去了坚持自我的勇气,在架构演化的道路上迟早会迷
路。
1.4.2 为了技术而技术
网站技术是为业务而存在的,除此毫无意义。在技术选型和架构设
计中,脱离网站业务发展的实际,一味追求时髦的新技术,可能会将网
站技术发展引入崎岖小道,架构之路越走越难。
1.4.3 企图用技术解决所有问题
最典型的例子就是2012年年初12306故障事件后,软件开发技术界
的反应。各路专业和非专业人士众说纷纭地帮12306的技术架构出谋划策,甚至有人提议帮12306写一个开源的网站,解决其大规模并发访问的问
题。
12306真正的问题其实不在于它的技术架构,而在于它的业务架
构:12306根本就不应该在几亿中国人一票难求的情况下以窗口售票的
模式在网上售票(零点开始出售若干天后的车票)。12306需要重构的
不仅是它的技术架构,更重要的是它的业务架构:调整业务需求,换一
种方式卖票,而不要去搞促销秒杀这种噱头式的游戏。
后来证明12306确实是朝这个方向发展的:在售票方式上引入了排
队机制、整点售票调整为分时段售票。其实如果能控制住并发访问的
量,很多棘手的技术问题也就不是什么问题了。
技术是用来解决业务问题的,而业务的问题,也可以通过业务的手
段去解决。
1.5 小结
时至今日,大型网站的架构演化方案已经非常成熟,各种技术方案
也逐渐产品化。许多小型网站已经慢慢不需要再经历大型网站经历过的
架构演化之路就可以逐步发展壮大,因为现在越来越多的网站从建立之
初就是搭建在大型网站提供的云计算服务基础之上,所需要的一切技术
资源:计算、存储、网络都可以按需购买,线性伸缩,不需要自己一点
一点地拼凑各种资源,综合使用各种技术方案逐步去完善自己的网站架
构了。
所以能亲身经历一个网站从小到大的架构演化过程的网站架构师越来越少,虽然过去有这种经历的架构师也很少(从小型网站发展成大型
网站的机会本来就极少),但是将来可能真就没有了。
但也正因为网站架构技术演化过程难以重现,所以网站架构师更应
该对这个过程深刻了解,理解已成熟的网站架构技术方案的来龙去脉和
历史渊源,在技术选型和架构决策时才能有的放矢,直击要害。2 大型网站架构模式
关于什么是模式,这个来自建筑学的词汇是这样定义的:“每一个
模式描述了一个在我们周围不断重复发生的问题及该问题解决方案的
核心。这样,你就能一次又一次地使用该方案而不必做重复工作”。
模式的关键在于模式的可重复性,问题与场景的可重复性带来解决方案
的可重复使用。
我们的现实生活中充斥着几乎千篇一律的人生架构模
式:读重点学校,选热门专业,进稳定高收入的政府部门和
企业,找门当户对的配偶,生一个听话的孩子继续这个模
式……但是人生不同于软件,精彩的人生绝不会来自于复
制。
也许互联网产品不是随便复制就能成功的,创新的产品更能为用户
创造价值。但是网站架构却有一些共同的模式,这些模式已经被许多大
型网站一再验证,通过对这些模式的学习,我们可以掌握大型网站架构
的一般思路和解决方案,以指导我们的架构设计。
2.1 网站架构模式
为了解决大型网站面临的高并发访问、海量数据处理、高可靠运行
等一系列问题与挑战,大型互联网公司在实践中提出了许多解决方案,以实现网站高性能、高可用、易伸缩、可扩展、安全等各种技术架构目标。这些解决方案又被更多网站重复使用,从而逐渐形成大型网站架构
模式。
2.1.1 分层
分层是企业应用系统中最常见的一种架构模式,将系统在横向维度
上切分成几个部分,每个部分负责一部分相对比较单一的职责,然后通
过上层对下层的依赖和调用组成一个完整的系统。
分层结构在计算机世界中无处不在,网络的7层通信协议是一种分
层结构;计算机硬件、操作系统、应用软件也可以看作是一种分层结
构。在大型网站架构中也采用分层结构,将网站软件系统分为应用层、服务层、数据层,如表2.1所示。
表2.1 网站分层架构
通过分层,可以更好地将一个庞大的软件系统切分成不同的部分,便于分工合作开发和维护;各层之间具有一定的独立性,只要维持调用
接口不变,各层可以根据具体问题独立演化发展而不需要其他层必须做
出相应调整。
但是分层架构也有一些挑战,就是必须合理规划层次边界和接口,在开发过程中,严格遵循分层架构的约束,禁止跨层次的调用(应用层
直接调用数据层)及逆向调用(数据层调用服务层,或者服务层调用应
用层)。在实践中,大的分层结构内部还可以继续分层,如应用层可以再细
分为视图层(美工负责)和业务逻辑层(工程师负责);服务层也可以
细分为数据接口层(适配各种输入和输出的数据格式)和逻辑处理层。
分层架构是逻辑上的,在物理部署上,三层结构可以部署在同一个
物理机器上,但是随着网站业务的发展,必然需要对已经分层的模块分
离部署,即三层结构分别部署在不同的服务器上,使网站拥有更多的计
算资源以应对越来越多的用户访问。
所以虽然分层架构模式最初的目的是规划软件清晰的逻辑结构便于
开发维护,但在网站的发展过程中,分层结构对网站支持高并发向分布
式方向发展至关重要。因此在网站规模还很小的时候就应该采用分层的
架构,这样将来网站做大时才能有更好地应对。
2.1.2 分割
如果说分层是将软件在横向方面进行切分,那么分割就是在纵向方
面对软件进行切分。
网站越大,功能越复杂,服务和数据处理的种类也越多,将这些不
同的功能和服务分割开来,包装成高内聚低耦合的模块单元,一方面有
助于软件的开发和维护;另一方面,便于不同模块的分布式部署,提高
网站的并发处理能力和功能扩展能力。
大型网站分割的粒度可能会很小。比如在应用层,将不同业务进行
分割,例如将购物、论坛、搜索、广告分割成不同的应用,由独立的团
队负责,部署在不同的服务器上;在同一个应用内部,如果规模庞大业
务复杂,会继续进行分割,比如购物业务,可以进一步分割成机票酒店
业务、3C业务,小商品业务等更细小的粒度。而即使在这个粒度上,还是可以继续分割成首页、搜索列表、商品详情等模块,这些模块不管在
逻辑上还是物理部署上,都可以是独立的。同样在服务层也可以根据需
要将服务分割成合适的模块。
2.1.3 分布式
对于大型网站,分层和分割的一个主要目的是为了切分后的模块便
于分布式部署,即将不同模块部署在不同的服务器上,通过远程调用协
同工作。分布式意味着可以使用更多的计算机完成同样的功能,计算机
越多,CPU、内存、存储资源也就越多,能够处理的并发访问和数据量
就越大,进而能够为更多的用户提供服务。
但分布式在解决网站高并发问题的同时也带来了其他问题。首先,分布式意味着服务调用必须通过网络,这可能会对性能造成比较严重的
影响;其次,服务器越多,服务器宕机的概率也就越大,一台服务器宕
机造成的服务不可用可能会导致很多应用不可访问,使网站可用性降
低;另外,数据在分布式的环境中保持数据一致性也非常困难,分布式
事务也难以保证,这对网站业务正确性和业务流程有可能造成很大影
响;分布式还导致网站依赖错综复杂,开发管理维护困难。因此分布式
设计要根据具体情况量力而行,切莫为了分布式而分布式。
在网站应用中,常用的分布式方案有以下几种。
分布式应用和服务: 将分层和分割后的应用和服务模块分布式部
署,除了可以改善网站性能和并发性、加快开发和发布速度、减少数据
库连接资源消耗外;还可以使不同应用复用共同的服务,便于业务功能
扩展。
分布式静态资源: 网站的静态资源如JS,CSS,Logo图片等资源独立分布式部署,并采用独立的域名,即人们常说的动静分离。静态资
源分布式部署可以减轻应用服务器的负载压力;通过使用独立域名加快
浏览器并发加载的速度;由负责用户体验的团队进行开发维护有利于网
站分工合作,使不同技术工种术业有专攻。
分布式数据和存储: 大型网站需要处理以P为单位的海量数据,单
台计算机无法提供如此大的存储空间,这些数据需要分布式存储。除了
对传统的关系数据库进行分布式部署外,为网站应用而生的各种NoSQL
产品几乎都是分布式的。
分布式计算: 严格说来,应用、服务、实时数据处理都是计算,网站除了要处理这些在线业务,还有很大一部分用户没有直观感受的后
台业务要处理,包括搜索引擎的索引构建、数据仓库的数据分析统计
等。这些业务的计算规模非常庞大,目前网站普遍使用Hadoop及其
MapReduce分布式计算框架进行此类批处理计算,其特点是移动计算而
不是移动数据,将计算程序分发到数据所在的位置以加速计算和分布式
计算。
此外,还有可以支持网站线上服务器配置实时更新的分布式配置;
分布式环境下实现并发和协同的分布式锁; 支持云存储的分布式文件
系统等。
2.1.4 集群
使用分布式虽然已经将分层和分割后的模块独立部署,但是对于用
户访问集中的模块(比如网站的首页),还需要将独立部署的服务器集
群化,即多台服务器部署相同应用构成一个集群,通过负载均衡设备共
同对外提供服务。因为服务器集群有更多服务器提供相同服务,因此可以提供更好的
并发特性,当有更多用户访问的时候,只需要向集群中加入新的机器即
可。同时因为一个应用由多台服务器提供,当某台服务器发生故障时,负载均衡设备或者系统的失效转移机制会将请求转发到集群中其他服务
器上,使服务器故障不影响用户使用。所以在网站应用中,即使是访问
量很小的分布式应用和服务,也至少要部署两台服务器构成一个小的集
群,目的就是提高系统的可用性。
2.1.5 缓存
缓存就是将数据存放在距离计算最近的位置以加快处理速度。缓存
是改善软件性能的第一手段,现代CPU越来越快的一个重要因素就是使
用了更多的缓存,在复杂的软件设计中,缓存几乎无处不在。大型网站
架构设计在很多方面都使用了缓存设计。
CDN: 即内容分发网络,部署在距离终端用户最近的网络服务
商,用户的网络请求总是先到达他的网络服务商那里,在这里缓存网站
的一些静态资源(较少变化的数据),可以就近以最快速度返回给用
户,如视频网站和门户网站会将用户访问量大的热点内容缓存在CDN。
反向代理: 反向代理属于网站前端架构的一部分,部署在网站的
前端,当用户请求到达网站的数据中心时,最先访问到的就是反向代理
服务器,这里缓存网站的静态资源,无需将请求继续转发给应用服务器
就能返回给用户。
本地缓存: 在应用服务器本地缓存着热点数据,应用程序可以在
本机内存中直接访问数据,而无需访问数据库。
分布式缓存: 大型网站的数据量非常庞大,即使只缓存一小部分,需要的内存空间也不是单机能承受的,所以除了本地缓存,还需要
分布式缓存,将数据缓存在一个专门的分布式缓存集群中,应用程序通
过网络通信访问缓存数据。
使用缓存有两个前提条件,一是数据访问热点不均衡,某些数据会
被更频繁的访问,这些数据应该放在缓存中;二是数据在某个时间段内
有效,不会很快过期,否则缓存的数据就会因已经失效而产生脏读,影
响结果的正确性。网站应用中,缓存除了可以加快数据访问速度,还可
以减轻后端应用和数据存储的负载压力,这一点对网站数据库架构至关
重要,网站数据库几乎都是按照有缓存的前提进行负载能力设计的。
2.1.6 异步
计算机软件发展的一个重要目标和驱动力是降低软件耦合性。事物
之间直接关系越少,就越少被彼此影响,越可以独立发展。大型网站架
构中,系统解耦合的手段除了前面提到的分层、分割、分布等,还有一
个重要手段是异步,业务之间的消息传递不是同步调用,而是将一个业
务操作分成多个阶段,每个阶段之间通过共享数据的方式异步执行进行
协作。
在单一服务器内部可通过多线程共享内存队列的方式实现异步,处
在业务操作前面的线程将输出写入到队列,后面的线程从队列中读取数
据进行处理;在分布式系统中,多个服务器集群通过分布式消息队列实
现异步,分布式消息队列可以看作内存队列的分布式部署。
异步架构是典型的生产者消费者模式,两者不存在直接调用,只要
保持数据结构不变,彼此功能实现可以随意变化而不互相影响,这对网
站扩展新功能非常便利。除此之外,使用异步消息队列还有如下特性。提高系统可用性。 消费者服务器发生故障,数据会在消息队列服
务器中存储堆积,生产者服务器可以继续处理业务请求,系统整体表现
无故障。消费者服务器恢复正常后,继续处理消息队列中的数据。
加快网站响应速度。 处在业务处理前端的生产者服务器在处理完
业务请求后,将数据写入消息队列,不需要等待消费者服务器处理就可
以返回,响应延迟减少。
消除并发访问高峰。 用户访问网站是随机的,存在访问高峰和低
谷,即使网站按照一般访问高峰进行规划和部署,也依然会出现突发事
件,比如购物网站的促销活动,微博上的热点事件,都会造成网站并发
访问突然增大,这可能会造成整个网站负载过重,响应延迟,严重时甚
至会出现服务宕机的情况。使用消息队列将突然增加的访问请求数据放
入消息队列中,等待消费者服务器依次处理,就不会对整个网站负载造
成太大压力。
但需要注意的是,使用异步方式处理业务可能会对用户体验、业务
流程造成影响,需要网站产品设计方面的支持。
2.1.7 冗余
网站需要7724小时连续运行,但是服务器随时可能出现故障,特别
是服务器规模比较大时,出现某台服务器宕机是必然事件。要想保证在
服务器宕机的情况下网站依然可以继续服务,不丢失数据,就需要一定
程度的服务器冗余运行,数据冗余备份,这样当某台服务器宕机时,可
以将其上的服务和数据访问转移到其他机器上。
访问和负载很小的服务也必须部署至少两台服务器构成一个集群,其目的就是通过冗余实现服务高可用。数据库除了定期备份,存档保存,实现冷备份 外,为了保证在线业务高可用,还需要对数据库进行
主从分离,实时同步实现热备份 。
为了抵御地震、海啸等不可抗力导致的网站完全瘫痪,某些大型网
站会对整个数据中心进行备份,全球范围内部署灾备数据中心 。网站
程序和数据实时同步到多个灾备数据中心。
2.1.8 自动化
在无人值守的情况下网站可以正常运行,一切都可以自动化是网站
的理想状态。目前大型网站的自动化架构设计主要集中在发布运维方
面。
发布对网站都是头等大事,许多网站故障出在发布环节,网站工程
师经常加班也是因为发布不顺利。通过减少人为干预,使发布过程自动
化 可有效减少故障。发布过程包括诸多环节。自动化代码管理 ,代码
版本控制、代码分支创建合并等过程自动化,开发工程师只要提交自己
参与开发的产品代号,系统就会自动为其创建开发分支,后期会自动进
行代码合并;自动化测试 ,代码开发完成,提交测试后,系统自动将
代码部署到测试环境,启动自动化测试用例进行测试,向相关人员发送
测试报告,向系统反馈测试结果;自动化安全检测 ,安全检测工具通
过对代码进行静态安全扫描及部署到安全测试环境进行安全攻击测试,评估其安全性;最后进行自动化部署 ,将工程代码自动部署到线上生
产环境。
此外,网站在运行过程中可能会遇到各种问题:服务器宕机、程序
Bug、存储空间不足、突然爆发的访问高峰。网站需要对线上生产环境
进行自动化监控 ,对服务器进行心跳检测,并监控其各项性能指标和应用程序的关键数据指标。如果发现异常、超出预设的阈值,就进行自
动化报警 ,向相关人员发送报警信息,警告故障可能会发生。在检测
到故障发生后,系统会进行自动化失效转移 ,将失效的服务器从集群
中隔离出去,不再处理系统中的应用请求。待故障消除后,系统进行自
动化失效恢复 ,重新启动服务,同步数据保证数据的一致性。在网站
遇到访问高峰,超出网站最大处理能力时,为了保证整个网站的安全可
用,还会进行自动化降级 ,通过拒绝部分请求及关闭部分不重要的服
务将系统负载降至一个安全的水平,必要时,还需要自动化分配资源
,将空闲资源分配给重要的服务,扩大其部署规模。
2.1.9 安全
互联网的开放特性使得其从诞生起就面对巨大的安全挑战,网站在
安全架构方面也积累了许多模式:通过密码 和手机校验码 进行身份认
证;登录、交易等操作需要对网络通信进行加密 ,网站服务器上存储
的敏感数据如用户信息等也进行加密处理;为了防止机器人程序滥用网
络资源攻击网站,网站使用验证码 进行识别;对于常见的用于攻击 网
站的XSS攻击、SQL注入、进行编码转换等相应处理;对于垃圾信息、敏感信息进行过滤 ;对交易转账等重要操作根据交易模式和交易信息
进行风险控制 。
2.2 架构模式在新浪微博的应用
短短几年时间新浪微博的用户数就从零增长到数亿,明星用户的粉
丝数达数千万,围绕着新浪微博正在发展一个集社交、媒体、游戏、电
商等多位一体的生态系统。
同大多数网站一样,新浪微博也是从一个小网站发展起来的。简单的LAMP(Linux+Apache+MySQL+PHP)架构,支撑起最初的新浪
微博,应用程序用PHP开发,所有的数据,包括微博、用户、关系都存
储在MySQL数据库中。
这样简单的架构无法支撑新浪微博快速发展的业务需求,随着访问
用户的逐渐增加,系统不堪重负。新浪微博的架构在较短时间内几经重
构,最后形成现在的架构,如图2.1所示。
图2.1 新浪微博的系统架构
(图片来源:http:timyang.netarchitectureweibo)
系统分为三个层次 ,最下层是基础服务层,提供数据库、缓存、存储、搜索等数据服务,以及其他一些基础技术服务,这些服务支撑了
新浪微博的海量数据和高并发访问,是整个系统的技术基础。
中间层是平台服务和应用服务层,新浪微博的核心服务是微博、关
系和用户,它们是新浪微博业务大厦的支柱。这些服务被分割为独立的
服务模块,通过依赖调用和共享基础数据构成新浪微博的业务基础。最上层是API和新浪微博的业务层,各种客户端(包括Web网站)
和第三方应用,通过调用API集成到新浪微博的系统中,共同组成一个
生态系统。
这些被分层和分割后的业务模块与基础技术模块分布式 部署,每
个模块都部署在一组独立的服务器集群上 ,通过远程调用的方式进行
依赖访问。新浪微博在早期还使用过一种叫作MPSS(MultiPort Single
Server,单服务器多端口)的分布式集群部署方案,在集群中的多台服
务器上,每台都部署多个服务,每个服务使用不同的端口对外提供服
务,通过这种方式使得有限的服务器可以部署更多的服务实例,改善服
务的负载均衡和可用性。现在网站应用中常见的将物理机虚拟化成多个
虚拟机后,在虚拟机上部署应用的方案跟新浪微博的MPSS方案异曲同
工,只是更加简单,还能在不同虚拟机上使用相同的端口号。
在新浪微博的早期架构中,微博发布使用同步推模式,用户发表微
博后系统会立即将这条微博插入到数据库所有粉丝的订阅列表中,当用
户量比较大时,特别是明星用户发布微博时,会引起大量的数据库写操
作,超出数据库负载,系统性能急剧下降,用户响应延迟加剧。后来新
浪微博改用异步 推拉结合的模式,用户发表微博后系统将微博写入消
息队列后立即返回,用户响应迅速,消息队列消费者任务将微博推送给
所有当前在线粉丝的订阅列表中,非在线用户登录后再根据关注列表拉
取微博订阅列表。
由于微博频繁刷新,新浪微博使用多级缓存 策略,热门微博和明
星用户的微博缓存在所有的微博服务器上,在线用户的微博和近期微博
缓存在分布式缓存集群中,对于微博操作中最常见的“刷微博”操作,几
乎全部都是缓存访问操作,可以获得很好的系统性能。为了提高系统的整体可用性和性能,新浪微博启用了多个数据中
心。这些数据中心既是地区用户访问中心,用户可以就近访问最近的数
据中心以加快访问速度,改善系统性能;同时也是数据冗余 复制的灾
备中心,所有的用户和微博数据通过远程消息系统在不同的数据中心之
间同步,提高系统可用性。
同时,新浪微博还开发了一系列自动化 工具,包括自动化监控,自动化发布,自动化故障修复等,这些自动化工具还在持续开发中,以
改善运维水平提高系统可用性。
由于微博的开放特性,新浪微博也遇到了一系列的安全 挑战,垃
圾内容、僵尸粉、微博攻击从未停止,除了使用一般网站常见的安全策
略,新浪微博在开放平台上使用多级安全审核的策略以保护系统和用
户。
2.3 小结
在程序设计与架构设计领域,模式正变得越来越受人关注,许多人
寄希望通过模式一劳永逸地解决自己的问题。正确使用模式可以更好地
利用业界和前人的思想与实践,用更少的时间开发出更好的系统,使设
计者的水平也达到更高的境界。但是模式受其适用场景限制,对系统的
要求和约束也很多,不恰当地使用模式只会画虎不成反类犬,不但没有
解决原来的老问题,反而带来了更棘手的新问题。
好的设计绝对不是模仿,不是生搬硬套某个模式,而是对问题深刻
理解之上的创造与创新,即使是“微创新”,也是让人耳目一新的似曾相
识。山寨与创新的最大区别不在于是否抄袭,是否模仿,而在于对问题
和需求是否真正理解与把握。3 大型网站核心架构要素
关于什么是架构 ,一种比较通俗的说法是“最高层次的规划,难以
改变的决定” ,这些规划和决定奠定了事物未来发展的方向和最终的蓝
图。
从这个意义上说,人生规划也是一种架构。选什么学
校、学什么专业、进什么公司、找什么对象,过什么样的生
活,都是自己人生的架构。
具体到软件架构 ,维基百科是这样定义的:“有关软件整体结构与
组件的抽象描述,用于指导大型软件系统各个方面的设计” 。系统的
各个重要组成部分及其关系构成了系统的架构,这些组成部分可以是具
体的功能模块,也可以是非功能的设计与决策,他们相互关系组成一个
整体,共同构成了软件系统的架构。
一般说来,除了当前的系统功能需求外,软件架构还需要关注性
能、可用性、伸缩性、扩展性和安全性这5个架构要素,架构设计过程
中需要平衡这5个要素之间的关系以实现需求和架构目标,也可以通过
考察这些架构要素来衡量一个软件架构设计的优劣,判断其是否满足期
望。
3.1 性能
性能是网站的一个重要指标,除非是没得选择(比如只能到www.12306.cn这一个网站上买火车票),否则用户无法忍受一个响应缓
慢的网站。一个打开缓慢的网站会导致严重的用户流失,很多时候网站
性能问题是网站架构升级优化的触发器。可以说性能是网站架构设计的
一个重要方面,任何软件架构设计方案都必须考虑可能会带来的性能问
题。
也正是因为性能问题几乎无处不在,所以优化网站性能的手段也非
常多,从用户浏览器到数据库,影响用户请求的所有环节都可以进行性
能优化。
在浏览器端,可以通过浏览器缓存、使用页面压缩、合理布局页
面、减少Cookie传输等手段改善性能。
还可以使用CDN,将网站静态内容分发至离用户最近的网络服务商
机房,使用户通过最短访问路径获取数据。可以在网站机房部署反向代
理服务器,缓存热点文件,加快请求响应速度,减轻应用服务器负载压
力。
在应用服务器端,可以使用服务器本地缓存和分布式缓存,通过缓
存在内存中的热点数据处理用户请求,加快请求处理过程,减轻数据库
负载压力。
也可以通过异步操作将用户请求发送至消息队列等待后续任务处
理,而当前请求直接返回响应给用户。
在网站有很多用户高并发请求的情况下,可以将多台应用服务器组
成一个集群共同对外服务,提高整体处理能力,改善性能。
在代码层面,也可以通过使用多线程、改善内存管理等手段优化性能。
在数据库服务器端,索引、缓存、SQL优化等性能优化手段都已经
比较成熟。而方兴未艾的NoSQL数据库通过优化数据模型、存储结构、伸缩特性等手段在性能方面的优势也日趋明显。
衡量网站性能有一系列指标,重要的有响应时间、TPS、系统性能
计数器等,通过测试这些指标以确定系统设计是否达到目标。这些指标
也是网站监控的重要参数,通过监控这些指标可以分析系统瓶颈,预测
网站容量,并对异常指标进行报警,保障系统可用性。
对于网站而言,性能符合预期仅仅是必要条件,因为无法预知网站
可能会面临的访问压力,所以必须要考察系统在高并发访问情况下,超
出负载设计能力的情况下可能会出现的性能问题。网站需要长时间持续
运行,还必须保证系统在持续运行且访问压力不均匀的情况下保持稳定
的性能特性。
3.2 可用性
对于大型网站而言,特别是知名网站,网站宕掉、服务不可用是一
个重大的事故,轻则影响网站声誉,重则可能会摊上官司。对于电子商
务类网站,网站不可用还意味着损失金钱和用户。因此几乎所有网站都
承诺7724可用,但事实上任何网站都不可能达到完全的7724可用,总会
有一些故障时间,扣除这些故障时间,就是网站的总可用时间,这个时
间可以换算成网站的可用性指标,以此衡量网站的可用性,一些知名大
型网站可以做到4个9以上的可用性,也就是可用性超过99.99%。
因为网站使用的服务器硬件通常是普通的商用服务器,这些服务器的设计目标本身并不保证高可用,也就是说,很有可能会出现服务器硬
件故障,也就是俗称的服务器宕机。大型网站通常都会有上万台服务
器,每天都必定会有一些服务器宕机,因此网站高可用架构设计的前提
是必然会出现服务器宕机,而高可用设计的目标就是当服务器宕机的时
候,服务或者应用依然可用。
网站高可用的主要手段是冗余,应用部署在多台服务器上同时提供
访问,数据存储在多台服务器上互相备份,任何一台服务器宕机都不会
影响应用的整体可用,也不会导致数据丢失。
对于应用服务器而言,多台应用服务器通过负载均衡设备组成一个
集群共同对外提供服务,任何一台服务器宕机,只需把请求切换到其他
服务器就可实现应用的高可用,但是一个前提条件是应用服务器上不能
保存请求的会话信息。否则服务器宕机,会话丢失,即使将用户请求转
发到其他服务器上也无法完成业务处理。
对于存储服务器,由于其上存储着数据,需要对数据进行实时备
份,当服务器宕机时需要将数据访问转移到可用的服务器上,并进行数
据恢复以保证继续有服务器宕机的时候数据依然可用。
除了运行环境,网站的高可用还需要软件开发过程的质量保证。通
过预发布验证、自动化测试、自动化发布、灰度发布等手段,减少将故
障引入线上环境的可能,避免故障范围扩大。
衡量一个系统架构设计是否满足高可用的目标,就是假设系统中任
何一台或者多台服务器宕机时,以及出现各种不可预期的问题时,系统
整体是否依然可用。
3.3 伸缩性大型网站需要面对大量用户的高并发访问和存储海量数据,不可能
只用一台服务器就处理全部用户请求,存储全部数据。网站通过集群的
方式将多台服务器组成一个整体共同提供服务。所谓伸缩性是指通过不
断向集群中加入服务器的手段来缓解不断上升的用户并发访问压力和不
断增长的数据存储需求。
衡量架构伸缩性的主要标准就是是否可以用多台服务器构建集群,是否容易向集群中添加新的服务器。加入新的服务器后是否可以提供和
原来的服务器无差别的服务。集群中可容纳的总的服务器数量是否有限
制。
对于应用服务器集群,只要服务器上不保存数据,所有服务器都是
对等的,通过使用合适的负载均衡设备就可以向集群中不断加入服务
器。
对于缓存服务器集群,加入新的服务器可能会导致缓存路由失效,进而导致集群中大部分缓存数据都无法访问。虽然缓存的数据可以通过
数据库重新加载,但是如果应用已经严重依赖缓存,可能会导致整个网
站崩溃。需要改进缓存路由算法保证缓存数据的可访问性。
关系数据库虽然支持数据复制,主从热备等机制,但是很难做到大
规模集群的可伸缩性,因此关系数据库的集群伸缩性方案必须在数据库
之外实现,通过路由分区等手段将部署有多个数据库的服务器组成一个
集群。
至于大部分NoSQL数据库产品,由于其先天就是为海量数据而生,因此其对伸缩性的支持通常都非常好,可以做到在较少运维参与的情况
下实现集群规模的线性伸缩。3.4 扩展性
不同于其他架构要素主要关注非功能性需求,网站的扩展性架构直
接关注网站的功能需求。网站快速发展,功能不断扩展,如何设计网站
的架构使其能够快速响应需求变化,是网站可扩展架构主要的目的。
衡量网站架构扩展性好坏的主要标准就是在网站增加新的业务产品
时,是否可以实现对现有产品透明无影响,不需要任何改动或者很少改
动既有业务功能就可以上线新产品。不同产品之间是否很少耦合,一个
产品改动对其他产品无影响,其他产品和功能不需要受牵连进行改动。
网站可伸缩架构的主要手段是事件驱动架构和分布式服务。
事件驱动架构在网站通常利用消息队列实现,将用户请求和其他业
务事件构造成消息发布到消息队列,消息的处理者作为消费者从消息队
列中获取消息进行处理。通过这种方式将消息产生和消息处理分离开
来,可以透明地增加新的消息生产者任务或者新的消息消费者任务。
分布式服务则是将业务和可复用服务分离开来,通过分布式服务框
架调用。新增产品可以通过调用可复用的服务实现自身的业务逻辑,而
对现有产品没有任何影响。可复用服务升级变更的时候,也可以通过提
供多版本服务对应用实现透明升级,不需要强制应用同步变更。
大型网站为了保持市场地位,还会吸引第三方开发者,调用网站服
务,使用网站数据开发周边产品,扩展网站业务。第三方开发者使用网
站服务的主要途径是大型网站提供的开放平台接口。
3.5 安全性互联网是开放的,任何人在任何地方都可以访问网站。网站的安全
架构就是保护网站不受恶意访问和攻击,保护网站的重要数据不被窃
取。
衡量网站安全架构的标准就是针对现存和潜在的各种攻击与窃密手
段,是否有可靠的应对策略。
3.6 小结
性能、可用性、伸缩性、扩展性和安全性是网站架构最核心的几个
要素,这几个问题解决了,大型网站架构设计的大部分挑战也就克服
了。因此本书第二篇即按这五个架构要素进行组织。
本章既可以看作本书第二篇的前情提要,同时也可以当做第二篇的
总结和归纳,阅读本章过程中如果有任何困惑都不必纠结,请直接跳
过,等读完全书后可以再回头重新回顾。第2篇 架构4 瞬时响应:网站的高性能架构
什么叫高性能的网站?
两个网站性能架构设计方案:A方案和B方案,A方案在小于100个
并发用户访问时,每个请求的响应时间是1秒,当并发请求达到200的时
候,请求的响应时间将骤增到10秒。B方案不管是100个并发用户访问还
是200个并发用户访问,每个请求的响应时间都差不多是1.5秒。哪个方
案的性能好?如果老板说“我们要改善网站的性能”,他指的是什么?
同类型的两个网站,X网站服务器平均每个请求的处理时间是500
毫秒,Y网站服务器平均每个请求的处理时间是1000毫秒,为什么用户
却反映Y网站的速度快呢?
网站性能是客观的指标,可以具体体现到响应时间、吞吐量等技术
指标,同时也是主观的感受,而感受则是一种与具体参与者相关的微妙
的东西,用户的感受和工程师的感受不同,不同的用户感受也不同。
4.1 网站性能测试
性能测试是性能优化的前提和基础,也是性能优化结果的检查和度
量标准。不同视角下的网站性能有不同的标准,也有不同的优化手段。
4.1.1 不同视角下的网站性能
软件工程师说到网站性能的时候,通常和用户说的不一样。
1.用户视角的网站性能从用户角度,网站性能就是用户在浏览器上直观感受到的网站响应
速度快还是慢。用户感受到的时间,包括用户计算机和网站服务器通信
的时间、网站服务器处理的时间、用户计算机浏览器构造请求解析响应
数据的时间,如图4.1所示。
图4.1 用户视角的网站性能
不同计算机的性能差异,不同浏览器解析HTML速度的差异,不同
网络运营商提供的互联网宽带服务的差异,这些差异最终导致用户感受
到的响应延迟可能会远远大于网站服务器处理请求需要的时间。
在实践中,使用一些前端架构优化手段,通过优化页面HTML式
样、利用浏览器端的并发和异步特性、调整浏览器缓存策略、使用CDN
服务、反向代理等手段,使浏览器尽快地显示用户感兴趣的内容、尽可
能近地获取页面内容,即使不优化应用程序和架构,也可以很大程度地
改善用户视角下的网站性能。
2.开发人员视角的网站性能
开发人员关注的主要是应用程序本身及其相关子系统的性能,包括
响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标。主要
的优化手段有使用缓存加速数据读取,使用集群提高吞吐能力,使用异
步消息加快请求响应及实现削峰,使用代码优化手段改善程序性能。
3.运维人员视角的网站性能运维人员更关注基础设施性能和资源利用率,如网络运营商的带宽
能力、服务器硬件的配置、数据中心网络架构、服务器和网络带宽的资
源利用率等。主要优化手段有建设优化骨干网、使用高性价比定制服务
器、利用虚拟化技术优化资源利用等。
4.1.2 性能测试指标
不同视角下有不同的性能标准,不同的标准有不同的性能测试指
标,从开发和测试人员的视角,网站性能测试的主要指标有响应时间、并发数、吞吐量、性能计数器等。
1.响应时间
指应用执行一个操作需要的时间,包括从发出请求开始到收到最后
响应数据所需要的时间。响应时间是系统最重要的性能指标,直观地反
映了系统的“快慢”。表4.1列出了一些常用的系统操作需要的响应时间。
表4.1 常用系统操作响应时间表
(部分数据来源:http:www.eecs.berkeley.edu~rcsresearchinteractive_latency.html)
测试程序通过模拟应用程序,记录收到响应和发出请求之间的时间差来计算系统响应时间。但是记录及获取系统时间这个操作也需要花费
一定的时间,如果测试目标操作本身需要花费的时间极少,比如几微
秒,那么测试程序就无法测试得到系统的响应时间。实践中通常采用的
办法是重复请求,比如一个请求操作重复执行一万次,测试一万次执行
需要的总响应时间之和,然后除以一万,得到单次请求的响应时间。
2.并发数
指系统能够同时处理请求的数目,这个数字也反映了系统的负载特
性。对于网站而言,并发数即网站并发用户数,指同时提交请求的用户
数目。
与网站并发用户数相对应的还有网站在线用户数(当前登录网站的
用户总数)和网站系统用户数(可能访问系统的总用户数,对多数网站
而言就是注册用户数)。其数量比较关系为:
网站系统用户数>>网站在线用户数>>网站并发用户数
在网站产品设计初期,产品经理和运营人员就需要规划不同发展阶
段的网站系统用户数,并以此为基础,根据产品特性和运营手段,推算
在线用户数和并发用户数。这些指标将成为系统非功能设计的重要依
据。
现实中,经常看到某些网站,特别是电商类网站,市场推广人员兴
致勃勃地打广告打折促销,用户兴致勃勃地去抢购,结果活动刚一开
始,就因为并发用户数超过网站最大负载而响应缓慢,急性子的用户不
停刷新浏览器,导致系统并发数更高,最后以服务器系统崩溃,用户浏
览器显示“Service is too busy”而告终。出现这种情况,有可能是网站技术准备不充分导致,也有可能是运营人员错误地评估并发用户数导致。
测试程序通过多线程模拟并发用户的办法来测试系统的并发处理能
力,为了真实模拟用户行为,测试程序并不是启动多线程然后不停地发
送请求,而是在两次请求之间加入一个随机等待时间,这个时间被称作
思考时间。
3.吞吐量
指单位时间内系统处理的请求数量,体现系统的整体处理能力。对
于网站,可以用“请求数秒”或是“页面数秒”来衡量,也可以用“访问人
数天”或是“处理的业务数小时”等来衡量。TPS(每秒事务数)是吞吐
量的一个常用量化指标,此外还有HPS(每秒HTTP请求数)、QPS(每
秒查询数)等。
在系统并发数由小逐渐增大的过程中(这个过程也伴随着服务器系
统资源消耗逐渐增大),系统吞吐量先是逐渐增加,达到一个极限后,随着并发数的增加反而下降,达到系统崩溃点后,系统资源耗尽,吞吐
量为零。
而这个过程中,响应时间则是先保持小幅上升,到达吞吐量极限
后,快速上升,到达系统崩溃点后,系统失去响应。系统吞吐量、系统
并发数及响应时间之间的关系将在本章后面内容中介绍。
系统吞吐量和系统并发数,以及响应时间的关系可以形象地理解为
高速公路的通行状况:吞吐量是每天通过收费站的车辆数目(可以换算
成收费站收取的高速费),并发数是高速公路上的正在行驶的车辆数
目,响应时间是车速。车辆很少时,车速很快,但是收到的高速费也相应较少;随着高速公路上车辆数目的增多,车速略受影响,但是收到的
高速费增加很快;随着车辆的继续增加,车速变得越来越慢,高速公路
越来越堵,收费不增反降;如果车流量继续增加,超过某个极限后,任
何偶然因素都会导致高速全部瘫痪,车走不动,费当然也收不着,而高
速公路成了停车场(资源耗尽)。
网站性能优化的目的,除了改善用户体验的响应时间,还要尽量提
高系统吞吐量,最大限度利用服务器资源。
4.性能计数器
它是描述服务器或操作系统性能的一些数据指标。包括System
Load、对象与线程数、内存使用、CPU使用、磁盘与网络IO等指标。
这些指标也是系统监控的重要参数,对这些指标设置报警阈值,当监控
系统发现性能计数器超过阈值时,就向运维和开发人员报警,及时发现
处理系统异常。
System Load即系统负载,指当前正在被CPU执行和等待被CPU执行
的进程数目总和,是反映系统忙闲程度的重要指标。多核CPU的情况
下,完美情况是所有CPU都在使用,没有进程在等待处理,所以Load的
理想值是CPU的数目。当Load值低于CPU数目的时候,表示CPU有空
闲,资源存在浪费;当Load值高于CPU数目的时候,表示进程在排队等
待CPU调度,表示系统资源不足,影响应用程序的执行性能。在Linux
系统中使用top命令查看,该值是三个浮点数,表示最近1分钟,10分
钟,15分钟的运行队列平均进程数。如图4.2所示。
图4.2 在Linux命令行查看系统负载4.1.3 性能测试方法
性能测试是一个总称,具体可细分为性能测试、负载测试、压力测
试、稳定性测试。
性能测试
以系统设计初期规划的性能指标为预期目标,对系统不断施加压
力,验证系统在资源可接受范围内,是否能达到性能预期。
负载测试
对系统不断地增加并发请求以增加系统压力,直到系统的某项或多
项性能指标达到安全临界值,如某种资源已经呈饱和状态,这时继续对
系统施加压力,系统的处理能力不但不能提高,反而会下降。
压力测试
超过安全负载的情况下,对系统继续施加压力,直到系统崩溃或不
能再处理任何请求,以此获得系统最大压力承受能力。
稳定性测试
被测试系统在特定硬件、软件、网络环境条件下,给系统加载一定
业务压力,使系统运行一段较长时间,以此检测系统是否稳定。在不同
生产环境、不同时间点的请求压力是不均匀的,呈波浪特性,因此为了
更好地模拟生产环境,稳定性测试也应不均匀地对系统施加压力。
性能测试是一个不断对系统增加访问压力,以获得系统性能指标、最大负载能力、最大压力承受能力的过程。所谓的增加访问压力,在系统测试环境中,就是不断增加测试程序的并发请求数,一般说来,性能
测试遵循如图4.3所示的抛物线规律。
图4.3 性能测试曲线
图4.3中的横坐标表示消耗的系统资源,纵坐标表示系统处理能力
(吞吐量)。在开始阶段,随着并发请求数目的增加,系统使用较少的
资源就达到较好的处理能力(a~b段),这一段是网站的日常运行区间
,网站的绝大部分访问负载压力都集中在这一段区间,被称作性能测
试,测试目标是评估系统性能是否符合需求及设计目标;随着压力的持
续增加,系统处理能力增加变缓,直到达到一个最大值(c点),这是
系统的最大负载点 ,这一段被称作负载测试。测试目标是评估当系统
因为突发事件超出日常访问压力的情况下,保证系统正常运行情况下能
够承受的最大访问负载压力;超过这个点后,再增加压力,系统的处理
能力反而下降,而资源消耗却更多,直到资源消耗达到极限(d点),这个点可以看作是系统的崩溃点 ,超过这个点继续加大并发请求数
目,系统不能再处理任何请求,这一段被称作压力测试,测试目标是评
估可能导致系统崩溃的最大访问负载压力。
性能测试反应的是系统在实际生产环境中使用时,随着用户并发访
问数量的增加,系统的处理能力。与性能曲线相对应的是用户访问的等待时间(系统响应时间),如图4.4所示。
图4.4 并发用户访问响应时间曲线
在日常运行区间,可以获得最好的用户响应时间,随着并发用户数
的增加,响应延迟越来越大,直到系统崩溃,用户失去响应。
4.1.4 性能测试报告
测试结果报告应能够反映上述性能测试曲线的规律,阅读者可以得
到系统性能是否满足设计目标和业务要求、系统最大负载能力、系统最
大压力承受能力等重要信息,表4.2是一个简单示例。
表4.2 性能测试结果报告
4.1.5 性能优化策略如果性能测试结果不能满足设计或业务需求,那么就需要寻找系统
瓶颈,分而治之,逐步优化。
1.性能分析
大型网站结构复杂,用户从浏览器发出请求直到数据库完成操作事
务,中间需要经过很多环节,如果测试或者用户报告网站响应缓慢,存
在性能问题,必须对请求经历的各个环节进行分析,排查可能出现性能
瓶颈的地方,定位问题。
排查一个网站的性能瓶颈和排查一个程序的性能瓶颈的手法基本相
同:检查请求处理的各个环节的日志,分析哪个环节响应时间不合理、超过预期;然后检查监控数据,分析影响性能的主要因素是内存、磁
盘、网络、还是CPU,是代码问题还是架构设计不合理,或者系统资源
确实不足。
2.性能优化
定位产生性能问题的具体原因后,就需要进行性能优化,根据网站
分层架构,可分为Web前端性能优化、应用服务器性能优化、存储服务
器性能优化3大类。
4.2 Web前端性能优化
一般说来Web前端指网站业务逻辑之前的部分,包括浏览器加载、网站视图模型、图片服务、CDN服务等,主要优化手段有优化浏览器访
问、使用反向代理、CDN等。4.2.1 浏览器访问优化
1.减少http请求
HTTP协议是无状态的应用层协议,意味着每次HTTP请求都需要建
立通信链路、进行数据传输,而在服务器端,每个HTTP都需要启动独
立的线程去处理。这些通信和服务的开销都很昂贵,减少HTTP请求的
数目可有效提高访问性能。
减少HTTP的主要手段是合并CSS、合并JavaScript、合并图片。将
浏览器一次访问需要的JavaScript、CSS合并成一个文件,这样浏览器就
只需要一次请求。图片也可以合并,多张图片合并成一张,如果每张图
片都有不同的超链接,可通过CSS偏移响应鼠标点击操作,构造不同的
URL。
2.使用浏览器缓存
对一个网站而言,CSS、JavaScript、Logo、图标这些静态资源文件
更新的频率都比较低,而这些文件又几乎是每次HTTP请求都需要的,如果将这些文件缓存在浏览器中,可以极好地改善性能。通过设置
HTTP头中Cache-Control和Expires的属性,可设定浏览器缓存,缓存时
间可以是数天,甚至是几个月。
在某些时候,静态资源文件变化需要及时应用到客户端浏览器,这
种情况,可通过改变文件名实现,即更新JavaScript文件并不是更新
JavaScript文件内容,而是生成一个新的JS文件并更新HTML文件中的引
用。使用浏览器缓存策略的网站在更新静态资源时,应采用批量更新的
方法,比如需要更新10个图标文件,不宜把10个文件一次全部更新,而
是应一个文件一个文件逐步更新,并有一定的间隔时间,以免用户浏览
器突然大量缓存失效,集中更新缓存,造成服务器负载骤增、网络堵塞
的情况。
3.启用压缩
在服务器端对文件进行压缩,在浏览器端对文件解压缩,可有效减
少通信传输的数据量。文本文件的压缩效率可达80%以上,因此
HTML、CSS、JavaScript文件启用GZip压缩可达到较好的效果。但是压
缩对服务器和浏览器产生一定的压力,在通信带宽良好,而服务器资源
不足的情况下要权衡考虑。
4.CSS放在页面最上面、JavaScript放在页面最下面
浏览器会在下载完全部CSS之后才对整个页面进行渲染,因此最好
的做法是将CSS放在页面最上面,让浏览器尽快下载CSS。JavaScript则
相反,浏览器在加载JavaScript后立即执行,有可能会阻塞整个页面,造
成页面显示缓慢,因此JavaScript最好放在页面最下面。但如果页面解析
时就需要用到JavaScript,这时放在底部就不合适了。
5.减少Cookie传输
一方面,Cookie包含在每次请求和响应中,太大的Cookie会严重影
响数据传输,因此哪些数据需要写入Cookie需要慎重考虑,尽量减少
Cookie中传输的数据量。另一方面,对于某些静态资源的访问,如
CSS、Script等,发送Cookie没有意义,可以考虑静态资源使用独立域名访问,避免请求静态资源时发送Cookie,减少Cookie传输的次数。
4.2.2 CDN加速
CDN(Content Distribute Network,内容分发网络)的本质仍然是
一个缓存,而且将数据缓存在离用户最近的地方,使用户以最快速度获
取数据,即所谓网络访问第一跳,如图4.5所示。
图4.5 利用CDN的网站架构
由于CDN部署在网络运营商的机房,这些运营商又是终端用户的网
络服务提供商,因此用户请求路由的第一跳就到达了CDN服务器,当
CDN中存在浏览器请求的资源时,从CDN直接返回给浏览器,最短路
径返回响应,加快用户访问速度,减少数据中心负载压力。
CDN能够缓存的一般是静态资源,如图片、文件、CSS、Script脚本、静态网页等,但是这些文件访问频度很高,将其缓存在CDN可极大
改善网页的打开速度。
4.2.3 反向代理
传统代理服务器位于浏览器一侧,代理浏览器将HTTP请求发送到
互联网上,而反向代理服务器位于网站机房一侧,代理网站Web服务器
接收HTTP请求。如图4.6所示。
图4.6 利用反向代理的网站架构
和传统代理服务器可以保护浏览器安全一样,反向代理服务器也具
有保护网站安全的作用,来自互联网的访问请求必须经过代理服务器,相当于在Web服务器和可能的网络攻击之间建立了一个屏障。
除了安全功能,代理服务器也可以通过配置缓存功能加速Web请
求。当用户第一次访问静态内容的时候,静态内容就被缓存在反向代理
服务器上,这样当其他用户访问该静态内容的时候,就可以直接从反向
代理服务器返回,加速Web请求响应速度,减轻Web服务器负载压力。事实上,有些网站会把动态内容也缓存在代理服务器上,比如维基百科
及某些博客论坛网站,把热门词条、帖子、博客缓存在反向代理服务器
上加速用户访问速度,当这些动态内容有变化时,通过内部通知机制通
知反向代理缓存失效,反向代理会重新加载最新的动态内容再次缓存起
来。
此外,反向代理也可以实现负载均衡的功能,而通过负载均衡构建
的应用集群可以提高系统总体处理能力,进而改善网站高并发情况下的
性能。
4.3 应用服务器性能优化
应用服务器就是处理网站业务的服务器,网站的业务代码都部署在
这里,是网站开发最复杂,变化最多的地方,优化手段主要有缓存、集
群、异步等。
4.3.1 分布式缓存
回顾网站架构演化历程,当网站遇到性能瓶颈时,第一个想到的解
决方案就是使用缓存。在整个网站应用中,缓存几乎无所不在,既存在
于浏览器,也存在于应用服务器和数据库服务器;既可以对数据缓存,也可以对文件缓存,还可以对页面片段缓存。合理使用缓存,对网站性
能优化意义重大。
网站性能优化第一定律:优先考虑使用缓存优化性能。1.缓存的基本原理
缓存指将数据存储在相对较高访问速度的存储介质中,以供系统处
理。一方面缓存访问速度快,可以减少数据访问的时间,另一方面如果
缓存的数据是经过计算处理得到的,那么被缓存的数据无需重复计算即
可直接使用,因此缓存还起到减少计算时间的作用。
缓存的本质是一个内存Hash表,网站应用中,数据缓存以一对
Key、Value的形式存储在内存Hash表中。Hash表数据读写的时间复杂度
为O(1),图4.7为一对KV在Hash表中的存储。
图4.7 Hash表存储例子
计算KV对中Key的HashCode对应的Hash表索引,可快速访问Hash
表中的数据。许多语言支持获得任意对象的HashCode,可以把
HashCode理解为对象的唯一标示符,Java语言中Hashcode方法包含在根
对象Object中,其返回值是一个Int。然后通过Hashcode计算Hash表的索
引下标,最简单的是余数法,使用Hash表数组长度对Hashcode求余,余
数即为Hash表索引,使用该索引可直接访问得到Hash表中存储的KV
对。Hash表是软件开发中常用到的一种数据结构,其设计思想在很多场
景下都可以应用。
缓存主要用来存放那些读写比很高、很少变化的数据,如商品的类目信息,热门词的搜索列表信息,热门商品信息等。应用程序读取数据
时,先到缓存中读取,如果读取不到或数据已失效,再访问数据库,并
将数据写入缓存,如图4.8所示。
图4.8 使用缓存存取数据
网站数据访问通常遵循二八定律,即80%的访问落在20%的数据
上,因此利用Hash表和内存的高速访问特性,将这20%的数据缓存起
来,可很好地改善系统性能,提高数据读取速度,降低存储访问压力。
2.合理使用缓存
使用缓存对提高系统性能有很多好处,但是不合理使用缓存非但不
能提高系统的性能,还会成为系统的累赘,甚至风险。实践中,缓存滥
用的情景屡见不鲜——过分依赖低可用的缓存系统、不恰当地使用缓存
的数据访问特性等。
频繁修改的数据
如果缓存中保存的是频繁修改的数据,就会出现数据写入缓存后,应用还来不及读取缓存,数据就已失效的情形,徒增系统负担。一般说来,数据的读写比在2:1以上,即写入一次缓存,在数据更新前至少读
取两次,缓存才有意义。实践中,这个读写比通常非常高,比如新浪微
博的热门微博,缓存以后可能会被读取数百万次。
没有热点的访问
缓存使用内存作为存储,内存资源宝贵而有限,不可能将所有数据
都缓存起来,只能将最新访问的数据缓存起来,而将历史数据清理出缓
存。如果应用系统访问数据没有热点,不遵循二八定律,即大部分数据
访问并没有集中在小部分数据上,那么缓存就没有意义,因为大部分数
据还没有被再次访问就已经被挤出缓存了。
数据不一致与脏读
一般会对缓存的数据设置失效时间,一旦超过失效时间,就要从数
据库中重新加载。因此应用要容忍一定时间的数据不一致,如卖家已经
编辑了商品属性,但是需要过一段时间才能被买家看到。在互联网应用
中,这种延迟通常是可以接受的,但是具体应用仍需慎重对待。还有一
种策略是数据更新时立即更新缓存,不过这也会带来更多系统开销和事
务一致性的问题。
缓存可用性
缓存是为提高数据读取性能的,缓存数据丢失或者缓存不可用不会
影响到应用程序的处理——它可以从数据库直接获取数据。但是随着业
务的发展,缓存会承担大部分数据访问的压力,数据库已经习惯了有缓
存的日子,所以当缓存服务崩溃时,数据库会因为完全不能承受如此大
的压力而宕机,进而导致整个网站不可用。这种情况被称作缓存雪崩,发生这种故障,甚至不能简单地重启缓存服务器和数据库服务器来恢复网站访问。
实践中,有的网站通过缓存热备等手段提高缓存可用性:当某台缓
存服务器宕机时,将缓存访问切换到热备服务器上。但是这种设计显然
有违缓存的初衷,缓存根本就不应该被当做一个可靠的数据源来使用。
通过分布式缓存服务器集群,将缓存数据分布到集群多台服务器上
可在一定程度上改善缓存的可用性。当一台缓存服务器宕机的时候,只
有部分缓存数据丢失,重新从数据库加载这部分数据不会对数据库产生
很大影响。
产品在设计之初就需要一个明确的定位:什么是产品要
实现的功能,什么不是产品提供的特性。在产品漫长的生命
周期中,会有形形色色的困难和诱惑来改变产品的发展方
向,左右摇摆、什么都想做的产品,最后有可能成为一个失
去生命力的四不像。
缓存预热
缓存中存放的是热点数据,热点数据又是缓存系统利用LRU(最近
最久未用算法)对不断访问的数据筛选淘汰出来的,这个过程需要花费
较长的时间。新启动的缓存系统如果没有任何数据,在重建缓存数据的
过程中,系统的性能和数据库负载都不太好,那么最好在缓存系统启动
时就把热点数据加载好,这个缓存预加载手段叫作缓存预热(warm
up)。对于一些元数据如城市地名列表、类目信息,可以在启动时加载
数据库中全部数据到缓存进行预热。缓存穿透
如果因为不恰当的业务、或者恶意攻击持续高并发地请求某个不存
在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成很大压力,甚至崩溃。一个简单的对策是将不存在的数
据也缓存起来(其value值为null)。
3.分布式缓存架构
分布式缓存指缓存部署在多个服务器组成的集群中,以集群方式提
供缓存服务,其架构方式有两种,一种是以JBoss Cache为代表的需要更
新同步的分布式缓存,一种是以Memcached为代表的不互相通信的分布
式缓存。
JBoss Cache的分布式缓存在集群中所有服务器中保存相同的缓存数
据,当某台服务器有缓存数据更新的时候,会通知集群中其他机器更新
缓存数据或清除缓存数据,如图4.9所示。JBoss Cache通常将应用程序
和缓存部署在同一台服务器上,应用程序可从本地快速获取缓存数据,但是这种方式带来的问题是缓存数据的数量受限于单一服务器的内存空
间,而且当集群规模较大的时候,缓存更新信息需要同步到集群所有机
器,其代价惊人。因而这种方案更多见于企业应用系统中,而很少在大
型网站使用。图4.9 需要更新同步的JBoss Cache
大型网站需要缓存的数据量一般都很庞大,可能会需要数TB的内
存做缓存,这时候就需要另一种分布式缓存,如图4.10所示。
Memcached采用一种集中式的缓存集群管理,也被称作互不通信的分布
式架构方式。缓存与应用分离部署,缓存系统部署在一组专门的服务器
上,应用程序通过一致性Hash等路由算法选择缓存服务器远程访问缓存
数据,缓存服务器之间不通信,缓存集群的规模可以很容易地实现扩
容,具有良好的可伸缩性。
图4.10 不互相通信的Memcached
Memcached的伸缩性设计参考本书第6章内容。
4.Memcached
Memcached曾一度是网站分布式缓存的代名词,被大量网站使用。其简单的设计、优异的性能、互不通信的服务器集群、海量数据可伸缩
的架构令网站架构师们趋之若鹜。
简单的通信协议
远程通信设计需要考虑两方面的要素,一是通信协议,即选择TCP
协议还是UDP协议,抑或HTTP协议;一是通信序列化协议,数据传输
的两端,必须使用彼此可识别的数据序列化方式才能使通信得以完成,如XML、JSON等文本序列化协议,或者Google Protobuffer等二进制序
列化协议。Memcached使用TCP协议(UDP也支持)通信,其序列化协
议则是一套基于文本的自定义协议,非常简单,以一个命令关键字开
头,后面是一组命令操作数。例如读取一个数据的命令协议是get
议。
丰富的客户端程序
Memcached通信协议非常简单,只要支持该协议的客户端都可以和
Memcached服务器通信,因此Memcached发展出非常丰富的客户端程
序,几乎支持所有主流的网站编程语言,Java、CC++C、Perl、Python、PHP、Ruby等,因此在混合使用多种编程语言的网站,Memcached更是如鱼得水。
高性能的网络通信
Memcached服务端通信模块基于Libevent,一个支持事件触发的网
络通信程序库。Libevent的设计和实现有许多值得改善的地方,但它在
稳定的长连接方面的表现却正是Memcached需要的。高效的内存管理
内存管理中一个令人头痛的问题就是内存碎片管理。操作系统、虚
拟机垃圾回收在这方面想了许多办法:压缩、复制等。Memcached使用
了一个非常简单的办法——固定空间分配。Memcached将内存空间分为
一组slab,每个slab里又包含一组chunk,同一个slab里的每个chunk的大
小是固定的,拥有相同大小chunk的slab被组织在一起,叫作slab_class,如图4.11所示。存储数据时根据数据的Size大小,寻找一个大于Size的最
小chunk将数据写入。这种内存管理方式避免了内存碎片管理的问题,内存的分配和释放都是以chunk为单位的。和其他缓存一样,Memcached采用LRU算法释放最近最久未被访问的数据占用的空间,释
放的chunk被标记为未用,等待下一个合适大小数据的写入。
图4.11 Memcached内存管理
当然这种方式也会带来内存浪费的问题。数据只能存入一个比它大
的chunk里,而一个chunk只能存一个数据,其他空间被浪费了。如果启
动参数配置不合理,浪费会更加惊人,发现没有缓存多少数据,内存空
间就用尽了。互不通信的服务器集群架构
如上所述,正是这个特性使得Memcached从JBoss Cache、OSCache
等众多分布式缓存产品中脱颖而出,满足网站对海量缓存数据的需求。
而其客户端路由算法一致性Hash更成为数据存储伸缩性架构设计的经典
范式(参考本书第6章)。事实上,正是集群内服务器互不通信使得集
群可以做到几乎无限制的线性伸缩,这也正是目前流行的许多大数据技
术的基本架构特点。
虽然近些年许多NoSQL产品层出不穷,在数据持久化、支持复杂数
据结构、甚至性能方面有许多产品优于Memcached,但Memcached由于
其简单、稳定、专注的特点,仍然在分布式缓存领域占据着重要地位。
4.3.2 异步操作
使用消息队列将调用异步化,可改善网站的扩展性(参考本书第7
章内容)。事实上,使用消息队列还可改善网站系统的性能,如图4.12
和图4.13所示。
图4.12 不使用消息队列服务器
图4.13 使用消息队列服务器
在不使用消息队列的情况下,用户的请求数据直接写入数据库,在高并发的情况下,会对数据库造成巨大的压力,同时也使得响应延迟加
剧。在使用消息队列后,用户请求的数据发送给消息队列后立即返回,再由消息队列的消费者进程(通常情况下,该进程通常独立部署在专门
的服务器集群上)从消息队列中获取数据,异步写入数据库。由于消息
队列服务器处理速度远快于数据库(消息队列服务器也比数据库具有更
好的伸缩性),因此用户的响应延迟可得到有效改善。
消息队列具有很好的削峰作用——即通过异步处理,将短时间高并
发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。在
电子商务网站促销活动中,合理使用消息队列,可有效抵御促销活动刚
开始大量涌入的订单对系统造成的冲击。如图4.14所示。
图4.14 使用消息队列消除并发访问高峰
需要注意的是,由于数据写入消息队列后立即返回给用户,数据在
后续的业务校验、写数据库等操作可能失败,因此在使用消息队列进行
业务异步处理后,需要适当修改业务流程进行配合,如订单提交后,订
单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队
列的订单消费者进程真正处理完该订单,甚至商品出库后,再通过电子
邮件或SMS消息通知用户订单成功,以免交易纠纷。任何可以晚点做的事情都应该晚点再做。
4.3.3 使用集群
在网站高并发访问的场景下,使用负载均衡技术为一个应用构建一
个由多台服务器组成的服务器集群,将并发访问请求分发到多台服务器
上处理,避免单一服务器因负载压力过大而响应缓慢,使用户请求具有
更好的响应延迟特性,如图4.15所示。
图4.15 利用负载均衡技术改善性能
三台Web服务器共同处理来自用户浏览器的访问请求,这样每台
Web服务器需要处理的http请求只有总并发请求数的三分之一,根据性
能测试曲线,使服务器的并发请求数目控制在最佳运行区间,获得最佳
的访问请求延迟。
4.3.4 代码优化网站的业务逻辑实现代码主要部署在应用服务器上,需要处理复杂
的并发事务。合理优化业务代码,可以很好地改善网站性能。不同编程
语言的代码优化手段有很多,这里我们概要地关注比较重要的几个方
面。
1.多线程
多用户并发访问是网站的基本需求,大型网站的并发用户数会达到
数万,单台服务器的并发用户也会达到数百。CGI编程时代,每个用户
请求都会创建一个独立的系统进程去处理。由于线程比进程更轻量,更
少占有系统资源,切换代价更小,所以目前主要的Web应用服务器都采
用多线程的方式响应并发用户请求,因此网站开发天然就是多线程编
程。
从资源利用的角度看,使用多线程的原因主要有两个:IO阻塞与多
CPU。当前线程进行IO处理的时候,会被阻塞释放CPU以等待IO操作完
成,由于IO操作(不管是磁盘IO还是网络IO)通常都需要较长的时间,这时CPU可以调度其他的线程进行处理。前面我们提到,理想的系统
Load是既没有进程(线程)等待也没有CPU空闲,利用多线程IO阻塞与
执行交替进行,可最大限度地利用CPU资源。使用多线程的另一个原因
是服务器有多个CPU,在这个连手机都有四核CPU的时代,除了最低配
置的虚拟机,一般数据中心的服务器至少16核CPU,要想最大限度地使
用这些CPU,必须启动多线程。
网站的应用程序一般都被Web服务器容器管理,用户请求的多线程
也通常被Web服务器容器管理,但不管是Web容器管理的线程,还是应
用程序自己创建的线程,一台服务器上启动多少线程合适呢?假设服务
器上执行的都是相同类型任务,针对该类任务启动的线程数有个简化的估算公式可供参考:
启动线程数=[任务执行时间(任务执行时间(IO等待时
间)]]CPU内核数
最佳启动线程数和CPU内核数量成正比,和IO阻塞时间成反比。如
果任务都是CPU计算型任务,那么线程数最多不超过CPU内核数,因为
启动再多线程,CPU也来不及调度;相反如果是任务需要等待磁盘操
作,网络响应,那么多启动线程有助于提高任务并发度,提高系统吞吐
能力,改善系统性能。
多线程编程一个需要注意的问题是线程安全问题,即多线程并发对
某个资源进行修改,导致数据混乱。这也是缺乏经验的网站工程师最容
易犯错的地方,而线程安全Bug又难以测试和重现,网站故障中,许多
所谓偶然发生的“灵异事件”都和多线程并发问题有关。对网站而言,不
管有没有进行多线程编程,工程师写的每一行代码都会被多线程执行,因为用户请求是并发提交的,也就是说,所有的资源——对象、内存、文件、数据库,乃至另一个线程都可能被多线程并发访问。
编程上,解决线程安全的主要手段有如下几点。
将对象设计为无状态对象: 所谓无状态对象是指对象本身不存储
状态信息(对象无成员变量,或者成员变量也是无状态对象),这样多
线程并发访问的时候就不会出现状态不一致,Java Web开发中常用的
Servlet对象就设计为无状态对象,可以被应用服务器多线程并发调用处
理用户请求。而Web开发中常用的贫血模型对象都是些无状态对象。不
过从面向对象设计的角度看,无状态对象是一种不良设计。
使用局部对象: 即在方法内部创建对象,这些对象会被每个进入该方法的线程创建,除非程序有意识地将这些对象传递给其他线程,否
则不会出现对象被多线程并发访问的情形。
并发访问资源时使用锁: 即多线程访问资源的时候,通过锁的方
式使多线程并发操作转化为顺序操作,从而避免资源被并发修改。随着
操作系统和编程语言的进步,出现各种轻量级锁,使得运行期线程获取
锁和释放锁的代价都变得更小,但是锁导致线程同步顺序执行,可能会
对系统性能产生严重影响。
2.资源复用
系统运行时,要尽量减少那些开销很大的系统资源的创建和销毁,比如数据库连接、网络通信连接、线程、复杂对象等。从编程角度,资
源复用主要有两种模式:单例(Singleton)和对象池(Object Pool)。
单例虽然是GoF经典设计模式中较多被诟病的一个模式,但由于目
前Web开发中主要使用贫血模式,从Service到Dao都是些无状态对象,无需重复创建,使用单例模式也就自然而然了。事实上,Java开发常用
的对象容器Spring默认构造的对象都是单例(需要注意的是Spring的单
例是Spring容器管理的单例,而不是用单例模式构造的单例)。
对象池模式通过复用对象实例,减少对象创建和资源消耗。对于数
据库连接对象,每次创建连接,数据库服务端都需要创建专门的资源以
应对,因此频繁创建关闭数据库连接,对数据库服务器而言是灾难性
的,同时频繁创建关闭连接也需要花费较长的时间。因此在实践中,应
用程序的数据库连接基本都使用连接池(Connection Pool)的方式。数
据库连接对象创建好以后,将连接对象放入对象池容器中,应用程序要
连接的时候,就从对象池中获取一个空闲的连接使用,使用完毕再将该对象归还到对象池中即可,不需要创建新的连接。
前面说过,对于每个Web请求(HTTP Request),Web应用服务器
都需要创建一个独立的线程去处理,这方面,应用服务器也采用线程池
(Thread Pool)的方式。这些所谓的连接池、线程池,本质上都是对象
池,即连接、线程都是对象,池管理方式也基本相同。
3.数据结构
早期关于程序的一个定义是,程序就是数据结构早算法,数据结构
对于编程的重要性不言而喻。在不同场景中合理使用恰当的数据结构,灵活组合各种数据结构改善数据读写和计算特性可极大优化程序的性
能。
前面缓存部分已经描述过Hash表的基本原理,Hash表的读写性能在
很大程度上依赖HashCode的随机性,即HashCode越随机散列,Hash表
的冲突就越少,读写性能也就越高,目前比较好的字符串Hash散列算法
有Time33算法,即对字符串逐字符迭代乘以33,求得Hash值,算法原型
为:
hash(i) = hash(i 1) 33 + str[i]
Time33虽然可以较好地解决冲突,但是有可能相似字符串的
HashCode也比较接近,如字符串“AA”的HashCode是2210,字符
串“AB”的HashCode是2211。这在某些应用场景是不能接受的,这种情
况下,一个可行的方案是对字符串取信息指纹,再对信息指纹求
HashCode,由于字符串微小的变化就可以引起信息指纹的巨大不同,因
此可以获得较好的随机散列,如图4.16所示。图4.16 通过MD5计算HashCode
4.垃圾回收
如果Web应用运行在JVM等具有垃圾回收功能的环境中,那么垃圾
回收可能会对系统的性能特性产生巨大影响。理解垃圾回收机制有助于
程序优化和参数调优,以及编写内存安全的代码。
以JVM为例,其内存主要可划分为堆(heap)和堆栈(stack)。堆
栈用于存储线程上下文信息,如方法参数、局部变量等。堆则是存储对
象的内存空间,对象的创建和释放、垃圾回收就在这里进行。通过对对
象生命周期的观察,发现大部分对象的生命周期都极其短暂,这部分对
象产生的垃圾应该被更快地收集,以释放内存,这就是JVM分代垃圾回
收,其基本原理如图4.17所示。
图4.17 JVM分代垃圾回收机制
在JVM分代垃圾回收机制中,将应用程序可用的堆空间分为年轻代
(Young Generation)和年老代(Old Generation),又将年轻代分为
Eden区(Eden Space)、From区和To区,新建对象总是在Eden区中被创
建,当Eden区空间已满,就触发一次Young GC(Garbage Collection,垃圾回收),将还被使用的对象复制到From区,这样整个Eden区都是
未被使用的空间,可供继续创建对象,当Eden区再次用完,再触发一次
Young GC,将Eden区和From区还在被使用的对象复制到To区,下一次Young GC则是将Eden区和To区还被使用的对象复制到From区。因此,经过多次Young GC,某些对象会在From区和To区多次复制,如果超过
某个阈值对象还未被释放,则将该对象复制到Old Generation。如果Old
Generation空间也已用完,那么就会触发Full GC,即所谓的全量回收,全量回收会对系统性能产生较大影响,因此应根据系统业务特点和对象
生命周期,合理设置Young Generation和Old Generation大小,尽量减少
Full GC。事实上,某些Web应用在整个运行期间可以做到从不进行Full
GC。
4.4 存储性能优化
在网站应用中,海量的数据读写对磁盘访问造成巨大压力,虽然可
以通过Cache解决一部分数据读压力,但是很多时候,磁盘仍然是系统
最严重的瓶颈。而且磁盘中存储的数据是网站最重要的资产,磁盘的可
用性和容错性也至关重要。
4.4.1 机械硬盘vs. 固态硬盘
机械硬盘是目前最常用的一种硬盘,通过马达驱动磁头臂,带动磁
头到指定的磁盘位置访问数据,由于每次访问数据都需要移动磁头臂,因此机械硬盘在数据连续访问(要访问的数据存储在连续的磁盘空间
上)和随机访问(要访问的数据存储在不连续的磁盘空间)时,由于移
动磁头臂的次数相差巨大,性能表现差别也非常大。机械硬盘结构如图
4.18所示。图4.18 机械硬盘结构图(图片来自互联网)
固态硬盘又称作SSD或Flash硬盘,这种硬盘没有机械装置,数据存
储在可持久记忆的硅晶体上,因此可以像内存一样快速随机访问。而且
SSD具有更小的功耗和更少的磁盘震动与噪声。SSD硬盘如图4.19所
示。
图4.19 SSD硬盘(图片来自互联网)
在网站应用中,大部分应用访问数据都是随机的,这种情况下SSD
具有更好的性能表现。但是目前SSD硬盘还不太成熟,可靠性、性价比
有待提升,因此SSD的使用还在摸索阶段。但是相信随着SSD工艺水平
的提高,逐步替代传统机械硬盘是迟早的事。4.4.2 B+树 vs. LSM树
本书前面提到,由于传统的机械磁盘具有快速顺序读写、慢速随机
读写的访问特性,这个特性对磁盘存储结构和算法的选择影响甚大。
为了改善数据访问特性,文件系统或数据库系统通常会对数据排序
后存储,加快数据检索速度,这就需要保证数据在不断更新、插入、删
除后依然有序,传统关系数据库的做法是使用B+树,如图4.20所示。
图4.20 B+树原理示意图
B+树是一种专门针对磁盘存储而优化的N叉排序树,以树节点为
单位存储在磁盘中,从根开始查找所需数据所在的节点编号和磁盘位
置,将其加载到内存中然后继续查找,直到找到所需的数据。
目前数据库多采用两级索引的B+树,树的层次最多三层。因此可
能需要5次磁盘访问才能更新一条记录(三次磁盘访问获得数据索引及
行ID,然后再进行一次数据文件读操作及一次数据文件写操作)。
但是由于每次磁盘访问都是随机的,而传统机械硬盘在数据随机访
问时性能较差,每次数据访问都需要多次访问磁盘影响数据访问性能。目前许多NoSQL产品采用LSM树作为主要数据结构,如图4.21所
示。
图4.21 LSM树原理示意图(图片来源互联网)
LSM树可以看作是一个N阶合并树。数据写操作(包括插入、修
改、删除)都在内存中进行,并且都会创建一个新记录(修改会记录新
的数据值,而删除会记录一个删除标志),这些数据在内存中仍然还是
一棵排序树,当数据量超过设定的内存阈值后,会将这棵排序树和磁盘
上最新的排序树合并。当这棵排序树的数据量也超过设定阈值后,和磁
盘上下一级的排序树合并。合并过程中,会用最新更新的数据覆盖旧的
数据(或者记录为不同版本)。
在需要进行读操作时,总是从内存中的排序树开始搜索,如果没有
找到,就从磁盘上的排序树顺序查找。
在LSM树上进行一次数据更新不需要磁盘访问,在内存即可完成,速度远快于B+树。当数据访问以写操作为主,而读操作则集中在最近
写入的数据上时,使用LSM树可以极大程度地减少磁盘的访问次数,加
快访问速度。
作为存储结构,B+树不是关系数据库所独有的,NoSQL数据库也
可以使用B+树。同理,关系数据库也可以使用LSM,而且随着SSD硬盘的日趋成熟及大容量持久存储的内存技术的出现,相信B+树这一“古
老”的存储结构会再次焕发青春。
4.4.3 RAID vs. HDFS
RAID(廉价磁盘冗余阵列)技术主要是为了改善磁盘的访问延
迟,增强磁盘的可用性和容错能力。目前服务器级别的计算机都支持插
入多块磁盘(8块或者更多),通过使用RAID技术,实现数据在多块磁
盘上的并发读写和数据备份。
常用RAID技术有以下几种,如图4.22所示。
图4.22 常用RAID技术原理图
假设服务器有N 块磁盘。
RAID0
数据在从内存缓冲区写入磁盘时,根据磁盘数量将数据分成N 份,这些数据同时并发写入N 块磁盘,使得数据整体写入速度是一块磁盘的
N 倍。读取时也一样,因此RAID0具有极快的数据读写速度,但是
RAID0不做数据备份,N 块磁盘中只要有一块损坏,数据完整性就被破
坏,所有磁盘的数据都会损坏。
RAID1数据在写入磁盘时,将一份数据同时写入两块磁盘,这样任何一块
磁盘损坏都不会导致数据丢失,插入一块新磁盘就可以通过复制数据的
方式自动修复,具有极高的可靠性。
RAID10
结合RAID0和RAID1两种方案,将所有磁盘平均分成两份,数据同
时在两份磁盘写入,相当于RAID1,但是在每一份磁盘里面的N 2块磁
盘上,利用RAID0技术并发读写,既提高可靠性又改善性能,不过
RAID10的磁盘利用率较低,有一半的磁盘用来写备份数据。
RAID3
一般情况下,一台服务器上不会出现同时损坏两块磁盘的情况,在
只损坏一块磁盘的情况下,如果能利用其他磁盘的数据恢复损坏磁盘的
数据,这样在保证可靠性和性能的同时,磁盘利用率也得到大幅提升。
在数据写入磁盘的时候,将数据分成N -1份,并发写入N -1块磁
盘,并在第N 块磁盘记录校验数据,任何一块磁盘损坏(包括校验数据
磁盘),都可以利用其他N -1块磁盘的数据修复。
但是在数据修改较多的场景中,修改任何磁盘数据都会导致第N 块
磁盘重写校验数据,频繁写入的后果是第N 块磁盘比其他磁盘容易损
坏,需要频繁更换,所以RAID3很少在实践中使用。
RAID5
相比RAID3,方案RAID5被更多地使用。
RAID5和RAID3很相似,但是校验数据不是写入第N 块磁盘,而是螺旋式地写入所有磁盘中。这样校验数据的修改也被平均到所有磁盘
上,避免RAID3频繁写坏一块磁盘的情况。
RAID6
如果数据需要很高的可靠性,在出现同时损坏两块磁盘的情况下
(或者运维管理水平比较落后,坏了一块磁盘但是迟迟没有更换,导致
又坏了一块磁盘),仍然需要修复数据,这时候可以使用RAID6。
RAID6和RAID5类似,但是数据只写入N -2块磁盘,并螺旋式地在
两块磁盘中写入校验信息(使用不同算法生成)。
在相同磁盘数目(N )的情况下,各种RAID技术的比较如表4.3所
示。
表4.3 几种RAID技术比较
RAID技术可以通过硬件实现,比如专用的RAID卡或者主板直接支
持,也可以通过软件实现。RAID技术在传统关系数据库及文件系统中
应用比较广泛,但是在大型网站比较喜欢使用的NoSQL,以及分布式文
件系统中,RAID技术却遭到冷落。
例如在HDFS(Hadoop 分布式文件系统)中,系统在整个存储集群
的多台服务器上进行数据并发读写和备份,可以看作在服务器集群规模
上实现了类似RAID的功能,因此不需要磁盘RAID。HDFS以块(Block)为单位管理文件内容,一个文件被分割成若干
个Block,当应用程序写文件时,每写完一个Block,HDFS就将其自动
复制到另外两台机器上,保证每个Block有三个副本,即使有两台服务
器宕机,数据依然可以访问,相当于实现了RAID1的数据复制功能。
当对文件进行处理计算时,通过MapReduce并发计算任务框架,可
以启动多个计算子任务(MapReduce Task),同时读取文件的多个
Block,并发处理,相当于实现了RAID0的并发访问功能。
HDFS架构如图4.23所示。
图4.23 HDFS架构原理图
在HDFS中有两种重要的服务器角色:NameNode(名字服务节点)
和DataNode(数据存储节点)。NameNode在整个HDFS中只部署一个实
例,提供元数据服务,相当于操作系统中的文件分配表(FAT),管理
文件名Block的分配,维护整个文件系统的目录树结构。DataNode则部
署在HDFS集群中其他所有服务器上,提供真正的数据存储服务。和操作系统一样,HDFS对数据存储空间的管理以数据块(Block)
为单位,只是比操作系统中的数据块(512字节)要大得多,默认为
64MB。HDFS将DataNode上的磁盘空间分成N 个这样的块,供应用程序
使用。
应用程序(Client)需要写文件时,首先访问NameNode,请求分配
数据块,NameNode根据管理的DataNode服务器的磁盘空间,按照一定
的负载均衡策略,分配若干数据块供Client使用。
当Client写完一个数据块时,HDFS会将这个数据块再复制两份存储
在其他DataNode服务器上,HDFS默认同一份数据有三个副本,保证数
据可靠性。因此在HDFS中,即使DataNode服务器有多块磁盘,也不需
要使用RAID进行数据备份,而是在整个集群上进行数据复制,而且系
统一旦发现某台服务器宕机,会自动利用其他机器上的数据将这台服务
器上存储的数据块自动再备份一份,从而获得更高的数据可靠性。
HDFS配合MapReduce等并行计算框架进行大数据处理时,可以在
整个集群上并发读写访问所有的磁盘,无需RAID支持。
4.5 小结
网站性能优化技术是在网站性能遇到问题时的解决方案。而网站的
性能问题很多是在用户高并发访问时产生的,所以网站性能优化的主要
工作是改善高并发用户访问情况下的网站响应速度。本章开篇所举的例
子,当老板说“我们要改善网站性能”的时候,他期望的是在A方案的基
础上,不管是100个并发访问还是200个并发访问,响应时间都能达到1
秒。而架构师能做到的,则是利用分布式的方案改善网站并发特性,由
于分布式不可避免地会带来架构复杂、网络通信延迟等问题,所以最终设计出来的可能是B方案:缩短高并发访问响应延迟的同时,却延长了
原来低并发访问时的响应延迟。架构师对这种可能性要心中有数,合理
调整相关各方对性能优化的心理预期。
网站性能对最终用户而言是一种主观感受,性能优化的最终目的就
是改善用户的体验,使他们感觉网站很快。离开这个目的,追求技术上
的所谓高性能,是舍本逐末,没有多大意义。而用户体验的快或是慢,可以通过技术手段改善,也可以通过优化交互体验改善。
即使在技术层面,性能优化也需要全面考虑,综合权衡:性能提升
一倍,但服务器数量也需要增加一倍;或者响应时间缩短,同时数据一
致性也下降,这样的优化是否可以接受?这类问题的答案不是技术团队
能回答的。归根结底,技术是为业务服务的,技术选型和架构决策依赖
业务规划乃至企业战略规划,离开业务发展的支撑和驱动,技术走不
远,甚至还会迷路。
前沿技术总是出现在前沿业务领域。近几年,以Google
为首的互联网企业领跑IT前沿技术潮流,是因为互联网企业
的业务发展远超传统IT企业领域,面临更多挑战,对IT系统
提出了更高的要求。
新技术的出现又会驱动企业开展新的业务。亚马逊等互
联网公司利用自己的技术优势进军企业级市场,以技术驱动
业务,开展云计算、SaaS等新兴IT业务,逐步蚕食IBM、HP、Oracle、微软等传统软件巨头的市场。5 万无一失:网站的高可用架构
2011年4月12日,亚马逊云计算服务EC2(Elastic Computer Cloud)
发生故障,其ESB(Elastic Block Storage)服务不可用,故障持续了数
天,最终还是有部分数据未能恢复。这一故障导致美国许多使用亚马逊
云服务的知名网站(如:Foursquare,Quora)受到影响,并引发了人们
对使用云计算安全性、可靠性的大规模讨论。
2010年1月12日,百度被黑客攻击,其DNS域名被劫持,导致百度
全站长达数小时不可访问。该事件一时成为新闻焦点,各种媒体争相报
道。
网站的可用性(Availability)描述网站可有效访问的特性(不同于
另一个网站运营指标:Usability,通常也被译作可用性,但是后者强调
的是网站的有用性,即对最终用户的使用价值),相比于网站的其他非
功能特性,网站的可用性更牵动人们的神经,大型网站的不可用事故直
接影响公司形象和利益,许多互联网公司都将网站可用性列入工程师的
绩效考核,与奖金升迁等利益挂钩。
5.1 网站可用性的度量与考核
网站的页面能完整呈现在最终用户面前,需要经过很多个环节,任
何一个环节出了问题,都可能导致网站页面不可访问。DNS会被劫持、CDN服务可能会挂掉、网站服务器可能会宕机、网络交换机可能会失
效、硬盘会损坏、网卡会松掉、甚至机房会停电、空调会失灵、程序会
有Bug、黑客会攻击、促销会引来大量访问、第三方合作伙伴的服务会
不可用……要保证一个网站永远完全可用几乎是一件不可能完成的使命。
5.1.1 网站可用性度量
网站不可用也被称作网站故障,业界通常用多少个9来衡量网站的
可用性,如QQ的可用性是4个9,即QQ服务99.99%可用,这意味着QQ
服务要保证其在所有运行时间中,只有0.01%的时间不可用,也就是一
年中大约最多53分钟不可用。
网站不可用时间(故障时间)=故障修复时间点网故障发现(报
告)时间点
网站年度可用性指标=(11网站不可用时间年度总时间)年100%
对于大多数网站而言,2个9是基本可用,网站年度不可用时间小于
88小时;3个9是较高可用,网站年度不可用时间小于9小时;4个9是具
有自动恢复能力的高可用,网站年度不可用时间小于53分钟;5个9是极
高可用性,网站年度不可用时间小于5分钟。
由于可用性影响因素很多,对于网站整体而言,达到4个9,乃至5
个9的可用性,除了过硬的技术、大量的设备资金投入和工程师的责任
心,还要有个好运气。
常使用Twitter的用户或多或少遇到过那个著名的服务不可用的鲸鱼
页面,事实上,Twitter网站的可用性不足2个9。
5.1.2 网站可用性考核
可用性指标是网站架构设计的重要指标,对外是服务承诺,对内是
考核指标。从管理层面,可用性指标是网站或者产品的整体考核指标,具体到每个工程师的考核,更多的是使用故障分。
所谓故障分是指对网站故障进行分类加权计算故障责任的方法。表
5.1为某网站故障分类权重表。
表5.1 网站故障分类权重表示例
故障分的计算公式为:
故障分=故障时间(分钟)( 故障权重
在年初或者考核季度的开始,会根据网站产品的可用性指标计算总
的故障分,然后根据团队和个人的职责角色分摊故障分,这个可用性指
标和故障分是管理预期。在实际发生故障的时候,根据故障分类和责任
划分将故障产生的故障分分配给责任者承担。等年末或者考核季度末的
时候,个人及团队实际承担的故障分如果超过了年初分摊的故障分,绩
效考核就会受到影响。
一个简化的故障处理流程如图5.1所示。图5.1 网站故障处理流程示例
有时候一个故障责任可能由多个部门或团队来承担,故障分也会相
应按责任分摊到不同的团队和个人。
不同于其他架构指标,网站可用性更加看得见摸得着,跟技术、运
营、相关各方的绩效考核息息相关,因此在架构设计与评审会议上,关
于系统可用性的讨论与争执总是最花费时间与精力的部分。
当然,不同的公司有不同的企业文化和市场策略,这些因素也会影
响到系统可用性的架构决策,崇尚创新和风险的企业会对可用性要求稍
低一些;业务快速增长的网站忙于应对指数级增长的用户,也会降低可
用性的标准;服务于收费用户的网站则会比服务于免费用户的网站对可
用性更加敏感,服务不可用或关键用户数据丢失可能会导致收费用户的投诉甚至引来官司。
5.2 高可用的网站架构
通常企业级应用系统为提高系统可用性,会采用较昂贵的软硬件设
备,如IBM的小型机乃至中型机大型机及专有操作系统、Oracle数据
库、EMC存储设备等。互联网公司更多地采用PC级服务器、开源的数
据库和操作系统,这些廉价的设备在节约成本的同时也降低了可用性,特别是服务器硬件设备,低价的商业级服务器一年宕机一次是一个大概
率事件,而那些高强度频繁读写的普通硬盘,损坏的概率则要更高一
些。
既然硬件故障是常态,网站的高可用架构设计的主要目的就是保证
服务器硬件故障时服务依然可用、数据依然保存并能够被访问。
实现上述高可用架构的主要手段是数据和服务的冗余备份及失效转
移,一旦某些服务器宕机,就将服务切换到其他可用的服务器上,如果
磁盘损坏,则从备份的磁盘读取数据。
一个典型的网站设计通常遵循如图5.2所示的基本分层架构模型。
图5.2 网站架构基本分层模型
典型的分层模型是三层,即应用层、服务层、数据层;各层之间具有相对独立性,应用层主要负责具体业务逻辑处理;服务层负责提供可
复用的服务;数据层负责数据的存储与访问。中小型网站在具体部署
时,通常将应用层和服务层部署在一起,而数据层则另外部署,如图
5.3所示(事实上,这也是网站架构演化的第一步)。
图5.3 应用和数据分离部署的网站架构
在复杂的大型网站架构中,划分的粒度会更小、更详细,结构更加
复杂,服务器规模更加庞大,但通常还是能够把这些服务器划分到这三
层中。如图5.4所示。
图5.4 分层后按模块分割的网站架构模型不同的业务产品会部署在不同的服务器集群上,如某网站的文库、贴吧、百科等属于不同的产品,部署在各自独立的服务器集群上,互不
相干。这些产品又会依赖一些共同的复用业务,如注册登录服务、Session管理服务、账户管理服务等,这些可复用的业务服务也各自部署
在独立的服务器集群上。至于数据层,数据库服务、文件服务、缓存服
务、搜索服务等数据存储与访问服务都部署在各自独立的服务器集群
上。
大型网站的分层架构及物理服务器的分布式部署使得位
于不同层次的服务器具有不同的可用性特点。关闭服务或者
服务器宕机时产生的影响也不相同,高可用的解决方案也差
异甚大。
位于应用层的服务器通常为了应对高并发的访问请求,会通过负载
均衡设备将一组服务器组成一个集群共同对外提供服务,当负载均衡设
备通过心跳检测等手段监控到某台应用服务器不可用时,就将其从集群
列表中剔除,并将请求分发到集群中其他可用的服务器上,使整个集群
保持可用,从而实现应用高可用。
位于服务层的服务器情况和应用层的服务器类似,也是通过集群方
式实现高可用,只是这些服务器被应用层通过分布式服务调用框架访
问,分布式服务调用框架会在应用层客户端程序中实现软件负载均衡,并通过服务注册中心对提供服务的服务器进行心跳检测,发现有服务不
可用,立即通知客户端程序修改服务访问列表,剔除不可用的服务器。
位于数据层的服务器情况比较特殊,数据服务器上存储着数据,为了保证服务器宕机时数据不丢失,数据访问服务不中断,需要在数据写
入时进行数据同步复制,将数据写入多台服务器上,实现数据冗余备
份。当数据服务器宕机时,应用程序将访问切换到有备份数据的服务器
上。
网站升级的频率一般都非常高,大型网站一周发布一次,中小型网
站一天发布几次。每次网站发布都需要关闭服务,重新部署系统,整个
过程相当于服务器宕机。因此网站的可用性架构设计不但要考虑实际的
硬件故障引起的宕机,还要考虑网站升级发布引起的宕机,而后者更加
频繁,不能因为系统可以接受偶尔的停机故障就降低可用性设计的标
准。
5.3 高可用的应用
应用层主要处理网站应用的业务逻辑,因此有时也称作业务逻辑
层,应用的一个显著特点是应用的无状态性。
所谓无状态的应用是指应用服务器不保存业务的上下文信息,而仅
根据每次请求提交的数据进行相应的业务逻辑处理,多个服务实例(服
务器)之间完全对等,请求提交到任意服务器,处理结果都是完全一样
的。
5.3.1 通过负载均衡进行无状态服务的失效转移
不保存状态的应用给高可用的架构设计带来了巨大便利,既然服务
器不保存请求的状态,那么所有的服务器完全对等,当任意一台或多台
服务器宕机,请求提交给集群中其他任意一台可用机器处理,这样对终
端用户而言,请求总是能够成功的,整个系统依然可用。对于应用服务器集群,实现这种服务器可用状态实时监测、自动转移失败任务的机制
是负载均衡。
负载均衡,顾名思义,主要使用在业务量和数据量较高的情况下,当单台服务器不足以承担所有的负载压力时,通过负载均衡手段,将流
量和数据分摊到一个集群组成的多台服务器上,以提高整体的负载处理
能力。目前,不管是开源免费的负载均衡软件还是昂贵的负载均衡硬
件,都提供失效转移功能。在网站应用中,当集群中的服务是无状态对
等时,负载均衡可以起到事实上高可用的作用,如图5.5所示。
图5.5 利用负载均衡服务器实现高可用的应用服务
当Web服务器集群中的服务器都可用时,负载均衡服务器会把用户
发送的访问请求分发到任意一台服务器上进行处理,而当服务器
10.0.0.1宕机时,负载均衡服务器通过心跳检测机制发现该服务器失去
响应,就会把它从服务器列表中删除,而将请求发送到其他服务器上,这些服务器是完全一样的,请求在任何一台服务器中处理都不会影响最
终的结果。由于负载均衡在应用层实际上起到了系统高可用的作用,因此即使
某个应用访问量非常少,只用一台服务器提供服务就绰绰有余,但如果
需要保证该服务高可用,也必须至少部署两台服务器,使用负载均衡技
术构建一个小型的集群。
5.3.2 应用服务器集群的Session管理
应用服务器的高可用架构设计主要基于服务无状态这一特性,但是
事实上,业务总是有状态的,在交易类的电子商务网站,需要有购物车
记录用户的购买信息,用户每次购买请求都是向购物车中增加商品;在
社交类的网站中,需要记录用户的当前登录状态、最新发布的消息及好
友状态等,用户每次刷新页面都需要更新这些信息。
Web应用中将这些多次请求修改使用的上下文对象称作会话
(Session),单机情况下,Session可由部署在服务器上的Web容器(如
JBoss)管理。在使用负载均衡的集群环境中,由于负载均衡服务器可
能会将请求分发到集群任何一台应用服务器上,所以保证每次请求依然
能够获得正确的Session比单机时要复杂很多。
集群环境下,Session管理主要有以下几种手段。
1.Session复制
Session复制是早期企业应用系统使用较多的一种服务器集群Session
管理机制。应用服务器开启Web容器的Session复制功能,在集群中的几
台服务器之间同步Session对象,使得每台服务器上都保存所有用户的
Session信息,这样任何一台机器宕机都不会导致Session数据的丢失,而
服务器使用Session时,也只需要在本机获取即可。如图5.6所示。图5.6 使用Session复制实现应用服务器共享Session
这种方案虽然简单,从本机读取Session信息也很快速,但只能使用
在集群规模比较小的情况下。当集群规模较大时,集群服务器间需要大
量的通信进行Session复制,占用服务器和网络的大量资源,系统不堪负
担。而且由于所有用户的Session信息在每台服务器上都有备份,在大量
用户访问的情况下,甚至会出现服务器内存不够Session使用的情况。
而大型网站的核心应用集群就是数千台服务器,同时在线用户可达
千万,因此并不适用这种方案。
2.Session绑定
Session绑定可以利用负载均衡的源地址Hash算法实现,负载均衡服
务器总是将来源于同一IP的请求分发到同一台服务器上(也可以根据
Cookie信息将同一个用户的请求总是分发到同一台服务器上,当然这时
负载均衡服务器必须工作在HTTP协议层上,关于负载均衡算法的更多信息请参考本书第6章内容。这样在整个会话期间,用户所有的请求都
在同一台服务器上处理,即Session绑定在某台特定服务器上,保证
Session总能在这台服务器上获取。这种方法又被称作会话黏滞,如图
5.7所示。
图5.7 利用负载均衡的会话黏滞机制将请求绑定到特定服务器
但是Session绑定的方案显然不符合我们对系统高可用的需求,因为
一旦某台服务器宕机,那么该机器上的Session也就不复存在了,用户请
求切换到其他机器后因为没有Session而无法完成业务处理。因此虽然大
部分负载均衡服务器都提供源地址负载均衡算法,但很少有网站利用这
个算法进行Session管理。
3.利用Cookie记录Session
早期的企业应用系统使用CS(客户端服务器)架构,一种管理
Session 的方式是将Session记录在客户端,每次请求服务器的时候,将Session放在请求中发送给服务器,服务器处理完请求后再将修改过的
Session响应给客户端。
网站没有客户端,但是可以利用浏览器支持的Cookie记录Session,如图5.8所示。
图5.8 利用Cookie记录Session信息
利用Cookie记录Session也有一些缺点,比如受Cookie大小限制,能
记录的信息有限;每次请求响应都需要传输Cookie,影响性能;如果用
户关闭Cookie,访问就会不正常。但是由于Cookie的简单易用,可用性
高,支持应用服务器的线性伸缩,而大部分应用需要记录的Session信息
又比较小。因此事实上,许多网站都或多或少地使用Cookie记录
Session。
4.Session服务器那么有没有可用性高、伸缩性好、性能也不错,对信息大小又没有
限制的服务器集群Session管理方案呢?
答案就是Session服务器。利用独立部署的Session服务器(集群)统
一管理Session,应用服务器每次读写Session时,都访问Session服务器,如图5.9所示。
图5.9 利用Session服务器共享Session
这种解决方案事实上是将应用服务器的状态分离,分为无状态的应
用服务器和有状态的Session服务器,然后针对这两种服务器的不同特性
分别设计其架构。
对于有状态的Session服务器,一种比较简单的方法是利用分布式缓
存、数据库等,在这些产品的基础上进行包装,使其符合Session的存储
和访问要求。如果业务场景对Session管理有比较高的要求,比如利用
Session服务集成单点登录(SSO)、用户服务等功能,则需要开发专门
的Session服务管理平台。5.4 高可用的服务
可复用的服务模块为业务产品提供基础公共服务,大型网站中这些
服务通常都独立分布式部署,被具体应用远程调用。可复用的服务和应
用一样,也是无状态的服务,因此可以使用类似负载均衡的失效转移策
略实现高可用的服务。
除此之外,具体实践中,还有以下几点高可用的服务策略。
1.分级管理
运维上将服务器进行分级管理,核心应用和服务优先使用更好的硬
件,在运维响应速度上也格外迅速。显然,用户及时付款购物比能不能
评价商品更重要,所以订单、支付服务比评价服务有更高优先级。
同时在服务部署上也进行必要的隔离,避免故障的连锁反应。低优
先级的服务通过启动不同的线程或者部署在不同的虚拟机上进行隔离,而高优先级的服务则需要部署在不同的物理机上,核心服务和数据甚至
需要部署在不同地域的数据中心。
2.超时设置
由于服务端宕机、线程死锁等原因,可能导致应用程序对服务端的
调用失去响应,进而导致用户请求长时间得不到响应,同时还占用应用
程序的资源,不利于及时将访问请求转移到正常的服务器上。
在应用程序中设置服务调用的超时时间,一旦超时,通信框架就抛
出异常,应用程序根据服务调度策略,可选择继续重试或将请求转移到
提供相同服务的其他服务器上。3.异步调用
应用对服务的调用通过消息队列等异步方式完成,避免一个服务失
败导致整个应用请求失败的情况。如提交一个新用户注册请求,应用需
要调用三个服务:将用户信息写入数据库,发送账户注册成功邮件,开
通对应权限。如果采用同步服务调用 ......
您现在查看是摘要介绍页, 详见PDF附件(7866KB,303页)。





