基本原则
- 尽可能减少单点部件,如果无法避免则最大限度减少到达单点部件的流量,最典型的例子是位于请求调用链末端的传统关系型数据库。
- 奥卡姆剃刀原则,即如无必要,勿增实体。多级分流不是越多越好,高并发高可用不是每个系统都追求的,最简单的系统是最好的系统。
客户端缓存
状态缓存
不经过服务器,直接根据缓存信息对目标网站的状态判断,以前只有 301(永久重定向)一种,后来增加了 HSTS(HTTP Strict Transport Security)。
强制缓存
客户端无需经过任何请求,在条件满足期间一直持有和使用该资源的本地缓存副本,用户主动刷新时(F5)失效。
- Expires(HTTP/1.0)
- 直接跟截止时间,直观易懂。
- 受限于客户端本地时间,本地时间修改后可能会出现缓存提前失效或超期持有。
- 无法处理涉及用户身份认证的私有资源。
- 无法精准描述不缓存的语义,通常需要使用额外脚本或在资源后手动增加时间戳。
- Cache-Control(HTTP/1.1)
- 和 Expires 语义冲突时以 Cache-Control 为准。
- 定义了一系列标准参数,如 no-transform 禁止资源被任何形式地修改,设置后则禁止自动 GZip 压缩图片或文本提高网络性能。
协商缓存
强制缓存基于时效性,但往往无法判断资源保持不变的时长。协商缓存一致性表现更好,但需要一次变化检测的额外开销。二者不具备互斥性,可以并行存在。
- Last-Modified 和 If-Modified-Since:前者是服务器响应 Header,告知客户端资源最后修改时间,客户端下次请求会带上 If-Modified-Since,并由服务端判断这之后资源是否修改过。如未修改,则返回 304 响应,不携带消息体,节省流量,缺点是只能精确到秒级,以及无法促使内容不变但 Last-Modified 发生改变的资源有效使用缓存。
- Etag 和 If-None-Match:Etag 是资源唯一标识,一般根据文件索引节点、大小和修改时间经哈希计算获得。该机制是一致性最强的缓存机制,文件在一秒内被多次修改,或未修改但 Last-Modified 改变等情况也能正确反馈。但该机制需要单独哈希计算,因此也是性能最差的缓存机制。
失效情况 依旧生效,只有用户强制刷新(Ctrl+F5)或明确禁用缓存(DevTools)时才失效,此时客户端发请求会自动携带“Cache-Control: no-cache”。
协商缓存在用户主动刷新(F5)时
域名解析
- 流程
- 客户端首先检查本地 DNS 缓存。
- 本地 DNS 查询。
- 递归查询直至到根域名服务器。
- DNS 预取:促使浏览器提前对该域名进行预解释,避免影响传输的响应速度。
- HTTPDNS:解决 DNS 劫持问题,用基于 HTTPS 协议的查询服务替代传统的基于 UDP 的 DNS 域名解析。
传输链路
优化措施
- 合并类措施:如 CSS Sprites 合并图片,则哪怕只需要用到其中一张小图,也需要完整加载整个大图片。小文件合并为大文件在 HTTP/2 毫无好处,无法通过并发提升性能。
- 媒体内嵌:Base64 编码导致传输容量膨胀,也无法有效利用缓存。
- 合并异步请求:木桶理论,所有请求返回时间被最慢的拖累,整体响应速度下降。
- 图片放至不同子域下:导致更大的 DNS 解析负担和缓存效率的降低。
协议层面
详细内容可参考 计算机网络急救指南
- 连接复用/持久连接:存在队首阻塞问题。
- HTTP 管道:需要多方支持,推行不够成功。
- HTTP/2 多路复用:帧作为最小粒度单位。
- QUIC:快速 UDP 网络连接。
数据压缩
节约传输带宽,HTML、CSS、Script 等文本数据压缩收益高,已经被压缩存储的资源通常不会被二次压缩,空耗性能。
内容分发网络 CDN
网络传输影响因素
- 网站服务器接入运营商的链路所能提供的出口带宽。
- 用户客户端接入运营商的链路所能提供的入口带宽。
- 从网站到用户间经过的不同运营商之间互联节点的带宽。
- 从网站到用户之间的物理链路传输时延。
- CDN 能够显著改善以上第一、三、四条。
路由解析
第一个用户来访触发一次未命中缓存的 DNS 查询后,链路解析主导权即交由 CDN 的调度服务。这个 DNS 服务将根据一定的均衡策略和参数,如拓扑结构、容量、时延等,在能提供服务的 CDN 缓存节点中挑选一个最适合的,将它的 IP 代替源站的 IP 地址,返回给本地 DNS。
内容分发
CDN 获取源站资源的过程,也是其核心价值所在。
- 主动分发(Push):源站发起,对用户一侧单向透明,适用于网站要预载大量资源的场景,如双十一前各网商平台将所需资源提前推送到 CDN 缓存节点中,或手机 APP 存储空间及浏览器的 localStorage 等。
- 被动回源(Pull):由用户访问所触发全自动、双向透明的资源缓存过程。理论上首次访问较慢,但由于 CDN 网络条件远高于普通用户,不一定比用户直接访问源站慢,且该方式不适用于数据量较大的资源。
CDN 管理更新:超时被动失效和手工主动失效相结合。
CDN 应用
- 加速静态资源:本职工作。
- 安全防御:可视作源站的壁垒机,对 DDoS 攻击防御尤其有效,但本身是不安全的,一旦真实 ip 泄露,会面临高风险。
- 协议升级:实现 HTTP→HTTPS,HTTP/1.x→HTTP/2,IPv4→IPv6 等
- 状态缓存:301/302 就能缓存让客户端直接跳转等。
- 修改资源:压缩资源节省带宽,自动启用客户端缓存,提供跨域能力等。
- 访问控制:ip 黑白名单控制,流量控制。
- 注入功能:不修改源站代码的前提下为源站注入各种功能。
负载均衡
四层负载均衡性能高,七层负载均衡功能强。
数据链路层负载均衡
修改请求的数据帧中的 MAC 目标地址,将原本发向负载均衡器请求的数据帧转发到对应的服务器网卡上。 三角传输模式:只有请求经过负载均衡器,响应无需从负载均衡器返回。 效率高,但必须位于同一子网,无法跨 VLAN,因此最适合做数据中心的第一级均衡设备,连接其它下级负载均衡器。
网络层负载均衡
- IP 隧道:套娃模式,保持原数据包不变,新创建数据包,把原数据包作为新数据包的 payload。需要保证所有真实服务器与均衡器有相同虚拟 IP 地址。
- NAT 模式:运维简单,但负载均衡器很容易成为系统瓶颈。
应用层负载均衡
- 正向代理:客户端设置的、代表客户端与服务器通信的代理服务,它是客户端可知,而对服务器透明的。
- 反向代理:设置在服务器这一侧,代表真实服务器来与客户端通信的代理服务,此时它对客户端来说是透明的,七层负载均衡器就属于这一种。
- 透明代理:对双方都透明的,配置在网络中间设备上的代理服务。
网络性能四层明显更高,七层至少多一轮 TCP 握手,有跟 NAT 转发模式一样的带宽问题,通常耗费更多 CPU 用于丰富的解析规则。但相比四层,可以实现如部分安全攻击的抵御(如 SQL 注入、SYN Flood 攻击等)、链路治理措施(如服务降级、熔断和异常注入)等更多的功能。
均衡策略与实现
- 轮循均衡、权重轮循均衡
- 随机均衡、权重随机均衡
- 一致性哈希均衡
- 响应速度均衡
- 最少连接数均衡
服务端缓存
缓存:空间换时间,并且提高了系统复杂度,需要考虑缓存失效、更新、一致性等问题,因此引入需要审慎考虑。
- 缓解 CPU 压力:存储方法运行结果,节省算力,提升响应性能。
- 缓解 I/O 压力:把对网络、磁盘等较慢介质的读写访问变成对内存等较快介质的访问,把对单点部件如数据库的读写访问变为可扩缩部件如缓存中间件的访问,提升响应性能。
缓存风险
- 缓存穿透:查询不存在的数据,每次必然都不会命中缓存,而是触及末端数据库。
- 业务无法避免的可以约定在一段时间内对返回为空的依旧缓存。
- 布隆过滤器应对恶意攻击导致的缓存穿透。
- 缓存击穿:缓存中热点数据失效,大量请求全部未能命中,到达真实数据源。
- 加锁同步,以该请求数据的 Key 值为锁。
- 热点数据由开发者代码来手动管理,避免缓存刚好在大量请求来时自动失效。
- 缓存雪崩:大批不同数据的请求都击穿了缓存到达数据库,如缓存预热功能导致大量不同数据对应的缓存同时失效。
- 透明多级缓存,各服务节点一级缓存中的数据通常有不一样的加载时间。
- 缓存生存期从固定时间段改为一个时间段内的随机时间。
- 缓存污染:缓存和数据源不满足最终一致性。
- 读数据先读缓存,缓存没有再读数据源并放入缓存,响应请求。
- 写数据时先写数据源,然后失效(不是更新)掉缓存。