一、服务雪崩
微服务架构是将单个应用程序被划分成各种小而连接的服务,每一个服务完成一个单一的业务功能。相对于传统的单体服务,微服务具有隔离性、技术异构性、可扩展性以及简化部署等优点。通常一个应用由多个微服务组成,微服务之间的数据交互需要通过远过程调用的方式完成。
下图是一个微服务之间互相调用的场景:
微服务A调用微服务B、C和D,微服务C又调用微服务E。假设某一时刻,微服务E变为不可用。微服务C需要等待微服务E返回结果,于是请求就会逐渐堆积在微服务C,形成阻塞。随着微服务C堆积的请求不断增加,微服务A也会随之慢慢阻塞。因为服务器所能支撑的并发数有限,所以最终会耗尽服务器资源,从而导致调用链条上更多的微服务不可用,形成雪崩效应。这种由一个服务崩溃导致整条服务链崩溃的情况,我们就称之为服务雪崩。
在微服务架构中,通常有两种情况会导致服务雪崩:
突发性的访问量剧增,超出了服务处理极限
调用链条上某个服务出现故障或者响应慢(延迟)
针对以上这两种情况,产生了对应的解决方案:服务限流和服务熔断。
二、 服务限流
服务限流是指在一定时间段内限制服务的请求量以保护系统,主要用于防止突发流量而导致的服务崩溃,比如秒杀、抢购、双十一等场景,也可以用于安全目的,比如应对外部暴力攻击。
常用的限流算法有以下几种:
计数器算法
通过维护一个内部计数器,对一段时间的服务请求进行累计,判断计数器是否达到预先设定的阈值。如果没有达到阈值,就允许请求通过,并且计数器加1;如果达到阈值,则拒绝服务,抛弃请求。进入下一个计时周期后,计数器清零,重新计数。
计数器算法是限流算法中最简单的算法,缺点是有突刺现象,不仅请求通过的速率不均匀,而且请求输出的速率也不均匀,对后续处理的并发要求比较高。比如:设定服务周期为1秒,请求的上限阈值为1000。如果前1ms内已经收到1000个请求,那么剩下的时间都只能拒绝,而且后端服务需要并发处理1000个请求。
漏桶算法
漏桶算法的原理可以这样理解,将服务请求想象成流入漏桶的水,漏桶中的水以恒定的速率从桶底流出,当流入漏桶的水速度过快,超过了漏桶容量时,则直接溢出。所以,漏桶算法能够控制服务请求按照固定速率均匀输出,平滑突发流量,实现流量整形,为后续处理提供一个稳定的流量。但是,漏桶算法无法控制请求按照一定的速率均匀输入。
令牌桶算法
令牌桶算法是速率限制(Rate Limiting)和流量整形(Traffic Shaping)中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。
首先设定一个可以存放固定数量的令牌桶,使用某种机制以恒定的速度产生令牌。每次请求调用时,都必须去桶中获取令牌,只有拿到令牌,才能放行,否则只能等待可用的令牌,或者直接拒绝。如果桶中的令牌消耗的速度小于产生的速度,令牌就会不断地增多,直至填满,再产生的令牌就会从桶中溢出。
所以,令牌桶算法既可以控制请求均匀输入的速度,又可以控制请求的均匀输出速率。
三、服务熔断
我们在各种场景下都会接触到熔断这两个字。高压电路中,如果某处电压过高,熔断器就会熔断,对电路进行保护。股票交易中,如果股票涨跌幅过大,也会采用熔断机制,暂停交易,来控制交易风险。
同样,在微服务架构中,熔断机制也是起着类似的作用。当调用链路中的某个微服务长时间不可用或者有延迟,响应过慢,系统就会熔断对该节点微服务的调用,快速返回错误信息。当监控到该微服务正常工作后,再次恢复该调用链路。
四、Hystrix
服务熔断和服务限流作为微服务架构中作为服务治理的重要手段,在很多开源框架或者产品中都包含了此类功能。比如阿里dubbo,Netflix Hystrix,go-micro,go-kit等。
Hystrix是Netflix公司的开源项目,它是一个延迟和故障容错库,旨在隔离对远程系统、服务和第三方库的访问点,防止级联故障,并在无法避免发生故障的复杂分布式系统中实现了弹性。
Hystrix项目使用了java语言开发,代码托管地址为:https://github.com/Netflix/Hystrix。另外,有人使用go语言将该项目进行了移植,代码托管地址为:https://github.com/afex/hystrix-go/hystrix。
Hystrix可以做到以下事情:
- 通过控制延迟和故障来保障第三方服务调用的可靠性
- 在复杂的分布式系统中防止级联故障,防止雪崩
- 快速失败、快速恢复
- 回退并优雅降级
- 提供近实时监控、报警和操作控制
Hystrix 能使你的系统在出现依赖服务失效的时候,通过隔离系统所依赖的服务,防止服务级联失败,同时提供失败回退机制,更优雅地应对失效,并使你的系统能更快地从异常中恢复。
使用Hystrix 非常简单:
package main
import (
"github.com/afex/hystrix-go/hystrix"
"log"
)
func main() {
// 每一类微服务、远程系统、第三方库的调用都被包装为一个命令,
// 调用之前需要先行设置相关的配置信息
hystrix.ConfigureCommand(
"ServiceA", // 命令名称
hystrix.CommandConfig{
Timeout: 1000, // 超时时间设置(毫秒)
MaxConcurrentRequests: 1, // 最大并发请求数
ErrorPercentThreshold: 50, // 错误率阈值,超过阈值将熔断服务
SleepWindow: 5000, // 服务熔断后,过多长时间,熔断器再次检测是否开启(毫秒)
RequestVolumeThreshold: 20, // 服务熔断前,所需的最小请求量,请求阈值 熔断器是否打开首先要满足这个条件;这里的设置表示至少有5个请求才进行ErrorPercentThreshold错误百分比计算
})
// 使用hystrix调用远程服务,并根据命令名称启用ConfigureCommand中设置的参数
_ = hystrix.Do(
"ServiceA", // 命令名称
// 运行函数
func() error {
// 调用远程服务
log.Println("invoke a remote service")
return nil
},
// 失败回退函数
func(err error) error {
log.Println("fall back")
return err
},
)
}
注:
本文全部源代码位于:https://github.com/wangshizebin/micro-service
本文时候用的开发工具为:goland 来自 嗖嗖下载