《大型网站技术架构-核心原理与技术分析》读书笔记
大型网站架构优化
- 初始阶段
- 应用服务和数据服务分离
- 使用缓存改善网站性能
- 使用应用服务器集群改善网站的并发处理能力
- 数据库读写分离
- 使用反向代理和CDN加速网站响应
- 使用分布式文件系统和分布式数据库系统
- 使用NoSQL和搜索引擎
- 业务拆分
- 分布式服务
大型网站架构演化的价值观
- 技术的核心价值是随网站所需灵活应对
- 技术发展的主要力量是网站的业务发展
设计误区
- 一味追随大公司的解决方案
- 为了技术而技术
- 企图用技术解决所有问题
大型网站架构模式
- 分层-横向
- 应用层
- 服务层
- 数据层
- 分割-纵向
- 将不同的功能和服务分割,包装成高内聚低耦合的模块单元
- 分布式
切分后的模块便于分布式部署,即将不同的模块部署在不同的服务器上,通过远程调用协同工作- 分布式应用和服务
- 分布式静态资源
- 分布式数据和存储
- 分布式计算
- Hadoop
- MapReduce
- 分布式配置
- 分布式锁
- 分布式文件
- 集群
将多台服务器部署相同应用构成一个集群,通过负载均衡设备共同对外提供服务 - 缓存
- CDN
- 反向代理
前端架构的一部分,用户请求最先访问。这里缓存网站的静态资源,无需将请求继续转发给应用服务就能返回给用户 - 本地缓存
- 分布式缓存
- 异步
非同步调用、解耦、队列。异步架构是典型的生产者消费者模式。- 提供系统可用性
- 加快网站响应速度
- 消除并发访问高峰
- 冗余(避免宕机情况)
- 访问和负载很小的服务也必须部署至少两台服务器构成一个集群,通过冗余实现服务高可用
- 数据库除了定期备份,存档保存,实现冷备份外,还要对数据库进行主从分离,实时同步实现热备份
- 全球范围内部署灾难数据中心,抵御地震等不可抗力导致的网站瘫痪
- 自动化
- 发布过程自动化
- 自动化代码管理
- 自动化测试
- 自动化安全检测
- 自动化部署
- 自动化监控
- 自动化报警
- 自动化失效转移
- 自动化失效恢复
- 自动化降级
- 自动化分配资源
- 安全
应用
大型网站架构核心架构要素
- 性能
- 优化手段(CDN、反向代理、本地缓存、分布式缓存、异步、集群、代码层面_多线程等、数据库优化、NOSQL)
- 性能指标(响应事件、TPS、系统性能计数器等)
通过监控这些指标可以分析系统瓶颈,预测网站容量,并对异常指标进行报警,保障系统可用性 - 要考虑在高并发访问情况下,超出负载设计能力的情况下可能会出现的性能问题。网站需要长时间持续运行,还必须保证系统在持续运行且访问压力不均匀的情况下保持稳定的性能特性
- 可用性
- 排除故障时间,即总可用时间
一些知名大型网站可以做到4个9以上的可用性,即可用性超过99.99% - 高可用设计目标即当服务器宕机的时候,服务或者应用依然可用
- 网站高可用的主要手段是冗余
- 应用服务器通过负载均衡设备组成集群
- 存储服务需要对数据实时备份
- 需要质量保证。通过预发布验证、自动化测试、自动化发布、灰度发布等手段,减少故障引入线上环境的可能
- 衡量一个系统架构设计是否满足高可用的目标,就是假设系统中任何一台或者多台服务器宕机时以及出现各种不可预期的问题时,系统整体是否依然可用
- 排除故障时间,即总可用时间
- 伸缩性
- 所谓伸缩性即指通过不断向集群中加入服务器的手段来缓解不断上升的用户并发压力和不断增长的数据存储需求
- 衡量架构伸缩性的主要标准 就是是否可以用多台服务器构建集群,是否容易向集群中增加新的服务器。加入新的服务器后是否可以提供和原来的服务器无差别的服务。集群中可容纳的总的服务器数量是否有限制。
- 应用服务器集群,只有服务器上不保存数据,所有的服务器都是对等的。通过使用合适的负载均衡设备就可以向集群中不断加入服务器
- 对于缓存服务器集群,加入新的服务器可能会导致缓存路由失效,进而导致集群中的大部分缓存数据都无法访问。需要改进缓存路由算法保证缓存数据的可访问性。
- 关系数据库虽然支持数据复制,主从热备等机制,但是很难做到大规模集群的可伸缩性,可通过如路由分区等手段将部署有多个数据库的服务器组成一个集群。
- 大部分NoSQL数据库产品先天即为海量数据而生,其对伸缩性的支持通常都非常好
- 扩展性
- 衡量网站架构扩展性好坏的主要标准就是网站增加新的业务产品时,是否可以实现对现有产品透明无影响,不需要任何改动或者很少改动既有业务功能就可以上线新产品。不同产品之间是否很少耦合,一个产品改动对其他产品无影响,其他产品和功能不需要受牵连进行改动
- 网站可伸缩架构的主要手段
- 事件驱动框架
- 消息队列实现
- 分布式服务
- 将业务和可复用服务分离开来,通过分布式服务框架调用
- 事件驱动框架
- 安全性
- 保护网站不受恶意访问和攻击
- 保护网站的重要数据不被窃取
- 衡量网站安全架构的标准就是针对现存和潜在的各种攻击与窃密手段,是否有可靠的应对策略
瞬时响应:网站的高性能架构
网站性能测试
- 从用户角度的网站性能
- 包括用户计算机和网站服务器通信的时间、网站服务器处理的时间、用户计算机浏览器构造请求解析响应数据的时间
- 实践中,可使用一些前端架构优化手段,如优化页面HTML样式,利用浏览器端的并发和异步特性、调整浏览器缓存策略、使用CDN、反向代理等
- 开发人员视角的网站性能
- 开发人员关注的主要是应用程序本身及其相关子系统的性能,包括响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标。
- 主要的优化手段有使用缓存加速数据读取、使用集群提高吞吐能力、使用异步消息加快请求响应及实现削峰、使用代码优化手段改善程序性能
- 运维人员视角的网站性能
- 运维人员更关注基础设施性能和资源利用率,如网络运营商的带宽能力、服务器硬件的配置、数据中心网络架构、服务器和网络带宽的资源利用率等。
- 主要优化手段有建设优化骨干网、使用高性价比定制服务器、利用虚拟技术优化资源利用等
性能测试指标
- 响应时间
- 并发数
- 指系统能够同时处理请求的数目,这个数字也反映了系统的负载特性。对于网站而言,并发数即网站并发用户数,指同时提交请求的用户数目
- 网站用户数 > 网站在线用户数 > 网站并发用户数
- 测试程序通过多线程模拟并发用户的办法来测试系统的并发处理能力,为了真实模拟用户行为,测试程序并非启动多线程然后不停的发送请求,而是在两次请求之间加上一个随机等待时间,即思考时间
- 吞吐量
- 指单位时间内系统处理的请求数量,体现系统的整体处理能力
- 对于网站,可以用“请求数/秒”、”页面数/秒“、”访问人数/天“、”处理的业务数/小时“等来衡量
- TPS(每秒事务数)是吞吐量的一个常用量化指标,此外还有HPS(每秒HTTP请求数)、QPS(每秒查询数)等
- 在系统并发数由小逐渐增加的过程中(这个过程也伴随着服务系统资源消耗逐渐增加),系统吞吐量先是逐渐增加,达到一个极限后,随着并发数的增加反而下降,达到系统崩溃点后,系统资源耗尽,吞吐量为0
- 这个过程中,响应时间则是先保持小幅上升,到达吞吐量极限后,快速上升,到达系统崩溃点后,系统失去响应。
- 形象理解
- 吞吐量是每天通过收费站的车辆数目
- 并发数是高速公路上正在行驶的车辆数目
- 响应时间是车速
- 车辆很少时,车速很快,但是通过收费站的车辆也较少
- 随着高速公路上车辆数目的增多,车速略受影响,但是通过收费站的车辆增加很快
- 随着车辆的继续增加,车速变的越来越慢,高速公路越来越堵,通过收费站的车辆不增反降
- 如果车流量继续增加,超过某个极限后,任何偶然因素都会导致高速全部瘫痪,车走不通,也不会有车通过收费站,而高速公路成了停车场,资源耗尽
- 网站性能优化的目的,处理改善用户体验的响应时间,还要尽量提高系统吞吐量,最大限度利用服务器资源
- 性能计数器
- 它是描述服务器或者操作系统性能的一些数据指标、包括SystemLoad、对象与线程数、内存使用、CPU使用、磁盘与网络I/O等指标。这些指标也是系统监控的重要参数,对这些指标设置报警阀值,当监控系统发现性能计数器超过阀值时,就向运维和开发人员报警,及时发现处理系统异常
- System Load即系统负载,只当前正在被CPU执行和等待被CPU执行的进程数目总和,是反映系统忙闲程度的重要指标。多核CPU的情况下,完美情况是所有CPU都在使用,没有进程在等待处理,所以Load的理想值是CPU的数目。当Load值低于CPU数目的时候,表示CPU有空闲,资源存在浪费;当Load值高于CPU数目的时候,表示进程在排队等待CPU调度,表示系统资源不足,影响程序的执行性能。Linux下使用top命令查看,该值表示最近1分钟、10分钟、15分钟的运行队列平均进程数
性能测试方法
- 性能测试
- 以系统设计初期规划的性能指标为预期目标,对系统不断施加压力,验证系统在资源可接受范围内,是否达到性能预期
- 负载测试
- 对系统不断的增加并发请求以增加系统压力,直到系统的某项或多项性能指标达到安全临界值,如某种资源已经呈饱和状态,这时继续对系统施加压力,系统的处理能力不但不能提高反而会下降
- 压力测试
- 超过安全负载的情况下,对系统持续施加压力,直到系统崩溃或不能再处理任何请求,以此获得系统最大压力承受能力
- 稳定性测试
- 被测试系统在特定硬件、软件、网络环境条件下,给系统加载一定业务压力,使系统运行一段较长时间,以此检测系统是否稳定。在不同生成环境、不同时间点的请求压力是不均匀的,呈波浪特性,因为为了更好的模拟生产环境,稳定性测试也应不均匀地对系统施加压力
- 性能测试是一个不断对系统增加访问压力,以获得系统性能指标、最大负载能力、最大压力承受能力的过程。所谓的增加访问压力,在系统测试环境中,就是不断增加测试程序的并发请求数,一般说来,性能测试遵循抛物线规律:
- 横坐标表示消耗的系统资源,纵坐标表示系统处理能力,即吞吐量
- 开始阶段,随着并发请求数目的增加,系统使用较少的资源就达到较好的处理能力(a~b)段,这一段是网站的日常运行区间,网站的绝大部分访问负载压力都集中在这一段区间,被称作性能测试
- 随着压力的测试增加,系统处理能力变缓,直到达到一个最大值c点,这是系统的最大负载点,这一阶段被称作负载测试,测试目标是评估系统因为突发事件超出日常访问压力的情况下保证系统正常运行情况下能够承受的最大访问负载压力
- 超过这个点后,再增加压力,系统的处理能力反而下降,而资源消耗却更多,直到资源消耗达到极限d,这个点可以看做是系统的崩溃点,超过这个点继续增大并发请求数目,系统不能再处理任何请求,这一段被称作压力测试,测试目标是评估可能导致系统崩溃的最大访问负载压力
- 性能测试反映的是系统在实际生产环境中使用时,随着用户并发访问数量的增加,系统的处理能力。与性能曲线相对应的是用户访问的等待时间(系统响应时间),如下:
- 在日常运行期间,可以获得最好的用户响应时间,随着并发用户数的增加,响应延迟越来越大,直到系统崩溃,用户失去响应
性能测试报告
性能优化策略
- 性能分析
- 大型网站结构复杂,用户从浏览器发出请求直到数据库完成操作事务,中间需要经过很多环节,如果测试或者用户报告网站响应缓慢,存在性能问题,必须对请求经历的各个环节进行分析,排查可能出现性能瓶颈的地方,定位问题
- 排查一个网站的性能瓶颈和排查一个程序的性能瓶颈的手法基本相同:检查请求处理的各个环节的日志,分析哪个环节响应时间不合理、超过预期;然后检查监控数据,分析影响性能的主要因素是内存、磁盘、网络还是CPU,是代码问题还是架构设计不合理或者系统资源确实不足
- 性能优化
- 定位产生性能问题的基本原因后,就需要进行性能优化
- Web前端性能优化
- 应用服务器性能优化
- 存储服务器性能优化
- 定位产生性能问题的基本原因后,就需要进行性能优化
Web前端性能优化
- 浏览器访问优化
- 减少http请求
- 合并CSS、合并JavaScript、合并图片
- 使用浏览器缓存
- 设置http头中cache-control和expires的属性,设定浏览器缓存
- 静态资源文件变化->生成一个新的文件
- 使用浏览器缓存策略的网站在更新静态资源时应采用批量更新的方法以免用户浏览器突然大量缓存失效,集中更新缓存,造成服务器负载骤增、网络堵塞的情况
- 启用压缩
- gzip
- 压缩对服务器和浏览器产生一定的压力,在通信带宽良好而服务器资源不足的情况要权衡考虑
- CSS放在页面最上面、JavaScript放在页面最下方
- 浏览器会在下载完全部css之后才对整个页面进行渲染,所以最好放在页面最上面让浏览器尽快下载css
- 浏览器在加载javascript后立即执行有可能会阻塞整个页面,造成页面显示缓慢,所以最好放在页面最下面
- 如果页面解析时就需要用到javascript,这时放在底部就不合适了
- 减少cookie传输
- cookie因包含在每次请求和响应中,太大的cookie会验证影响数据传输
- 对于某些静态资源的访问,如css,script等,发送cookie没有意义,可考虑静态资源使用独立域名访问,避免请求静态资源时发送cookie,减少cookie传输的次数
- 减少http请求
- CDN加速
- 本质仍然是一个缓存,而且将数据缓存在离用户最近的地方
- CDN部署在网络运营商的机房,这些运营商又是终端用户的网络服务提供商,用户请求路由的第一跳就达到了CDN服务器->有缓存->直接返回给浏览器->最短路径返回响应
- CDN能够缓存的一般是静态资源
- 反向代理
- 传统代理服务器位于浏览器一侧,代理浏览器将http请求发送到互联网上
- 反向代理服务器位于网站机房一侧,代理网站web服务器接收http请求
- 反向代理服务器也具有保护网站安全的作用
- 除了安全功能,代理服务器也可以通过配置缓存功能加速web请求。当用户第一次访问静态内容,静态内容就被缓存在反向代理服务器上
- 有些网站也会把动态内容也缓存在代理服务器上,如热门词条,当这些动态内容有变化时通过内部通知机制通知反向代理缓存失效,反向代理会重新加载最新的动态内容再次缓存起来
- 反向代理也可以实现负载均衡的功能,通过负载均衡构建的应用集群可以提高系统总体处理能力
应用服务器性能优化
- 分布式缓存
- 网站性能优化第一定律:优先考虑使用缓存优化性能
- 缓存的基本原理-hash->hashcode->索引下标
- 网站数据通常遵循28定律,即80%的访问落在20%的数据上,因为利用hash和内存的高效访问属性,将这20%的数据缓存起来,可很好的改善系统性能,提高数据读取速度,减低存储访问压力
- 合理使用缓存,如过分依赖低可用的缓存系统,不恰当的使用缓存的访问特性等
- 频繁修改的数据,一般说来,数据的读写比在2:1以上,即写入一次缓存,在数据更新前至少读取两次,缓存才有意义
- 没有热点的访问,如果应用系统访问数据没有热点,缓存就没有意义。因为缓存使用内存作为存储,内存资源宝贵而有限,不可能将所有数据都缓存起来,只能将最新访问的数据缓存起来,而将历史数据清理出缓存
- 数据不一致与脏读,通常会对缓存的数据设置失效时间,一旦超过失效时间,就要从数据库中重新加载。应用要容忍一定时间的数据不一致,如卖家已经编辑了商品属性,但是需要过一段时间才能被买家看到。还有一种策略是数据更新时立即更新缓存,不过这也会带来更多系统开销和事务一致性的问题
- 缓存可用性,缓存数据丢失或者缓存不可用不会影响到应用程序的处理,其可以从数据库直接获取数据。当缓存服务器崩溃时,数据库会因为完全不能承受如此大的压力而宕机,即缓存雪崩。
- 缓存热备等手段提高缓存可用性,当某台缓存服务器宕机时将缓存访问切换到热备服务器上。但这种设计显然有违缓存的初衷,缓存根本就不应该被当做一个可靠的数据源来使用
- 通过分布式缓存服务器集群,将缓存数据分不到集群多台服务器上可在一定程度上改善缓存的可用行。当一台缓存服务器宕机的时候,只有部分缓存数据丢失,重新从数据库加载这部分数据不会对数据库产生很大影响。
- landon:从这个例子引入了两种分布式集群的做法
- 将总体数据分别划分到分布式子服务器,一台挂掉不会影响其他(缓存的设计)
- 水平扩展,所有的分布式子服务器逻辑全部一致,一台挂掉不会影响所有(应用服务器的设计)
- 当然也有如一致性hash的算法,也可以动态的增加或减少机器
- 缓存预热,缓存存放的是热点数据,热点数据又是缓存系统利用lru对不断访问的数据筛选淘汰出来的,这个过程需要花费较长的时间。新启动的缓存系统如果没有任何数据,在重新缓存数据的过程中,系统的系统和数据库负担都不好->在缓存系统启动的时候就把热点数据加载好
- 缓存穿透,如因为不恰当的业务或者恶意攻击持续高并发的请求某个不存在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,会到数据库造成很大压力->简单的对策是将不存在的数据也缓存起来,value为null
- 分布式缓存架构
- JBoss Cache
- 需要更新同步的分布式缓存
- 该分布式缓存在集群中所有服务器中保存相同的缓存数据
- 当某台服务器有缓存更新的时候,会通知集群中其他机器更新缓存数据或者清除缓存数据
- 其通常将应用程序和缓存部署在同一台服务器上,应用程序可以从本地快速获取缓存数据,不过这种方式受限于内存。另外当集群规模较大时,缓存更新信息需要同步到集群中所有机器,代价惊人。所以大型网站很少使用
- Memcached
- 不互相通信的分布式缓存
- 大型网站需要缓存的数据量一般都很大庞大
- Memcached采用一种集中式的缓存集群管理,也被称作互不通信的分布式架构方式
- 缓存与应用分离部署,缓存系统部署在一组专门的服务器上,应用程序通过一致性hash等路由算法选择缓存服务器远程远程访问缓存数据
- 缓存服务器之间不通信,缓存集群的规模可以很容易的实现扩容,具有良好的可伸缩性
- Memcached
- 简单的通讯协议-基于文本的自定义协议:命令关键字+命令操作数,如get ,可以看到这个redis很像,所以后续很多NoSQL产品都借鉴了或直接支持这套协议
- 丰富的客户端程序,几乎支持所有主流的网站编程语言
- 高性能的网络通信,其服务端通信模块基于libevent,一个基于事件触发的网络通信程序库
- 高效的内存管理-固定空间分配(解决内存碎片问题)-LRU算法
- 互不通信的服务器集群架构:客户端路由一致性hash算法成为数据存储伸缩性架构设计的经典范式;也正是集群内服务器互不通信使得集群可以做到几乎不限制的线性伸缩
- JBoss Cache
- 异步操作
- 使用消息队列将调用异步化
- 注意由于数据写入消息队列后立即返回给有用户,数据在后续的业务校验写数据库等操作可能是失败,因此在使用消息队列进行业务异步处理后,需要适当的修改业务流程进行配合。如订单提交后,订单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队列的订单消费者进程真正处理该订单甚至商品出库后再通过邮件等通知用户订单成功
- 任何可以晚点做的事情都应该晚点再做
- 使用集群
- 使用负载均衡技术为一个应用构建一个由多台服务器组成的服务器集群,将并发请求分发到多台服务器上处理
- 代码优化
- 多线程
- 从资源利用的角度看,使用多线程的原因主要有两个:IO阻塞和多CPU
- 假设服务器上执行的都是相同类型任务:启动线程数=[任务执行时间/(任务执行时间-IO等待时间)] * CPU内核数;如果是cpu计算型任务,那么线程数最多不超过CPU内核数,因为启动再多线程,cpu也来不及调度;而如果是任务需要等待磁盘操作,网络响应,那么启动多线程有助于提高任务并发度,提高系统吞吐能力,改善系统性能
- 注意线程安全问题
- 将对象设计为无状态对象
- 使用局部对象
- 并发访问资源使用锁
- 资源复用
- 单例
- 对象池(如连接池、线程池等)
- 数据结构
- hash散列算法Time33算法:hash(i)=hash(i-1) * 33 + str[i]
- 某些相似字符串hashcode比较接近->字符串取信息指纹->再对指纹求hashcode
- 原始字符串->MD5->信息指纹->hash计算->hashcode
- 垃圾回收
- 理解垃圾回收机制
- 根据系统业务特点和对象生命周期,合理设置jvm参数等->尽量减少full gc
- 多线程
存储性能优化
- 机械硬盘 vs. 固定硬盘
- B+树(关系数据库) vs. LSM树(许多NoSQL产品)
- LSM-Tree (Log-Structured Merge-Tree)
- RAID(实现数据在多块磁盘上的并发读写和数据备份) vs. HDFS(Hadoop分布式文件系统,可配合MapReduce)
- 磁盘阵列(Redundant Arrays of Independent Disks,RAID)
- Raid0、Raid1、Raid10、Raid3、Raid5、Raid6、
- Raid技术可以通过已经实现,也可通过软件实现,该技术在传统关系数据库及文件系统中影响比较广泛,但大型网站比较喜欢使用NOSQL及分布式文件系统
- HDFS中,系统在整个存储集群中的多台服务器上进行数据并发读写和备份,可以看做在服务器集群规模上实现了类似Raid的功能
小结
- 网站性能对最终用户而言是一种主观感受,性能优化的最终目的就是改善用户的体验,使他们感觉网站很快。离开这个目的,追求技术上的所谓高性能,是舍本逐末,没有多大意义
- 用户体验的快或是慢,可以通过技术手段改善,也可以通过优化交互体验改善
- 技术是为业务服务,技术选型和架构决策依赖业务规则,离开业务发展的支撑和驱动,技术走不远,甚至会走迷路
- 前沿技术总是出现在前沿业务领域
万无一失:网站的高可用架构
网站可用性的度量与考核
- 网站可用性度量
- 业界通常用多少个9来衡量网站的可用性
- QQ的可用性是4个9,即QQ服务99.99%可用,这意味着QQ服务要保证其在所有运行时间中,只有0.01%的时间不可用,即一年中大约最多53分钟不可用
- 网站不可用时间(故障时间)= 故障修复时间点-故障发现(报告)时间点
- 网站年度可用性指标=(1-网站不可用时间/年度总时间) * 100%
- 对于大多数网站而言,2个9是基本可用;3个9是较高可用;4个9是具有自动恢复能力的高可用;5个9是极高可用性(年度不可用时间小于5分钟)
- 业界通常用多少个9来衡量网站的可用性
- 网站可用性考核
高可用的网站架构
- 网站的高可用架构设计的主要目的就是保证服务器硬件故障时服务依然可用、数据依然保存并能够被访问
- 实现高可用架构的主要手段是数据和服务的冗余备份及失效转移
- 一个典型的网站通常遵循如下的基本分层架构模型
- 典型的分层模型是三层,即应用层、服务层、数据层
- 应用层主要负责具体业务逻辑处理;服务层主要负责提供可复用的服务;数据层负责数据的存储与访问等
- 中小型网站在具体部署时通常将应用层和服务层部署在一起,而数据层则另外部署,而这也是网站架构演化的第一步
- 复杂的大型网站架构中,划分的粒度会更小、更详细、结构更加复杂,服务器规模更加庞大,但通常还是能够把这些服务器划分到这三层中:
- 应用层的服务器通常为了应对高并发的访问请求,会通过负载均衡设备将一组服务器组成一个集群共同对外提供服务。负载均衡设备通过心跳检测等手段监控某台应用服务器不可用时就将其从集群列表中剔除并将请求转发到集群中其他可用的服务器上,使整个集群保持可用,从而实现高可用
- 位于服务层的服务器情况和应用层的服务器类似,也是通过集群方式实现高可用,只是这些服务器被应用层通过分布式服务框架调用,分布式服务调用框架会在应用层客户端程序中实现软件负载均衡,并通过服务注册中心对外提供服务的服务器进行心跳检测,发现有服务不可用,立即通知客户端程序修改服务访问列表,剔除不可用的服务器
- 数据服务器上存储这数据,为了保证服务器宕机时数据不丢失,数据访问服务不中断,需要在数据写入时进行数据同步复制,将数据写入多台服务器上,实现数据冗余备份。当数据服务器宕机时,应用程序将访问切换到有备份数据的服务器上
- 网站升级的频率较高,如大型网站一周发布一次。每次网站发布都需要关闭服务,重新部署系统,整个过程相当于服务器宕机。因此网站的可用性架构设计不但要考虑实际的硬件故障引起的宕机,还要考虑网站升级发布引起的宕机而后者更加频率,不能因为系统可以接受偶尔的停机故障将降低可用性设计的标准
高可用的应用
- 应用层主要处理网站应用的业务逻辑,因为有时也被称为业务逻辑层->应用的一个显著特点是应用的无状态性
- 所谓无状态的应用是指应用服务器不保存业务的上下文信息,而仅根据每次请求提交的数据进行相应的业务逻辑处理,多个服务实例(服务器)之间完全对等,请求提交到任意服务器,处理结果都是完全一样的
- 通过负载均衡进行无状态服务的失效转移
- 负载均衡,顾名思义,主要使用在业务量和数据量较高的情况下,当单台服务器不足以承担所有的负载压力时,通过负载均衡手段,将流量和数据分摊到一个集群组成的多台服务器上,以提高整体的负载处理能力
- 目前不管是开源免费的负载均衡软件还是昂贵的负载均衡硬件,都提供失效功能
- 在网站应用中,当集群中的服务是无状态对等时,负载均衡可以起到事实上高可用的作用
- 当Web服务器集群中的服务器都可用时,负载均衡服务器会把用户发送的访问请求分发到任意一台服务器上进行处理,而当10.0.0.1宕机时,负载均衡服务器通过心跳检测机制发现服务器失去响应,就会把它从服务器列表中删除,从而请求发送到其他服务器上,这些服务器是完全一样的,请求在任何一台服务器中处理都不会影响最终的结果
- 由于负载均衡在应用层实际起到了系统高可用的作用,因为即使某个应用访问量非常少,只用 一台服务器提供服务就绰绰有余,但如果需要保证该服务可用,也必须至少部署两台服务器,使用负载均衡技术构建一个小型的集群
- 应用服务器集群的Session管理
- 事实上,业务总是有状态的,如交易类的电子商务网站,需要有购物车记录用户的购买信息等
- Web应用中将这些多次请求修改使用的上下文对象称作为Session.单机情况下,Session可由部署在服务器上的web容器如jboss管理。在使用负载均衡的集群环境中,由于负载均衡服务器可能会将请求分发到集群中任何一台应用服务器上,所以保证每次请求依然能够获得正确的Session比单机要负载的多
- 集群环境下,Session管理主要有以下几种手段:
- Session复制
- 应用服务器开启web容器的session复制功能,在集群中的几台服务器之间同步session对象
- 只能用在集群规模比较小的情况。当集群规模较大,集群服务器之间通过大量的通信进行session复制,占用服务器和大量网络资源,系统不堪负担
- 所有用户的session信息在每台服务器上都有备份,大量用户访问的情况,甚至会出现服务器内存不够session使用的情况
- 大型网站的核心应用集群就是数千台服务器,同时在线用户可达千万,该方案不适用
- Session绑定
- 利用负载均衡的源地址Hash算法实现,负载均衡服务器总是将来源统一Ip的请求分发到同一台服务器上(也可以根据cookie信息将同一个用户的请求总是分发到同一台服务器上)
- 整个会话期间,用户所有的请求都在同一台服务器上处理,即Session绑定在某台特定服务器上,保证Session总能在这台服务器上获取
- 但是这种方案不符合高可用需求,因为一旦某台服务器宕机那么该机器上的session也就不复存在了,用户请求切换到其他机器后因为没有session而无法完成业务处理.虽然大部分负载均衡服务器都提供源地址负载均衡算法,但很少有网站利用这个算法进行Session管理
- 利用Cookie记录Session
- session服务器
- 利用独立部署的Session服务器集群统一管理session
- 应用服务器每次读写session,都访问session服务器
- 这种解决方案事实上是将应用服务器的状态分离,分为无状态的应用服务器和有状态的session服务器,然后针对这两种服务器的不同特性分别设计其架构
- 对于有状态的session服务器,一种比较简单的方法是利用分布式缓存,数据库等,在这些产品的基础上进行包装,使其符合session的存储和访问要求
- Session复制
高可用的服务
- 可复用的服务模块为业务产品提供基础公共服务,大型网站中这些服务通常都独立分布式部署,被具体应用远程调用。可复用的服务和应用一样,也是无状态的服务,因此也可以使用类似负载均衡的失效转移策略实现高可用的服务
- 具体实践中的高可用服务策略
- 分级管理:运维上将服务器进行分级管理,核心应用和服务优先使用更好的硬件;同时在服务部署上也进行必要的隔离,避免故障的连锁反应,如低优先级的服务通过启动不同的线程或者部署在不同的虚拟机上进行隔离,而高优先级的服务则需要部署在不同的物理机上,核心服务和数据甚至需要部署在不同地域的数据中心
- 超时设置:在应用程序中设置服务调用超时时间,一旦超时,通信框架就抛出异常,应用程序根据服务调度策略,可选择继续重试或者将请求转移到提供相同服务的其他机器上,避免如服务端宕机、线程死锁引起的可能导致应用程序对服务端的调用失去响应情况,进而导致用户请求长时间得不到响应同时还占用应用程序的资源
- 异步调用:应用对服务的调用通过消息队列等异步方式完成;当然并非所有服务调用都可以异步调用,对于获取用户信息这类调用才用异步会延迟响应时间,得不偿失。对于那些必须确认服务调用成功才能进行下一步操作的应用也不适合使用异步调用
- 服务降级:网站访问高峰,服务可能因为大量的并发调用而性能下降,严重时会导致服务宕机。为了保证核心应用和功能的正常运行,需要对服务进行降级:
- 拒绝服务:拒绝低优先级应用的调用,减少服务调用并发数,确保核心应用正常使用;或者随机拒绝部分请求调用,节省资源,让另一部分请求得以成功,避免要死大家一起死
- 关闭功能:关闭部分不重要的服务或者服务内部关闭部分不重要的功能,以节约系统开销为重要的服务和功能让出资源
- 幂等性设计
- 服务重复调用是无法避免的,如应用调用失败会将调用请求重新发送给其他服务器,但是这个失败可能是个虚假的失败,如服务已经处理成功,但是因为网络故障应用没有收到响应,这时应用重新提交请求就会导致服务重复调用。如果该服务是一转账操作,就会产生严重后果。而应用层不需要关心是否真的失败,只要没有收到成功的调用响应,就可以认为调用失败,并重试服务调用。因为必须在服务层保证服务重复调用和调用一次产生结相同,即服务具有幂等性。
- 有些服务天生就是幂等性,如将用户性别设置为男性,不管设置多少次,结果都一样。但对于转账交易等操作,则需要通过交易编号等信息进行服务调用有效性校验,只有有效的操作才能继续进行
高可用的数据
- 保证数据存储高可用的手段主要是数据备份和失效转移机制
- 缓存的高可用,实践中争议很大。一种观点因为缓存已成为数据服务的重要组成部分,缓存服务失效则可能会导致数据库负担过高而宕机,进而影响整个网站的可用性,因为缓存服务需要实现和数据存储服务同样的高可用
- 另一种观点认为缓存服务不是数据存储服务,缓存服务器宕机因为缓存数据丢失的导致服务器负载压力过高应该通过其他手段解决,而不是提高缓存服务本身的高可用
- 扩大缓存服务器集群规模的一个简单手段就是整个网站共享同一个分布式缓存集群并通过逻辑或物理分区的方式将每个应用的缓存部署在多台服务器上,任何一台宕机因为的缓存失效都只影响应用缓存数据的一小部分,不会对应用性能和数据库负载造成太大影响
CAP原理
- 为了保证数据的高可用,通常会牺牲了另一个很重要的指标:数据一致性
- 高可用数据含义:
- 数据持久性:各种情况下都不会出现数据丢失的问题->数据备份一个或多个副本,存放在不同的物理存储设备上
- 数据可访问性:当一个数据存储设备损坏则需要访问切换到另一个存储设备上,如果这个过程不能很快完成或者完成过程中需要停止用户访问数据,那么这段时间是不可访问的
- 数据一致性:数据多份副本情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分写入失败,则会造成各个副本之间数据不一致、实践中,导致数据不一致的情形有很多种,表现形式也多种多样
- CAP原理认为:一个提供数据服务的存储系统无法同时满足数据一致性、数据可用性、分区耐受性(系统具有跨网络分区的伸缩性)
- 大型网站应用中,数据规模总是快速扩张,因为可伸缩性即分区耐受性必不可少,规模变大,机器数量也会变得庞大,这时网络和服务器故障会频繁出现,要想保证可用就必须保证分布式处理系统的高可用性。所以大型网站中,通常会选择强化分布式存储系统的可用性A和伸缩性P,而在某种程度上放弃一致性C.
- 一般说来,数据不一致通常出现在系统高并发写操作或者集群状态不稳如故障恢复、集群扩容
- 数据一致性
- 数据强一致
- 数据用户一致:数据在物理存储中的各个副本的数据可能是不一致,但终端用户访问通过纠错和校验机制,可以确定一个一致且正确的数据返回给用户
- 数据最终一致
数据备份
- 数据冷备:定期将数据复制到某种存储介质;缺点不能保证数据最终一致,因定期复制,备份中的数据比系统的数据旧如果系统数据丢失则从上个备份点开始后更新的数据就会永久丢失;同时也不能保证数据可永兴,从冷备存储中恢复数据需要较长的时间而这段时间无法访问数据,系统也不可用
- 数据热备
- 异步热备
- 存储服务器分为Master和Slave,应用程序正常只连接主存储服务器,数据写入时由master的写操作代理模块将数据写入本机存储系统后立即返回写操作成功响应,然后通过异步线程将写操作数据同步到slave
- 同步热备
- 指多分数据副本的写入操作同步完成,即应用程序收到数据服务器系统的写成功响应时,多分数据都已经写操纵成功.
- 具体实现的时候,为了提供性能,在用用程序客户端并发现向多个存储服务器同时写入数据然后等待所有存储服务器都返回操作成功的响应后再通知应用程序写操作成功
- 这种情况下,存储服务器没有主从之分,完全对等,便于管理和维护
- 异步热备
- 传统的企业级关系数据库系统几乎都提供了数据实时同步备份的机制,而一开始就为大型网站设计的各种NoSQL数据库如HBase更是将数据备份机制作为产品最主要的功能点
- 关系数据库热备机制即通常所说的master-slave同步机制,该机制不但解决了数据备份问题还该神了数据库系统性能。实践汇总,通常使用读写分离,写操作只访问master,读操作只访问slave
失效转移
- 若数据服务器集群中任何一台服务器宕机,那么应用程序针对这台服务器的所有读写操作都需要重新路由到其他服务器,保证数据访问不会丢失,这个过程叫做失效转移
- 失效确认:判断服务器宕机时系统进行失效转移的第一步
- 心跳检测
- 应用程序访问失败报告(控制中心还需要再一次进行发送心跳检测以免错误判断服务器宕机)
- 访问转移:确认某台数据存储服务器宕机后,就需要将数据读写访问路由到其他服务器上(服务器是否是存储对等,如主从结构的存储服务器其存储的数据完全一样可直接切换,而不对等存储则需要重新路由)
- 数据恢复:因为宕机所以数据存储副本会减少,必须将副本数目恢复到系统设置的值;否则再有服务器宕机时就可能出现无法访问转移
高可用网站的软件质量保证
- 网站为了保证线上系统的可用性而采取了一些与传统软件开发不用的质量保证手段
- 网站发布:发布过程事实上和服务器宕机效果相当,相当于一次提前预知的服务器宕机,通常使用发布脚本完成发布。发布过程每次关闭的都是集群中的一小部分并在发布完成后立即可以访问,因此整个发布过程不影响用户使用
- 自动化测试:目前大部分网站都采用web自动化测试技术,使用自动化测试工具或者脚本完成测试,比较流行的web自动化测试工具是ThoughtWorks开发的Selenium.大部分网站也会开发自己的自动化测试工具可以一键完成系统部署、测试数据生成、测试执行、测试报告生成等全部测试过程
- 预发布验证:即使是经过严格的测试,软件部署到线上服务器之后还是经常会出现各种问题,甚至根本无法启动服务器,主要原因是测试环境和线上环境并不相同,特别是依赖的其他服务如数据库、缓存、第三方服务如银行网银接口等或其他一些为如依赖的服务线上环境还没有准备好,都有可能导致应用故障。因此网站发布时是将测试通过的代码包发布到预发布机器上,在该机器上进行预发布验证执行典型流程等确认系统没有问题才正式发布。预发布服务器是一种特殊用途的服务器,它和线上正式服务器唯一不同的就是没有配置在负载均衡服务器上,外部用户无法访问.网站工程师通过在自己的开发用计算机上配置hosts文件绑定域名IP关系直接用IP访问预发布服务器.问题是预发布服务器连接的是真实的生产环境,验证操作都是真实有效的数据,可能会引起不可预期的问题。如测试第三方支付,商品售价数千美金,为了测试工程师将金额改为1美元,但是第二天发现大量商品以1美元价格成交.另外网站应用中强调的一个处理错误的理念是快速失败,即如果系统在启动中发现问题立刻抛出异常,停止启动让工程师介入排查错误而不是启动后执行错误的操作
- 代码控制
- 网站代码控制的核心问题是如何进行代码管理,即能保证代码发布版本的稳定正确同时又能保证不同团队的开发互不影响,因为核心应用系统和公用业务模块涉及许多团队,需要对相同的代码库进行共同开发和维护。而这些团队对同一个应用的开发维护(开发周期和发布时间点各不相同),如果控制环节出问题,则会将有问题的代码发布上线,从而导致系统故障
- SVN代码控制和发布方式
- 主干开发、分支发布:代码修改都在主干(trunk)上进行,需要发布的时候,从主干上拉一个分支(branch)发布,该分支即成为一个发布版本,如果该版本发现bug,继续在该分支上修改发布并将修改合并(merge)回主干.
- 分支开发、主干发布:任何修改都不得在主干上直接进行,需要开发一个新功能或修复一个bug时,从主干上一个分支开发,开发完成并且测试通过则合并会主干,然后从主干上进行发布,主干上的代码永远是最新发布的版本
- 前者反映目前整个应用的状态,便于管理和控制,也利于持续集成;后者各个分支独立开发,互不干扰,可以使不同发布周期的开发在同一应用中进行
- 目前网站应用开发中主要用后者
- 因为对于前者,对于同一个应用,对于不同开发周期,不同发布时间的喜丧母,有可能A项目发布的时候,B项目只开发了一半,这时候的主干代码是半成品,根本不能发布。而后者的话,只需要将A项目的分支合并会主干即可发布,不受B项目发布时间的影响
- 目前开源技术设计,Git正逐步取代svn地位,而Git对于分布式开发,分支开发能有更好的支持.
- 自动化发布
- 很多网站选择周四作为发布日,这样一周前面有三天时间可以准备发布,后面还有一天时间可以挽回错误。如果选择周五发布,发现问题就必须要周末加班
- 网站火车站发布模型
- 由于火车站发布模型是基于规则驱动的流程,所以该流程可以自动化.人的干预越少,自动化程度越高,引入故障的可能性就越小
- 灰度发布
- 大型网站的主要业务服务器集群规模非常庞大,一旦发现故障,即使想要发布回滚有需要很长时间才能完成
- 大型网站会使用灰度发布模式,将集群服务器分成若干部分,每天只发布一部分服务器,观察运行稳定没有故障,第二天继续发布一部分服务器,持续几天才把整个集群全部发布完毕,期间如果发现问题则只需要回滚已发布的一部分服务器即可
- 灰度发布也长用于用户测试,即在部分服务器上发布新版本,其余服务器保持老版本或者发布另一个版本,然后监控用户操作行为、收集用户提要报告、比较用户对两个版本的满意度,以确定最终的发布版本。这种手段也被称作AB测试
网站运行监控
- 不允许没有监控的系统上线
- 监控数据采集
- 用户行为日志收集
- 服务器端日志收集:如Apache等几乎所有web服务器都具备日志记录功能,可以记录大部分用户行为日志,开启web服务器的日志记录功能即可
- 客户端浏览器日志收集:如利用页面嵌入专门的javascript脚本收集用户的真实操作,但是比较麻烦
- 因大型网站的用户日志数据量惊人,数据存储与计算压力很大,目前许多网站逐步开发基于实时计算框架storm的日志统计与分析工具
- 服务器性能监控
- 收集服务器性能指标如系统Load、内存占用、磁盘IO、网络IO等对其尽早做出故障预警
- 网站使用比较广泛的开源性能监控工具是Ganglia,其支持大规模集群并支持以图形的方式在浏览器展示实时性能曲线
- 运行数据报告
- 监控一些与具体业务场景相关的技术和业务指标,如缓存命中率、平均响应延迟时间、待处理的任务总数等
- 应用程序需要在代码中处理运行数据采集的逻辑
- 用户行为日志收集
- 监控管理
- 系统报警:监控指标如果某个阀值意味系统可能要出现故障->需要对相关人员报警
- 失效转移:监控系统在发现故障的情况下主动通知应用,进行失效转移;当然应用程序访问失败时也会进行失败转移
- 自动优雅降级:为了应付突然爆发的访问高峰,主动关闭部分功能,释放部分系统资源;监控系统实时监控所有服务器的运行状况,根据监控参数判断应用访问负载情况
永无止境:网站的伸缩性架构
- 网站的伸缩性指不需要改变网站的软硬件设计,仅仅通过改变部署的服务器数量就可以扩大或者缩小网站的服务处理能力
- 大型系统的渐进式的演化过程中,最重要的技术手段就是使用服务器集群,通过不断的向集群中添加服务器来增加整个集群的处理能力
- 只要技术上能做到向集群中加入服务器的数量和集群的处理能力成线性关系,那么网站就可以以此手段不断提升自己的规模
- 对于如电商网站的促销活动,某个短时间内访问量和交易规模突然爆发式增长,然后又回归正常状态。此时需要网站的技术架构具有极好的伸缩性-活动期间向服务器集群中加入更多服务器及向网络服务商租借更多的网络带宽以满足用户访问;活动结束后又将这些服务器下线以节约成本
网站架构的伸缩性设计
不同功能进行物理分离实现伸缩
单一功能通过集群实现伸缩
- 将不同功能分离部署可以实现一定程度的伸缩性,但随着网站访问量的逐步增加,即使分离到最小力度的独立部署,单一的服务器也不能满足业务规模的要求
- 必须使用服务器集群,即将相同服务部署在多台服务器上构成一个集群整体对外提供服务
- 当一头牛拉不动的时候,不要去寻找一头更强壮的牛,而是用两头牛来拉车
- 集群伸缩性可分为应用服务器集群伸缩性和数据服务器集群伸缩性;而数据服务器集群也可以分为缓存数据服务器集群和存储数据服务器集群
应用服务器集群的伸缩性设计
- 应用服务器设计成无状态,即应用服务器不存储请求上下文信息
- 利用负载均衡服务器实现请求分发:感知或者可以配置集群的服务器数量;可以及时发现集群中新上线或者下线的服务器;能向新上线的服务器分发请求,停止向已下线的服务器分发请求,即实现了应用服务器集群的伸缩性
- 实现负载均衡的基础技术
- HTTP重定向负载均衡
- 利用http重定向协议实现负载均衡
- http重定向服务器是一台普通的应用服务器,其唯一的功能是根据用户的http请求计算一台真实的web服务器地址并将该web服务器地址写入http重定向响应中(302)返回给有用户浏览器
- 浏览器自动重定向请求实际的物理服务器ip,完成访问
- 优点是比较简单;缺点是浏览器需要两次请求才能完成一次访问,性能较差;重定向服务器自身的处理能力有可能成为瓶颈;http302响应码重定向有可能使搜索引擎判断为seo(Search Engine Optimization)作弊,降低搜索排名。所以实践中并不多见
- DNS域名解析负载均衡
- DNS服务器中配置多个A记录,每次域名解析请求都会根据负载均衡算法计算一个不同的ip地址返回,这样A记录中配置多个服务器就构成了一个集群并可实现负载均衡
- 其优点是将负载均衡的工作转交给dns,省去了网站管理维护负载服务器的麻烦;许多dns还支持基于地理位置的域名解析,可解析成距离用户地理最近的一个服务器地址,加快用户访问速度
- 其缺点是dns是多级解析,每一级dns都可能缓存A记录;当下线某台服务器后,即使修改了dns的A记录,要使其生效也需要较长时间,这段时间dns仍然会将域名解析到已经下线的服务器,导致用户访问失败;而且dns负载均衡的控制权在域名服务商那里,网站无法对其做更多改善和更强大的管理
- 事实上,大型网站总是部分使用dns域名解析,利用域名解析作为第一级负载均衡手段,即域名解析得到的一组服务器并不是实际提供web服务的物理服务器而是同样提供负载均衡服务的内部服务器,这组内部负载均衡服务器再进行负载均衡,将请求分发到真实的web服务器上
- 反向代理负载均衡
- IP负载均衡
- 数据链路层负载均衡
- 负载均衡算法
- 负载均衡算法
- Round Robin-轮询
- Weighted Round Robin-加权轮询
- Random-随机
- Least Connections-最少连接
- Source Hashing-源地址散列
- HTTP重定向负载均衡
分布式缓存集群的伸缩性设计
- 分布式缓存服务器集群中不同服务器中缓存的数据各不相同,缓存访问请求不可以在缓存服务器集群中的任意一台处理,必须先找到缓存有需要数据的服务器,然后才能访问(注:和部署相同应用的应用服务器集群不同,有状态-数据,数据量很大,如果缓存机器数据一样,一则失去了缓存集群的意义_就因为数据量很大才使用集群,二则集群间同步数据及其复杂)
- 必须让新上线的缓存服务器对整个分布式缓存集群影响最小,即新加入缓存的服务器后应使整个缓存服务器集群中已经缓存的数据尽可能还被访问到
- Memcached分部署缓存集群的访问模型
- 分布式缓存的一致性Hash算法
- 构造一个长度为0~2^32的整数环,即一致性hash环
- 根据节点名称的hash值将缓存服务器节点放在这个hash环上
- 根据需要缓存数据的key值计算得到其hash值
- 在hash环上顺时针查找距离这个key的hash值最近的缓存服务器节点
- 解决一致性hash算法带来的负载不均衡问题
- 使用虚拟层,将每台物理缓存服务器虚拟为一组虚拟缓存服务器,将虚拟缓存服务器的hash值放置在hash环上
- key在环上先找到虚拟服务器节点,再得到物理服务器的信息
- 分布式缓存的一致性Hash算法
数据存储服务器集群的伸缩性设计
- 数据存储服务器必须保证数据的可靠存储,任何情况下都必须保证数据的可用性和正确性
- 关系数据库集群的伸缩性设计
- 数据复制:主从-读写分离
- 业务分割:不同业务数据表部署在不同的数据库集群上-分库
- 分片:将一张表拆开分别存储在多个数据库重
- Amoeba
- Cobar
- Cobar服务器集群
- 无状态
- MySQL服务器集群的伸缩
- 数据同步迁移-Schema为单位
- Cobar服务器集群
- NoSql数据库的伸缩性设计
- 放弃了SQL和事务一致性保证(ACID)
- 强化高可用性和可所伸缩性
- HBase
- 其伸缩性主要依赖其可分裂的HRegion和可伸缩性的HDFS(分布式文件系统)
- HBase启动多个HMaster,通过Zookeeper选出一个主服务器
- 应用程序通过zookeeper获得主HMaster的地址
- 输入key获得这个key所在HRegionServer地址
- 请求HRegionServer上的HRegion,获得需要的数据
随需应变:网站的可扩展架构
- 对现有系统影响最小的情况下,系统功能可持续扩展及提升
- 扩展性
- 指对现有系统影响最小的情况下,系统功能可持续扩展或提升的能力。表现在系统基础设施稳定不需要经常变更、应用之间较少依赖和耦合,对需求变更可以敏捷响应。它是系统架构设计层面的开闭原则,即对扩展开放,对修改关闭、架构设计考虑未来功能扩展,当系统增加新功能时,不需要对现有系统的结构和代码进行修改
- 伸缩性
- 指系统能够通过增加减少自身资源规模的方式增强减少自己计算事务的能力。如果这种增减是成比例的,则为线性伸缩性。在网站架构中,通常指利用集群的方式增加服务器数量,提供系统的整体事务吞吐能力
- 利用分布式消息队列降低系统耦合性
- 事件驱动架构
- 分布式消息队列
- 消息生成者应用程序通过远程访问接口将消息推送给消息队列服务器
- 消息队列服务器将消息写入本地内存队列后立即返回给成功响应给消息生产者
- 消息队列服务器根据消息订阅者列表查找订阅该消息的消息消费者应用程序,将消息队列中的消息按照先进先出的原则将消息通过远程通信接口发行给消息消费者程序
- 伸缩性:无状态,将新服务器加入分布式消息队列集群中,通知生产者服务器更改消息队列服务器列表即可
- 可用性:如果内存队列已满则会将消息写入磁盘;消息推送模块在将内存队列消息处理完成以后,将磁盘内容加载到内存队列继续处理;为了避免消息队列服务器宕机造成消息丢失,会将消息成功发送到消息队列的消息存储在消息生产者服务器,等消息真正被消息消费者服务器处理后才删除消息;在消息队列服务器宕机后,生产者服务器会选择分布式消息队列服务器集群中其他的服务器发布消息
- 利用分布式服务打造可复用的业务平台
- 将模块独立部署,降低系统耦合性
- 纵向拆分:将一个大应用拆分为多个小应用
- 横向拆分:将复用的业务拆分处理,独立部署为分布式服务
- Web Service
- 大型网站分布式服务的需求与特点:
- 负载均衡
- 失效转移
- 高效的远程通信
- 整合异构系统
- 对应用最少浸入
- 版本管理
- 实时监控
- 分布式服务框架设计
- Facebook#Thrift
- 阿里巴巴#Dubbo
- 可扩展的数据结构
- NoSQL#ColumnFamily-->Google#Bigtable
- 利用开放平台建设网站生态圈
- API接口、协议转换、安全、审计、路由、流程
固若金汤:网站的安全架构
网站应用攻击与防御
- XSS(Cross Site Script)攻击:指黑客通过篡改网页,注入恶意html脚本,用户浏览网页时,控制用户浏览器进行恶意操作的一种攻击方式
- 消毒
- HttpOnly-进制页面JavaScript访问带有httponly属性的cookie
- 注入攻击
- SQL注入攻击
- 消毒
- 预编译-绑定参数
- OS注入攻击
- SQL注入攻击
- CSRF(Cross Site Request Forgery),跨站点请求伪造-以用户的身份伪造请求
- 表单token-随机数
- 验证码
- Referer check:http请求头中的referer域中记录着请求来源,可通过验证请求来源,验证其是否合法-图片防盗链
- 其他攻击和漏洞
- Error Code:许多Web服务器默认是打开异常信息输出的,即服务器端未处理的异常堆栈信息会直接输出到客户端浏览器,给黑客造成可乘之机.通过配置web服务器参数跳转500(表示服务器内部错误)页面到专门的错误页面即可
- HTML注释:许多开发人员会在一些服务器页面程序中使用html注释语法进行程序注释,这些注释会显示在客户端浏览器,给黑客造成攻击便利。程序发布前需要进行代码review或自动扫描,避免该漏洞
- 文件上传:如果上传一个可执行程序,则可通过该程序获得服务端命令执行能力。防御手段是设置上传文件白名单,只允许上传可靠的文件类型等
- 路径遍历:攻击者在请求的url中使用先对路径,遍历系统未开放的目录和文件。防御方法则是将js等资源文件部署在独立服务器、使用独立域名,其他文件不使用静态url访问,动态参数不包含文件路径信息
- web应用防火墙
- ModSecurity
- 网站安全漏洞扫描:根据内置规则,构造具有攻击性的url请求,模拟黑客攻击行为,用来发现网站安全漏洞的工具
信息加密技术
- 单向散列加密-不可逆
- 因人们设置密码具有一定的模式,可通过彩虹表(常用密码和对应的密文关系)等手段猜测破解
- MD5,SHA
- 对称加密
- 加密和解密使用的密钥是同一个密钥或者互相可推算
- DES、RC
- 非对称加密
- 用公钥加密的信息必须用私钥才能解开;用私钥加密的信息只有公钥才能解开
- 公钥对外界公开,私钥只有所有者知道
- 用在信息安全传输、数字签名等场合
- RSA
- 密钥安全管理
- 将密钥和算法放在一个独立的服务器上
- 将加密算法放在应用系统,密钥则放在独立服务器中,实际存储密钥中,密钥被切分成数片
信息过滤与反垃圾
电子商务风险控制
案例
淘宝
- LAMP->Java+Oracle->WebX+IBatis->JBoss替换Weblogic->Jetty替代JBoss
- WebX、AntX、Jetty
- Tair、TFS、OceanBase、TDDL
- 集群环境下分布式高可用系统的架构设计
维基百科
- GeoDNS:可将域名解析到离用户最近的服务器
- LVS:基于Linux的开源负载均衡服务器
- Squid:基于Linux的开源反向代理服务器
- Lightttpd:开源的应用服务器
- PHP
- Memcached
- Lucene:Apache出品、Java开发的开源全文搜索引擎
- MySql
- 前端性能优化:
- 核心是反向代理服务器Squid集群,请求通过lvs负载均衡分发到squid服务器,热点词条被缓存,大量请求可直接返回
- Squid缓存不能命中的请求再通过lvs转发到apache应用集群
- CDN
- 服务端性能优化
- 硬件改善
- PHP改善
- 后端性能优化
- 使用缓存
- 热点特别集中的数据直接缓存到应用服务器的本地内存中
- 缓存数据的内容尽量是应用服务器可以直接使用的格式
- 使用缓存服务器存储session对象
- memcached
- MySql
- 使用较大的服务器内存
- RAID0
- 数据库事务一致性设置在较低水平
- Master宕机则立即切换到slave,同时关闭数据库写服务
海量分布式存储系统Doris的高可用架构设计分析
- 应用程序服务器:对系统发起数据操作请求
- 数据存储服务器:负责存储数据、响应应用服务器的数据操作请求
- 管理中心服务器:负责集群管理,对数据存储集群进行监控心跳检测;集群扩容、故障恢复管理;对应用程序服务器集群地址配置信息服务等
- 数据存储服务器根据应用的可用性级别设置数据复制份数,即每个数据实际存储的副本数目
- 正常访问模型:写数据时,需要路由计算获得两台不同的服务器,同时将数据写入两台服务器;而读数据时则只需要到这两台服务器上任意一台服务读取即可
- 瞬时故障的高可用解决方案:
- 多次重试
- 可能是应用服务器自己的故障,则需要请求管理中心服务器进行故障仲裁,存储服务器进行监控心跳检测
- 临时故障的高可用解决方案:
- 数据有多分副本,读数据只需要路由选择正常服务机器即可
- 写数据,正常服务的机器依然正常写入,发生故障的机器需要将数据写入到临时存储服务器
- 等待故障服务器恢复后再将临时服务器中的数据迁移到该机器
- 永久故障的高可用解决方案
- 临时故障超时或者人工确认为永久故障,系统启动备用服务器替代原来永久失效的服务器,进入永久故障恢复
网购秒杀系统架构设计案例分析
- 秒杀系统独立部署
- 秒杀页面静态化
- 租借秒杀活动网络带宽
- 动态生成随机下单页面url-下单页面url加入由服务器端生成的随机数作为参数,秒杀开始的时候才能得到
- 使用javascript脚本控制秒杀商品页面购买按钮的点亮
- javaScript服务器集群(lightttp)、定时任务服务器、秒杀商品服务器集群(apache)、下单服务器集群(jety)、订单处理子系统、全局计数器服务器(memcached)
大型网站典型故障案例分析
- 写日志也会引发故障
- 开发人员将log输出的level全局配置为debug
- 应用程序自己的日志输出配置和第三方组件日志输出要分别配置
- 高并发访问数据库引发的故障
- SQL执行频率非常高,远超正常水平
- 这条SQL被网站首页调用
- 首页不应该访问数据库,首页需要的数据可以从缓存服务器或者搜索服务器获取
- 首页最好静态
- 高并发情况下锁引发的故障
- 程序中某个单例对象多处使用了synchronized(this)
- 某个需要远程调用的操作也被加了,这个操作偶尔会比被执行,但是每次执行都需要较长的时间才能完成,这段时间锁被占用,所有的用户线程都要等待,响应超时,这个操作执行完释放锁,其他线程迅速执行,超时解除
- 使用锁操作要谨慎
- 缓存引发的故障
- 某缺乏经验的工程师关闭了缓存服务器集群中全部的十几台memcached服务器,导致网站全部瘫痪-数据库服务器Load飙升
- 当缓存不仅仅是改善性能而且成为网站架构不可或缺的一部分时,对缓存服务器的管理就需要提高到和其他服务器一样的级别
- 应用启动不同步引发的故障
- apache+jboss,用户请求通过apache转发jboss.发布时,apache和jboss同时启动,由于jboss启动时需要加载很多应用并初始化,花费时间较长,结果jboss还没有完全启动,apache就已经启动并开始接收用户请求,大量请求被阻塞在jboss进程中,最终导致jboss崩溃
- 网站很多场景需要后台服务准备好,前台应用才能启动,否则就会导致故障
- 本例中,启动脚本应该先启动jboss,然后在脚本中不断curl访问某个特定页面,直到收到ok,才启动apache
- 大文件读写独占磁盘引发的故障
- 图片存储服务器,大部分文件只有几百k,但是有几个文件非常大,有几百m,读写这些文件一次需要几十秒。则这段时间,磁盘基本上被这个文件操作独占,导致其他用户的文件操作缓慢
- 存储的使用需要根据不同文件类型和用途进行管理
- 滥用生产环境引发的故障
- 工程师在线上生产环境进行性能压力测试,占用了大部分带宽
- 访问线上生产环境要规范,不小心就会导致大事故
- 不规范的流程引发的故障
- 工程师开发,为了测试方便,特意注释掉读取缓存的代码,结果开发完成后忘记把注释去掉,直接提交到代码库并被发布到线上环境
- 代码提交前使用diff命令进行代码比较,确认没有提交不该提交大代码
- 加强code review,代码在正式提交前必须被至少一个其他工程师做过code review,并且共同承担因代码引起的故障责任
- 不好的编程习惯引发的故障
- 程序在处理一个输入的对象时,如果不能明确该对象是否为空,必须做空指针判断
- 程序再调用其他方法时,输入的对象尽量保证不是null,必要是构造空对象
架构师
- 领导艺术
- 关注人而不是产品
- 发掘人的优秀
- 共享美好蓝图
- 共同参与架构
- 学会妥协
- 成就他人
- 职场攻略
- 发现问题,寻找突破
- 提出问题,寻求支持