<大型网站技术架构> 读书笔记 - 2

146 阅读25分钟

架构

网站性能测试

不同视角下的网站性能

  • 用户视角的网站性能

    用户在浏览器上只管感受到网站响应速度快还是慢。

  • 开发人员视角的网站性能

    响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标。

  • 运维人员视角的网站性能

    网络运营商的带宽能力、服务器硬件的配置、数据中心网络架构、服务器和网络带宽的资源利用率扽。

性能测试指标

响应时间

指应用执行一个操作需要的时间,包括从发出请求开始到收到最后响应数据所需要的时间。

并发数

指系统能够同事处理请求的数目,这个数字也反映了系统的负载特性。

吞吐量

指单位时间内系统处理的请求数量,体现系统的整体处理能力。

性能计数器

它是描述服务器或者操作系统性能的一些数据指标。包括 System Load、对象与线程数、内存使用、CPU 使用、磁盘与网络 I/O 等指标。

性能测试方法

性能测试

以系统设计初期规划的性能指标为预期目标,对系统不断施加压力,验证系统在资源可接受范围内,是否能达到性能预期。

负载测试

对系统不断地增加并发请求,以增加系统压力,知道系统的某项或多项性能指标达到安全临界值。

压力测试

超过安全负载地情况下,对系统继续施加压力,知道系统崩溃或者不能再处理任何请求,以此获得系统最大压力承受能力。

稳定性测试

被测试系统在特定硬件、软件、网络环境条件下,给系统加载一定业务压力,使系统运行一段较长时间,以此检测系统是否稳定。

性能优化策略

性能分析

排查一个网站的性能瓶颈和排查一个程序的性能瓶颈的手法基本相同:检查请求处理的各个环节的日志,分析哪个环节响应时间不合理、超过预期;然后检查监控数据,分析影响性能的主要因素是内存、磁盘、网络、还是 CPU, 是代码问题还是架构设计不合理,或者系统资源确实不足。

性能优化

定位产生性能问题的具体原因后,就需要进行性能优化,根据网站分层架构,可分为 Web 前端性能优化、应用服务器性能优化、存储服务器性能优化三大类。

Web 前端性能优化

一般来说 Web 前端指网站业务逻辑之前的部分,包括浏览器加载、网站试图模型、图片服务、CDN 服务等,主要优化手段有优化浏览器访问、使用反向代理、CDN 等。

浏览器访问优化

减少 http 请求

在服务端,每个 HTTP 都需要启动独立的线程去处理。这些通信和服务的开销都很昂贵,减少 HTTP 请求的数据可有效提高访问性能。

减少 HTTP 的主要手段是 合并 CSS、 合并 JS、 合并图片。

使用浏览器缓存

对于一个网站而言,css, js, logo, 图标这些静态资源文件更新的频率都比较低,而这些文件又几乎是每次 HTTP 请求都需要的,如果将这些文件缓存在浏览器中,可以极好的改善性能。

启用压缩

在服务器端对文件进行压缩,在浏览器端对文件解压缩,可有效减少通信传输的数据量。

文件位置

css 放最上面,js 放最下面。

减少 CooKie 传输

Cookie 包含在每次请求和响应中,太大的 Cookie 会严重影响数据传输,因此哪些数据需要写入 Cookie 需要慎重考虑,尽量减少 Cookie 中传输的数据量。

CDN 加速

将一些静态资源存在的 CDN 上。

反向代理

代理服务器可以通过配置缓存功能加速 Web 请求。

应用服务器性能优化

分布式缓存

网站性能优化第一定律:有限考虑使用缓存优化性能。

合理使用缓存

以下情景不适合使用缓存:

  • 频繁修改的数据
  • 没有热点的访问

使用缓存可能带来的问题:

  • 数据不一致与脏读

    一般会对缓存的数据设置失效时间,一旦超过失效时间,就要从数据库中重新加载,因此应用要容忍一定时间的数据不一致。

  • 缓存可用性

    如果缓存服务器崩溃,数据库会因为完全不能承受如此大的压力而宕机,进而导致整个网站不可用。这种情况被称作缓存雪崩,发生这种故障,可能无法通过重启缓存服务器和数据库服务器来恢复网站访问。

    解决方法:建立分布式缓存服务器集群。

缓存的几种潜在风险及解决方案:

  • 缓存预热

    新启动的缓存系统如果没有任何数据,再重建缓存数据的过程中,系统的性能和数据库负载都不太好,那么最好在缓存系统启用时就把热点数据加载好,这个缓存预加载手段叫做缓存预热。

  • 缓存穿透

    如果因为不恰当的业务、或者恶意攻击持续高并发地请求某个不存在的数据,由于缓存没有保存该数据,所有的请求都会落在数据库上,会对数据库造成很大压力,甚至崩溃。一个简单的对策是将不存在的数据也缓存起来(其 value 值为 null)。

存储性能优化

机械硬盘 vs 固态硬盘

机械硬盘是目前最常用的一种硬盘,通过马达驱动磁手臂,带动磁头到指定的磁盘位置访问数据,由于每次访问数据都需要移动磁手臂,因此机械硬盘再数据连续访问(要访问的数据存储在连续的磁盘空间上)和随机访问(要访问的数据存储在不连续的磁盘空间) 时,由于移动磁手臂的次数相差巨大,表现差别也非常大。

固态硬盘又称作 SSD 或 Flash 硬盘,这种硬盘没有机械装置,数据存储在可持久记忆的硅晶体上,因此可以像内存一样快速随机访问。

高可用的网站架构

一个典型的网站设计通常遵循如下分层 模型:

应用层(负责具体业务逻辑处理)
服务层(负责提供可复用的服务)
数据层(负责数据的存储和访问)

中小型网站在具体部署时,通常将应用层和服务层部署在一起,而数据层则另外部署。

用户浏览器 -> 应用服务器(应用层&服务层)-> 数据库服务器

在复杂的大型网站架构中,划分的力度会更小、更详细,结构更加复杂,服务器规模更加庞大,但通常还是能够把这些服务划分到这三层中。

  • 应用层
    • 文库
    • 贴吧
    • 知道
    • 百科
  • 服务层
    • 账户服务
    • Session 服务
    • 登录服务
  • 数据层
    • 数据库服务
    • 文件服务
    • 缓存服务
    • 搜索服务

文库、贴吧、百科等属于不同产品,部署在各自独立的服务器集群上,互不相干。

这些产品又会以来一些共同的复用业务,如注册登录服务、Session管理服务、账户管理服务等,这些可复用的业务也各自部署在独立的服务器集群上。

网站升级的频率一般都非常高,大型网站一周发布一次,中小型网站一天发布几次。每次网站发布都需要关闭服务,重新部署系统,整个过程相当于服务器宕机。因此网络的可用性架构设计不但要考虑实际的硬件故障引起的宕机,还要考虑网站升级发布引起的宕机,而后者更加频繁,不能因为系统可以接受偶尔的停机故障就降低可用性设计的标准。

高可用的应用

通过负载均衡进行无状态服务的失效转移

负载均衡,顾名思义,主要是用在业务量和数据量较高的情况下,当单台服务器不足以承担所有负载压力时,通过负载均衡手段,将流量和数据分摊到一个集群组成的堕胎服务器上,以提高整体的负载处理能力。

当某个服务器宕机时,负载均衡服务器通过心跳就i安策机制发现该服务器失去响应,就会把它从服务器列表中删除,而将请求发送到其他服务器上,这些服务器是完全一样的,请求在在和一台服务器中处理都不会影响最终的结果。

服务器集群的 Session 管理

应用服务器的高可用架构设计主要基于服务无状态这一特性,但是事实上,业务总是油状态的,在交易类的电子商务网站,需要有购物车记录用户的购买信息,用户每次购买请求都是向购物车中增加商品;在社交类的网站中,需要记录用的当前登录状态、最新发布的消息及好友状态等,用户每次刷新页面都需要更新这些信息。

Web 应用中将这些多次请求修改使用的上下文对象称作绘画(Session), 单机情况下,Session 可由部署在服务器上的 Web 容器(如 JBoss) 管理。在使用负载均衡的集群环境中,由于负载均衡服务器可能会将请求分发到集群任何一台应用服务器上,所以保证每次请求依然能够获得正确的 Session 比单机时药复杂很多。

集群环境下,Session 管理主要有以下几种手段。

Session 复制

在集群中的几台服务器之间同步 Session 对象,使得每台服务器上都保存所有用户的 Session 信息。

缺点:

集群规模较大的时候,需要浪费大量的资源进行 Session 复制,如果用户量过多,可能会出现服务器内存不够 Session 使用的情况。

Session 绑定

Session 绑定可以利用负载均衡的源地址 Hash 算法实现,负载均衡服务器总是将来源于同一 IP 的请求分发到同一台服务器上,这样就不需要多台服务器同步 Session 了,但是这样做的问题是显而易见的,一旦这台服务器宕机,那就 game over 了。所以很少有网站采用这种方法。

利用 Cookie 记录 Session

将 Session 记录在客户端,每次请求的时候将 Session 放在请求中发送给服务端,服务端处理完请求后再将修改过的 Session 响应给客户端。

缺点:Cookie 大小有限制,记录信息有限;每次请求响应都需要传输 Cookie, 影响性能;如果用户关闭 Cookie , 访问就会不正常。

Session 服务器

利用独立部署的 Session 服务器(集群)统一管理 Session, 应用服务器每次读写 Sessin 时,都访问 Session 服务器。

Snipaste_2022-08-01_14-25-23.png

高可用的服务

分级管理

运维上将服务器进行分级管理,核心应用和服务优先使用更好的硬件,在运维响应速度上也格外迅速。显然,用户及时付款购物比能不能评价商品更重要,所以订单、支付服务比评价服务有更高优先级。

超时设置

在应用程序中设置服务调用的超时时间,一旦超时,通信框架就抛出异常,应用程序根据服务调度策略,可选择重试或将请求转移到提供相同服务的其他服务器上。

异步调用

应用对服务器的调用通过消息队列等异步方式完成,避免一个服务失败导致整个应用请求失败的情况。

服务降级

在网站访问高峰期,服务可能因为大量的并发调用而性能下降,严重时可能会导致服务宕机。为了保证核心应用和功能的正常运行,需要对服务进行降级。降级有两种手段:拒绝服务及关闭服务。

  • 拒绝服务

    • 拒绝低优先级应用的调用,减少服务调用并发数,确保核心应用正常使用;或者随机拒绝部分请求调用,节约资源,让另一部分请求得以成功。

      比如有时候访问显示访问失败,但是周围的人访问成功了,可能你的请求就被拒绝了。

  • 关闭功能

    • 关系部分不重要的服务,或者服务内部关闭部分不重要的功能,以节约系统开销,为重要的服务和功能让出资源。

      比如淘宝在双十一的时候会关闭评价、确认收获之类的非核心服务。

冥等性设计

应用调用服务失败后,会将调用请求重新发送到其他服务器,但是这个失败可能是虚假的失败。比如服务已经处理成功,但因为网络故障应用没有收到响应,这时应用重新提交请求就导致服务器重复调用,如果这个服务是一个转账操作,就会产生严重后果。

因此必须在服务层保证服务重复调用和调用一次产生的结果相同,即结果具有冥等性。

有些服务天然居然幂等性,比如将用户性别设置为男性,不管设置多少次,结果都一样。但是对于转账交易等操作,问题就会比较复杂,需要通过交易编号等信息进行服务调用有效性校验,只有有效的操作才能继续执行。

高可用数据

对于许多网站而言,数据是其最宝贵的物质资产(用户数据、交易数据、商品数据……)

因此,失去数据,对网站的打击可以说是毁灭性的,因此,保护网站的数据对企业来说非常重要。不同于高可用的应用和服务,由于数据存储服务器上保存的数据不同,当某台服务器宕机的时候,数据访问请求不能任意切换到集群中其他的机器上。

保证数据存储高可用的手段主要是数据备份和失效转移机制。

数据备份是保证数据有多个副本,任意副本的失效都不会导致数据的永久丢失。

而失效转移机制则是保证当一个数据副本不可访问时,可以快速切换访问数据的其他副本,保证系统可用。

高可用网站的软件质量保证

网站发布

发布时,每次关闭的服务器都是集群中的一小部分,并在发布完成后立即可以访问,因此整个发布过程不影响用户使用。

自动化测试

大型网站通常会开发自己的自动化测试工具,可以一键完成系统部署,测试数据生成、测试执行、测试报告生成等全部测试过程。

预发布验证

为了尽可能地接近真实的环境以保证系统的运行,有些企业会将测试通过的代码包发布到预发布机器上。

预发布服务器和线上正式服务器都部署在相同的物理环境,使用相同的线上配置,依赖相同的外部服务。不过它并没有配置在负载均衡服务器上,外部用户无法访问。

这种形式可能引起的一个问题是,由于使用真实数据,使用数据测试地时候,可能会对实际网站地功能产生影响。

代码控制

分支开发、主干发布更加灵活。

自动化发布

开发自动化发布的工具实现发布过程的自动化。

灰度发布

将服务器集群分成若干部分,每天只发布一部分服务器。如果运行稳定,就继续发布;出现故障就及时回滚到上一个版本,尽可能降低影响。

网站运行监控

不允许没有监控的系统上线

网站运行监控对于网站运维和架构设计优化只管重要,运维没有监控的网站,犹如驾驶没有仪表的飞机,生死尚且未卜,提高可用性、减少故障率就更无从做起了。

监控数据采集

用户行为日志采集

用户在浏览器上所做的所有操作机器所在的操作环境,包括用户操作系统与浏览器版本信息,IP 地址、页面访问路径、页面停留时间等,这些数据对统计网站 PV/UV 指标,分析用户行为、优化网站设计、个性化营销与推荐等非常重要。

具体用户行为日志收集手段有两种。

  • 服务端日志收集

    几乎所有 Web 服务器都具备日志记录功能,可以记录大部分用户行为。但是可能会出现信息失真,如 IP 地址是代理服务器地址,而不是用户真实 IP; 无法识别访问路径等。

  • 客户端浏览器日志手机。

    利用页面嵌入专门的 JS 脚本可以手机用户真实的操作行为。

服务器性能监控

系统 Load、内存占用、磁盘IO、网络IO 等对今早做出故障预警。

运行数据报告

缓存命中率、平均响应演示时间、每分钟发送邮件数据、待处理的任务总数

监控管理

  • 系统报警

    如果系统指标超过阈值,以为这系统可能将要出现故障,需要对相关人员进行报警。

    报警方式可以通过邮件、手机短信,语音报警等。

  • 失效转移

    访问失败时进行失效转移。

  • 自动优雅降级

    自动关闭部分不重要的功能。

网站的伸缩性架构

只要技术上能做到向集群中加入服务器的数量和集群的处理能力成线性关系,那么网站就可以一次为手段不断提升自己的规模,从一个服务几十人的小网站发展成服务几十亿人的大网站,从只能存储几个 G 图片的小网站发展成存储几百 P 图片的大网站 ,这就是网站系统的伸缩性架构。

网站架构的伸缩性设计

一般来叔,网站的伸缩性设计可分成两类,一类是根据功能进行物理分离实现伸缩,一类是单一功能通过集群实现伸缩。前者是不同的服务器部署不同的服务,提供不同的功能;后者是集群内的多台服务器部署相同的服务,提供相同的功能。

不同功能进行物理分离实现伸缩

网站发展早期,分离过程如下:

单一服务器处理所有服务 -> 数据库从应用服务器分离 -> 缓存从应用服务器分离 -> 静态资源从应用服务器分离

随着网站的发展,物理分离又可以分成如下两种情况。

纵向分离

网站具体产品
可复用业务服务
基础技术服务
数据库

横向分离

网站前台卖家后台买家后台交易论坛
1234

横向分离的力度可以非常小,甚至可以一个关键网页部署一个独立服务,比如对于电商网站非常重要的产品详情页面,商铺页面,搜索列表页面,每个页面都可以独立部署,专门维护。

单一功能通过集群规模实现伸缩

将不同功能分离部署可以实现一定程度的伸缩性,但是随着网站访问量的逐步增加,即使分离到最小粒度的独立部署,单一的服务器也不能满足业务规模的要求,因此必须使用服务器集群,即将相同服务器部署在多台服务器上构成一个集群整体对外提供服务。

应用服务器集群的伸缩性设计

HTTP 重定向负载均衡

微信图片_20220720135352.jpg 这种负载均衡方案的优点是比较简单,缺点是浏览器需要两次请求服务器才能完成一次访问,性能较差;重定向服务器自身的处理能力有可能成为瓶颈,整个集群的伸缩性规模有限;因此实践种使用这种方案进行负载均衡的案例并不多见。

DNS 域名解析负载均衡

微信图片_20220809173942.jpg

  • 优点

    省掉了网站管理为父负载均衡服务器的麻烦,同时许多 DNS 还支持即域地理位置的域名杰西,即会将域名解析成距离用户地理最近的一个服务器地址,这样可以i爱快用户访问速度,改善性能。

  • 缺点

    DNS 多级解析,如果需要修改,需要很久才能生效;DNS 负载均衡的控制权在域名服务商那里,网站无法对其做更多改善和更强大的管理。

实际上,大型网站总是部分使用 DNS 域名解析,利用域名解析作为第一级负载均衡手段,即域名解析得到的一组服务器并不是实际提供 Web 服务的物理服务器,而是同样提供负载均衡服务的内部服务器,这组内部负载均衡服务器再进行负载均衡,将请求分发到真实的 Web 服务器上。

反向代理负载均衡

在实际的部署中,反向代理服务器处于 Web 服务器前面,这个位置也正好是负载均衡服务器的位置,所以大多数反向代理服务器同时提供负载均衡的功能,管理一组 Web 服务器,将请求根据负载均衡算法转发到不同的 Web 服务器上。

IP 负载均衡

用户请求数据包到达负载均衡服务器 114.100.80.10 后,负载均衡服务器在操作系统内核进程获取网络数据包,根据负载均衡算法计算得到一台真实的 Web 服务器 10.0.0.1, 然后将数据目的的 IP 地址修改为 10.0.0.1,不需要通过用户进程处理。真实 Web 应用服务器处理完成后,响应数据包回到负载均衡服务器,负载均衡服务器再将数据包源地址修改为自身的 IP 地址(114.100.80.10)发送给用户浏览器。

缺点:所有的请求想用都需要经过负载均衡服务器,集群的最大响应数据吞吐量不得不受制于负载均衡服务器网卡带宽。对于提供下载服务或者视频服务等需要传输大量数据的网站而言,难以满足需求。能不能让负载均衡服务器只分发请求,而使响应数据从真实物理服务器直接返回给用户呢?

数据链路层负载均衡

微信图片_20220810144001.jpg

由于实际处理请求的真实物理服务器 IP 和数据请求目的 IP 一致,不需要通过负载均衡服务器进行地址转换,可将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成为瓶颈,这种负载均衡方式又称作直接路由方式。

目前,这种方式是目前大型网站使用最广的一种负载均衡手段。

负载均衡算法

负载均衡服务器的实现可以分成两个部分:

1.根据负载均衡算法和 Web 服务器列表计算得到集群中一台 Web 服务器的地址。

2.将请求数据发送到该地址对应的 Web 服务器上。

  • 轮询

    所有请求一次分发到每台应用服务器上。

  • 加权轮询

    根据应用服务器硬件性能的情况,在轮询的基础上,按照配置的权重将请求分发到每个服务器。

  • 随机

    随机分配到各个应用服务器。

  • 最少链接

    记录每个应用服务器正在处理的连接数,将新到的请求分发到最少连接的服务器上。

  • 源地址散列

    根据请求来源的 IP 地址进行 Hash 计算,得到应用服务器,这样来自同一个 IP 地址的请求总在同一个服务器上处理,该请求的上下文信息可以存储在这台服务器上,在一个会话周期内重复使用,从而实现会话粘滞。

分布式缓存集群的伸缩性设计

和所有服务器都部署相同应用的应用服务器集群不同,分布式缓存服务器集群中不同服务器种缓存的数据各不相同,缓存访问请求不可以在缓存服务器集群中的任意一台处理,必须先找到缓存有需要数据的服务器,然后才能访问。这个特点会严重制约分布式缓存集群的伸缩性设计,因为新上线的缓存服务器没有缓存任何数据,而已下显得缓存服务器还缓存着网站许多热点数据。

[涉及算法,暂时跳过]

网站的可拓展架构

产品是否可以快速上新,取决于产品的拓展性。

  • 拓展性( Extensibility)

    指对现有系统影响最小的情况下,系统功能可持续扩展或提升的能力。表现在系统基础设施稳定不需要经常变更,应用之间较少依赖和耦合,对需求变更可以敏捷响应。

  • 伸缩性(Scalability)

    指系统能够通过增加(减少)自身资源规模的方式增强(减少)自己计算处理事务的能力。

构建可拓展的网站架构

低耦合的系统更容易拓展,低耦合的模块梗容易复用。

如何分解系统的各个模块、如何定义各个模块的接口、如果复用组合不同的模块构造成一个完整的系统,这事软件设计中最有挑战的部分。

大型网站中,通过将模块分开部署在独立的服务器上,从物理上分离模块之间的耦合关系,进一步降低耦合性,提高复用性。

模块分布式部署以后具体聚合方式主要有分布式消息队列和分布式服务。

利用分布式消息队列降低系统耦合性

如果模块之间不存在直接调用,那么新增模块或者修改模块就对其他模块影响最小,这样系统的可拓展性无疑更好一些。

事件驱动架构

微信图片_20220811154938.jpg

消息接受者在对消息进行过滤、处理、包装后,构成一个新的消息类型,将消息继续发送出去,等待其他消息接受者订阅处理该消息。因此基于事件(消息对象)驱动的业务架构可以是一系列的流程。

分布式消息队列

队列是一种先进先出的数据结构,分布式消息队列可以看作将这种数据结构部署到独立的服务器上,应用程序可以通过远程访问接口使用分布式消息队列,进行消息存取操作,进而实现分布式的异步调用。

利用分布式服务打造可复用的业务平台

使用分布式服务是降低系统耦合性的另一个重要手段。如果说分布式消息队列通过消息对象分解系统耦合性,不同子系统处理同一个消息;那么分布式服务则通过接口分解系统耦合性,不同子系统通过相同的接口描述进行服务调用。

可拓展的数据结构

有个问题,随着业务的发展,以前的数据表字段不够用了,怎么办?

如果字段变更的非常频繁呢?

又没有什么办法能够做到可拓展的数据结构设计呢?

可使用支持 ColumnFamily 结构的 NoSQL 数据库,创建表的时候,只需要指定 ColumnFamily 的名字,无需指定字段(Column),可以在数据写入时再指定,通过这种方式,数据表可以包含数百万字段,是的应用程序的数据结构可以随意拓展。而在查询时,可以通过指定任意字段名称和值进行查询。

网站应用攻击与防御

全球大约 70% 的 Web 应用攻击都来自 XSS 攻击和 SQL 注入攻击。此外,常用的 Web 应用还包括 CSRF、Session 劫持等手段。

XSS 攻击

XSS 攻击即跨站脚本攻击。可见博文:juejin.cn/post/684490…

待完善。