写点什么

架构师训练营第 1 期 - 第 5 周学习总结

用户头像
Anyou Liu
关注
发布于: 2020 年 10 月 25 日

本周学习了分布式缓存架构、消息队列和负载均衡架构

一、分布式缓存架构

缓存是用来提升数据访问的,一般是原始数据的一个副本,比直接访问原始数据更快。缓存无处不在,有 CPU 缓存、操作系统缓存、数据库缓存、JVM 编译缓存、CDN 缓存、代理与反向代理缓存、前端缓存、应用程序缓存、分布式对象缓存等。

缓存数据一般以 hash 表的方式存储,hash 表的时间复杂度是 O(1),可以快速的访问和保存数据。简单来说,hash 表的底层是一个数组,假设有一个 hash 表的数组的长度是 16,当要保存数据到缓存中时,散列函数根据数据的键 Key 得到 hash 值,然后 hash 值对 16 取模获得数组的下标,然后把数据的值 Value 保存到数组对应下标的位置。当然,一个完整的 hash 表的设计没有这么简单,还要考虑 hash 冲突和数组扩容等问题。

缓存的关键指标

缓存的关键指标是缓存命中率,就是说当应用程序访问缓存时,每次是否都能从缓存中获取到想要的数据,每次命中的概率就是缓存命中率。如果 10 次访问缓存的请求,只有 8 次能访问到想到的数据,那么缓存命中率就是 80%

影响缓存命中率的因素

  1. 缓存键集合大小

缓存中的键数量越多,则缓存查询数据的效率越低,因为需要在大量的数据中找到想要的数据,需要更多时间。键数量越少,缓存的效率越高。

  1. 缓存可使用内存空间

缓存一般使用内存保存数据,而内存又受限于物理机的内存。如果物理机内存不够,而需要缓存的数据又多,那么每次缓存时,都需要删除老的数据,才能添加新的数据。那么应用程序访问老的数据的话,不会命中,就会导致缓存命中率变低。内存越多,能缓存的数据越多,缓存的命中率就越高。

  1. 缓存对象生存时间

缓存对象的生存时间是对象在缓存中留存的时间。对象的生存时间越短,那么下次访问缓存时,可能就不会访问到数据了,因为数据已经过期清除了。对象的生存时间越长,那么下次访问到数据时,命中的可能性就越高。

缓存的类型

  1. 代理缓存

这里的代理缓存是正向代理缓存,当用户通过代理给网站发送请求时,如果请求的页面可以在代理服务器上面找到时,代理服务器就直接返回结果。如果请求的页面不在时,则直接转发给网站的服务器。

  1. 反向代理缓存

当请求来到反向代理服务器时,反向代理服务器先检查请求的页面是否在反向代理服务器的缓存上面时,如果是,则直接返回,否则代理服务器就把请求转发给应用服务器。

  1. 多层反向代理缓存

多层反向代理是指由多层反向代理服务器集群来处理用户的请求。缓存的作用跟一般的反向代理缓存是一样的。

  1. 内容分发网络(CDN)

当用户访问网站时,请求首先来到运营商的机房,当服务器上面有用户请求的资源时,就直接发送返回结果,否则,把请求转发给应用程序。

缓存的策略

  1. 通读缓存(read-through)

通读缓存是指当访问缓存资源时,如果缓存中没有找到想到的数据,则直接请求原始的数据。代理缓存、反向代理缓存、CDN 缓存都是通读缓存。

  1. 旁路缓存(cache-aside)

旁路缓存是指当访问缓存资源时,如果缓存中数据,则直接返回。如果缓存中没有数据,则直接请求原始的数据,并把返回结果写入到缓存中。

本地对象缓存构建分布式缓存 VS 远程分布式缓存

  1. 本地对象缓存构建分布式缓存

假设有应用服务器,应用程序和缓存都部署在服务器上,应用程序会更新、访问本机的缓存,当本机的缓存更新时,通过同步的方式把本机的缓存更新到其他机器上的缓存中,其他机器的缓存也通过同步的方式把缓存同步到本机。每台机器上的缓存始终保持同步,虽然实现了分布式缓存集群,但是同样的缓存数据有 N 份拷贝,内存利用率低下,造成严重浪费资源。

  1. 远程分布式缓存

远程分布式缓存是指缓存单独部署在缓存服务器上,应用程序通过远程调用方式访问缓存服务器集群。比如 Memcached 分布式缓存集群

一致性 hash 算法

之前提到的普通 hash 算法在集群增加节点时,会导致大量的数据移动,伸缩性较差。有没有更好的路由算法呢?我们可以选择一致性 hash 算法。一致性 hash 算法是指定义一个长度为 2^32 的环,把服务器的节点映射到环上的一个位置,当保存数据到缓存中时,根据 hash 算法得到 hash 值,对应环上的一个位置,如果当前位置有服务器节点,则选择该节点作为数据对应的节点,如果当前位置没有服务器节点,则顺时针查找最近的服务器节点。

实际上一般采用把节点分成很多个虚拟节点,把虚拟节点映射到环上,当把数据保存到对应的虚拟节点时,实际上是把数据保存到物理节点上。虚拟节点数量越多,增加和减少节点时,需要迁移的数据量越少。还有,这里对一致性 hash 算法的实现有一定要求,需要 hash 算法越随机越好,如果不够随机,会导致数据分布不均衡,一些节点会存储过多的数据,而一些节点会存储过少的数据。

合理使用缓存

缓存技术简单、对系统的性能提升显著、应用场景多,堪称系统性能优化的大杀器。但是我们也要合理使用缓存,使用缓存不当,不仅不能提供系统性能,反而会增加系统开销。

  1. 频繁修改的数据

一般来说,数据读写比在 2:1 以上,缓存才有意义。反之,数据频繁修改,还没来得及访问,就从缓存中失效了。

  1. 没有热点的访问

实际应用中,一般不会把所有的数据都缓存起来,一般会把热点数据缓存起来,如果应用程序没有热点数据,不遵循二八定律,数据还没有被访问,就已经失效了。

  1. 数据不一致与脏读

一般会对数据设置失效时间,不设置失效时间的话,可能会导致缓存中获取的不是最新的数据,当失效时间过了,下次访问时,会先从数据库把数据查询出来,然后写入缓存。在并发情况下,还可能导致脏读问题,实际上可以采用 cache-aside 和 read/write-through 的更新策略来更新缓存。

  1. 缓存雪崩

缓存雪崩是指缓存中的数据在同一时间大量失效或者缓存不可用时,应用程序的请求会直接命中数据库,由于访问数据库的请求过多,导致数据库无法承受过高复杂而崩溃。

  1. 缓存预热

缓存预热是指当应用程序发布之后,人工或者自动请求应用程序,把数据库的数据更新到缓存中,这样下次外面来的用户请求就可以直接访问缓存了。否则当外面有大量并发请求过来时,可能会导致数据库的复杂过高而发生问题。

  1. 缓存穿透

缓存穿透是指用户请求的数据不在缓存中,直接会访问数据获取原始数据,当系统中有大量这样的请求时,会对数据造成很大的压力。一个简单的策略是对不存在的数据也缓存起来,并设置一个过短的失效时间。


二、消息队列

消息队列作为一种常用的中间件,是异步调用的一种体现。传统的同步调用中,当用户请求一个耗时的资源时,线程会阻塞并等待请求完成才返回给客户端。不同于异步调用,可以把耗时的请求缓存在消息队列中,线程直接返回给客户端告知请求正在处理,后台消费者可以从消息队列中获取消息并处理耗时的请求,待请求结果处理完后,异步通知客户。异步调用不阻塞当前线程,提高了系统响应速度。

消息队列异步调用架构

  1. 点对点模型

点对点模型是指消息只能被一组消费者处理,消息生产者把消息放入消息队列中,消费者从队列中获取消息并处理,处理完的消息不能被再次处理。

  1. 发布订阅模型

发布订阅模型是指消息存放在消息队列中,消费者可以自己订阅消息,其他消费者也可以订阅消息,消息可以被不同组的消费者处理。

消息队列的好处

  1. 异步处理,提升处理性能

同步模式中,用户请求需要等待所有步骤都执行完之后,才响应用户。异步模式中,可以在必要的步骤执行之后就响应客户,后续的步骤可以通过消息队列异步处理。单位时间内处理的用户请求更多了,提升了系统处理性能。

  1. 更好的伸缩性

消息存放在消息队列中,被消费者消费。当消息队列中的消息过多时,可以通过增加消费者来提高消费速度,当消息过少时,也可以减少消费者,消息队列提供了更好的伸缩性。

  1. 削峰填谷

生产者生产消息并放入队列中,消费者消费队列中的数据。一般而言,消费者的消费速度是恒定的,当系统中有大量突发访问请求时,消息可以暂存在队列中,待消费者慢慢处理完,这样整个系统的负载压力就比较平滑,不会对数据库造成很大的压力。

  1. 失败隔离和自我修复

因为生产者和消费者以消息队列为渠道,并不直接相互依赖。当生产者发生故障时,不会影响到消费者,当消费者发生故障时,也不会影响生产者。生产者和消费者相互隔离,可以独立进行发布、维护等。

  1. 服务解耦

生产者只管把消息发给消息队列,它并不关心消息被谁处理。同样的,消费者只管消费消息,它并不关心消息由谁产生的。生产者和消费者耦合度低,可以独立开发、演进、升级、维护。


三、负载均衡架构

负载均衡是指把请求分发到集群中的服务器节点,所有的请求由服务器节点共同分担,整个集群对外提供服务能力。

HTTP 重定向负载均衡

当用户发出请求时,负载均衡服务器返回 302 重定向请求给客户端,并把集群中的某台服务节点的地址放在响应头中,这样客户端浏览器接收重定向请求后,就会再请求具体的服务器节点。因为用户需要发送 2 次请求,性能低下,而且搜索引擎会认为作弊,所以一般不采用 HTTP 重定向负载均衡。

DNS 负载均衡

当用户发出请求时,请求先到达域名解析服务器,可以通过配置域名解析服务器,一个域名对应多个 ip 地址,域名解析服务器会返回其中的一个 ip 给客户端,这样客户端会向该 ip 发送真正的请求。一般大型互联网公司都使用 DNS 域名解析服务器返回二级负载均衡的 ip 地址,用户请求访问二级负载均衡的 ip 时,会再进行负载均衡才访问到应用服务器。

反向代理负载均衡

反向代理服务器是配置在互联网数据中心内部的服务器,用户的请求会先经过反向代理服务器,通过配置方向代理服务器,可以根据请求的路径转发请求到不同的应用服务器,从而实现负载均衡。反向代理服务器接收外网的请求,重新转发请求发送到内网的应用服务器,接收到响应之后,再返回给客户端浏览器,每次转发时都会重写 http 请求头和响应头,处理速度低下,不适合大型互联网应用集群。

IP 负载均衡

IP 负载均衡是指通过修改请求目标地址进行负载均衡。用户请求数据包到达负载均衡服务器后,负载均衡服务器根据负载均衡算法得到一台真实的应用服务器的 ip 地址,然后修改请求数据包的目标 ip 地址为该应用服务器的 ip 地址,当请求处理完之后,负载均衡服务器再把响应数据包的源 ip 地址修改为自身的 ip 地址并返回给客户端浏览器。因为每次请求都需要处理数据包,当有大量请求时,负载均衡服务器本身的网卡就会成为性能瓶颈,特别是视频网站和下载服务,无法满足需求。

数据链路层负载均衡

数据链路层负载均衡是指在通信协议的数据链路层修改 mac 地址进行负载均衡,在这种负载模式下,负载均衡服务器的虚拟 ip 和应用服务器的虚拟 ip 都是一样的,但是 mac 地址不一样。当请求到达负载均衡服务器时,负载均衡服务器把请求的目的 mac 地址修改为应用服务器集群中某台机器的 mac 地址,并不修改数据包的目标 ip 地址,因为应用服务器的 ip 地址和负载均衡服务器的 IP 地址一样,这样数据可以传输到应用服务器上,当服务器处理完请求后,响应就直接发送给客户端浏览器了,不需要经过负载均衡服务器,避免负载均衡服务器的网卡成为瓶颈。

负载均衡算法

  1. 轮询算法

请求被一次的分发到每台应用服务器上,适合硬件都相同的服务器集群

  1. 加权轮询算法

根据服务器的硬件配置,针对服务器配置高的节点,给予更大的权重,这样,高性能的服务器会接收到更多的请求。

  1. 随机算法

请求被随机的分配到每个应用服务器,适应硬件相同的服务器集群,如果服务器硬件不同,也可以使用加权轮询。

  1. 最少连接

记录每台应用服务器的连接数,当新的请求到来时,分发到连接数最少的服务器上。

  1. 源地址散列

对请求的 ip 地址进行 hash 算法,对应用服务器的数量取模得到一台应用服务器,该算法使得同一个 ip 地址的请求总是发到同一台节点上,实现会话粘滞。但是实际上,互联网应用会有专门的会话服务器集群来处理会话粘滞。

应用服务器集群的 session 管理

  1. session 复制

session 复制是指每台应用服务器都有记录请求的 session 状态,并通过 session 复制同步 session 状态到其他服务器,这样后续的请求如果在其他服务器处理时,也能正确的获取会话。使用 session 复制,处理速度慢,同步延迟大,而且同样的 session 数据每台服务器都有,资源利用率低。

  1. session 绑定

session 绑定是指负载均衡设备根据用户请求的 ip 地址,总是发送到同一个服务器进行处理,这样总是能获取到正确的 session。但是如果某台应用服务器宕机时,则会话数据会全部丢失。

  1. 利用 cookie 记录 session

每次用户请求时,都把 session 数据写入到 cookie 中,这样应用服务器可以从请求的 cookie 中解析出 session 数据并处理,因为每次发送都有 cookie,请求可以被不同的服务器处理。但是因为 cookie 有大小限制,不适合会话数据多的场景。

  1. session 服务器

一般会有专门的 session 服务器来管理会话状态,每次请求被不同的应用服务器处理时,应用服务器都会从会话服务器获取 session 的状态。一般 session 服务器也是集群部署,提供可靠性保障。


用户头像

Anyou Liu

关注

还未添加个人签名 2019.05.24 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营第 1 期 - 第5周学习总结