分布式存储
在单机模式下,应对Hash冲突问题,多采用链地址法或者开放寻址法来进行解决。
一致性Hash
需要中心化的确定数据存储在的服务器上
Kademlia算法
去中心化确定缓存的数据在那个设备上
高可用接入层
保证一个域名的高可用性
参考 博客
DNS轮询
一个域名配置多个IP地址,采用轮询的方式,对于不同的客户端均匀返回配置的多个IP地址
优点:
- 只需要在DNS上配置多个IP,即可实现扩容,操作简单
- 部署简单,多配置一个后端服务器即可,原系统框架不需要改变
- 采用轮询,基本上实现了负载均衡
缺点:
- 非高可用,域名服务器并不关心配置的IP是否是可用的
- 扩容不是实时的,DNS有生效的生命周期
- 暴露了太多外网IP,不方便管理
配置Nginx反代理
从域名对应的接入IP的高可用角度出发,通过nginx反代理,将流量导向多台后端服务器,提升公网IP的负载能力。
优点:
- 不需要改动DNS
- 通过nginx保证负载均衡,可用的负载策略更加多样
- 只暴露一个公网IP
- 实时扩容,响应迅速
- 保证后端服务的高可用,nginx可以把挂掉的某台后端设备的流量导向其他设备
缺点:
- 时延增加,架构更加复杂
- 反向代理仍是单节点,非高可用
Keepalived高可用方案
两台nginx组成一个集群,分别部署上keepalived,设置成相同的虚IP,保证Nginx高可用。当一台nginx服务器挂掉了,keepalived会将流量自动迁移到另一台nginx上,整个过程调用方是透明的。
实现原理,比较有趣,例如现在的负载服务器有A和B两台,A正在提供服务,B在热备。现在网络中
10.58.0.1
的IP地址对应了A的mac地址;若B发现A挂掉了,那么B会在网络中发送一个ARP报文,声明10.58.0.1
的IP地址对应的mac地址是B,从而实现将流量调节到热备设备上,继续提供服务。
优点:
- 在nginx反代理方案上保证了反代理服务器的高可用
缺点:
- 资源利用率低,只会有一台服务器处于服务阶段
- nginx仍是单点接入,负载仍有上限
垂直扩容方案
为了解决nginx的负载上限的问题,可以采用基于操作系统(lvs)、甚至是底层硬件层面(f5)的更强大的反代理服务。通过这一层,负载到多个nginx上,每个nginx再均匀的负载到多个后端服务器上,实现可以垂直扩容
感觉就是换了个比nginx更能打的来,但是单点负载的压力依旧在
优点:
- nginx也可以扩容成多台
- keepalived+VIP(虚拟IP)的方案,依旧可以保证高可用
水平扩容方案
相比于垂直扩容,结合了DNS轮询的优势,实现了
- 通过DNS轮询来线性扩展入口lvs层的性能
- 通过keepalived来保证高可用
- 通过lvs来扩展多个nginx
- 通过nginx来做负载均衡,业务七层路由
负载均衡类型
负载均衡又分为四层负载均衡和七层负载均衡。
四层负载均衡
根据数据报的目的地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。
例如TCP的三次握手,实际上是客户端和后端服务器直接进行的三次握手操作,负载均衡设备只起到了路由转发的功能。
实际工作原理可参考外网访问docker的服务的操作:修改了客户端报文的目的地址和源地址,进行报文转发
目前四层负载均衡主要应用在以TCP协议为基础的C/S架构上
七层负载均衡
七层负载均衡,也称为“内容交换”,也就是主要通过报文中的真正有意义的应用层内容,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。
以TCP为例,负载均衡设备需要与客户端和后端服务器之间均建立连接,根据客户端发送的实际内容,决定使用的后端服务器。
相比于四层负载均衡的优势:
- 相比于四层负载均衡协议,这种方式可以对客户端的请求和服务器的响应进行任意意义上的修改,极大的提升了应用系统在网络层的灵活性,例如Nginx可以灵活的修改报文头的内容。
- 更加的安全,例如SYN Flood攻击会止步于负载均衡服务器,而不会穿透到后端服务器。此外可以在七层层面设定多种策略,过滤特定报文,例如SQL Injection等应用层面的特定攻击手段,从应用层面进一步提高系统整体安全。
目前七层负载均衡主要应用基于HTTP的B/S服务上。
可以通过七层负载均衡实现读写分离,业务分离
负载均衡算法
这些方法的归纳,参考于知乎文章
轮询法
按照请求按顺序轮流分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
为了记录上次的访问位置,多线程下需要用锁吧,这效率??
随机法
通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。由概率统计理论可以得知,随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配调用量到后端的每一台服务器,也就是轮询的结果。
可以作为轮寻方法的一个改进版本,十分严格的平均会带来较高的开销,并不值得
源地址哈希法
源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。
很好的解决了需要和后端服务器维护状态的连接问题(例如session),可以结合一致性哈希,实现更均匀的负载和更容易的伸缩扩容
加权轮询法
不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
实现方法和下面的加权随机类似,一个每次增1的计数器取模后充当了随机数的角色
加权随机法
与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。
实现方法上比较巧妙,可以根据权重,为每台服务器划定不同长度的区间,然后生成随机数,看落入了哪个区间,就选择哪台服务器
例如 1-2, 2-10, 10-15; 若随机数为9,则选择第二个区间,对应第二台服务器
平滑加权轮询
在加权轮询算法中,会有一个问题,总是连续轰炸一台服务器,造成负载总是像拖拉机一样,突增突增的。平滑轮询就是为了平滑这些连续。算法如下,详细介绍参考。
初始化三台服务器的权重是
5,1,1
,
- 第一次使用默认权重,选择其中权重最大的5,然后将当前被选中的服务器的权重减去所有的权重之和,为
-2,1,1
- 将第一次选择后生成的数组,
-2,1,1
,对应加上5,1,1
,得到3,2,2
,选择权重最大的,然后将选中的服务器的权重减去所有的服务器的权重之和,为-4,2,2
- 重复步骤2,不断的选择
实际效果: AABACAA, AABACAA, AABACAA
最小连接数法
最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。
结合Nginx负载实战看看。
缓存问题
参考小林coding
为了减少存储在硬盘上的数据的访问次数,常用的做法是在中间添加一个基于内存的缓存服务,减少对硬盘的访问压力,例如下图的架构
引入缓存层会导致常见的三个问题:缓存雪崩、缓存击穿、缓存穿透 。下面分类分析
缓存雪崩
产生的原因
- 大量缓存数据同时过期
- Redis故障
针对于大量缓存同时过期的手段
- 均匀设置过期时间,避免同时过期
- 互斥锁,相同的key每次只有一个请求可以获得访问数据库的机会,其他请求需要等待这次请求的结果存入缓存之后,再从缓存中重新读取。为了避免读取数据库的请求失败,这个互斥锁还可以加上最长等待时间
- 双key策略,主key会设置过期时间、副key没有过期时间,当主key失效,触发一次读取数据库的操作,期间访问的返回值用副key来答复,读取到新数据之后,同时更新主key和副key
- 由后台定时程序负责定时更新redis中的数据,redis中的key都是永久有效的(可以提前载入数据到缓存中,被称为缓存预热)
- Redis中的key,仍会存在因为内存紧张,而被踢出的情况,解决的办法是:
- 若redis中没有相关的主键,则通过消息队列向更新程序发出更新请求
- Redis中的key,仍会存在因为内存紧张,而被踢出的情况,解决的办法是:
针对于Redis故障宕机的情况
- 服务熔断或者请求限流措施:熔断是指暂停对缓存的访问,对请求直接返回错误,等待缓存预热完成之后,再对外服务;限流是指只讲少部分请求数据发送到数据库进行处理,对于更多的流量,直接返回错误。
- 构建高可用的Redis缓存集群,主从节点,减少Redis宕机的几率
缓存击穿
- 热点数据缓存过期
这种情况可以认为是缓存雪崩的一种子情况,可以采用互斥锁或者后台更新的操作
缓存穿透
- 数据既不在缓存也不在数据库中
发生上述的情况有业务的误操作或者是黑客攻击的情况。针对这种情况有下面三种解决方案:
- 限制非法请求
- 缓存控制或者默认值
- 采用布隆过滤器,来判断数据是否存在,从而减少对数据库的这类访问(不可以杜绝,但是可以减少情况的发生)