当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。
1.1 使用场景
服务降级主要用于什么场景呢?当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,我们可以将一些不重要或不紧急的服务或任务进行服务的延迟使用或暂停使用。
1.2 降级服务的特性
- 原因:整体负荷超出整体负载承受能力。
- 目的:保证重要或基本服务正常运行,非重要服务延迟使用或暂停使用。
- 大小:降低服务粒度,要考虑整体模块粒度的大小,将粒度控制在合适的范围内。
- 可控性:在服务粒度大小的基础上增加服务的可控性,后台服务开关的功能是一项必要配置(单机可配置文件,其他可领用数据库和缓存),可分为手动控制和自动控制。
- 次序:一般从外围延伸服务开始降级,需要有一定的配置项,重要性低的优先降级,比如可以分组设置等级1-10,当服务需要降级到某一个级别时,进行相关配置。
- 页面降级 —— 可视化界面禁用点击按钮、页面直接跳转到下一地址。
- 延迟服务 —— 如定时任务延迟处理、消息入MQ后延迟处理。
- 写降级 —— 直接禁止相关写操作的服务请求。(比如秒杀抢购,我们只更新缓存,然后异步入库)
- 读降级 —— 只读缓存(多级缓存,直接读取缓存,适用于对读一致性要求一致性不高的情况)、直接禁止读。
- 关闭服务 —— 在粒度范围内关闭服务,比如关闭相关文章推荐、关闭推荐区。
- 超时降级 —— 主要配置好超时时间和超时重试次数和机制,并使用异步机制探测恢复情况。
- 失败次数降级 —— 主要是一些不稳定的API,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况。
- 故障降级 —— 如要调用的远程服务挂掉了(网络故障、DNS故障、HTTP服务返回错误的状态码和RPC服务抛出异常),则可以直接降级。(降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据))
- 限流降级 —— 当触发了限流超额时,可以使用暂时屏蔽的方式来进行短暂的屏蔽。(将用户倒流到排队页面)
随着微服务的流行,服务之间的调用可能变得越来越复杂,一个业务流程可能需要调用五六个甚至更多服务,这就会导致,假设某个服务出现问题,严重可能出现服务器负载过高,导致服务雪崩的现象。因此为了防止此现象的发生,决定了解下服务熔断机制,根据自身业务的需求,将其应用到服务中。
定义:在微服务架构中,扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。(可以联想到我们家里的电表的保险丝,当电压负载过高后,保险丝熔断,确保家里的电器等其他安全。)
2.1 服务熔断需要考虑的设计
- 异常处理:调用受熔断器保护的服务的时候,我们必须要处理当服务不可用时的异常情况。这些异常处理通常需要视具体的业务情况而定。比如,如果应用程序只是暂时的功能降级,可能需要切换到其它的可替换的服务上来执行相同的任务或者获取相同的数据,或者给用户报告错误然后提示他们稍后重试。
- 异常的类型:请求失败的原因可能有很多种。一些原因可能会比其它原因更严重。比如,请求会失败可能是由于远程的服务崩溃,这可能需要花费数分钟来恢复;也可能是由于服务器暂时负载过重导致超时。熔断器应该能够检查错误的类型,从而根据具体的错误情况来调整策略。比如,可能需要很多次超时异常才可以断定需要切换到断开状态,而只需要几次错误提示就可以判断服务不可用而快速切换到断开状态。
- 日志:熔断器应该能够记录所有失败的请求,以及一些可能会尝试成功的请求,使得的管理员能够监控使用熔断器保护的服务的执行情况。
- 测试服务是否可用:在断开状态下,熔断器可以采用定期的ping远程的服务或者资源,来判断是否服务是否恢复,而不是使用计时器来自动切换到半断开状态。这种ping操作可以模拟之前那些失败的请求,或者可以使用通过调用远程服务提供的检查服务是否可用的方法来判断。
- 手动重置:在系统中对于失败操作的恢复时间是很难确定的,提供一个手动重置功能能够使得管理员可以手动的强制将熔断器切换到闭合状态。同样的,如果受熔断器保护的服务暂时不可用的话,管理员能够强制的将熔断器设置为断开状态。
- 并发问题:相同的熔断器有可能被大量并发请求同时访问。熔断器的实现不应该阻塞并发的请求或者增加每次请求调用的负担。
- 资源的差异性:使用单个熔断器时,一个资源如果有分布在多个地方就需要小心。比如,一个数据可能存储在多个磁盘分区上(shard),某个分区可以正常访问,而另一个可能存在暂时性的问题。在这种情况下,不同的错误响应如果混为一谈,那么应用程序访问的这些存在问题的分区的失败的可能性就会高,而那些被认为是正常的分区,就有可能被阻塞。
- 加快熔断器的熔断操作:有时候,服务返回的错误信息足够让熔断器立即执行熔断操作并且保持一段时间。比如,如果从一个分布式资源返回的响应提示负载超重,那么应该等待几分钟后再重试。(HTTP协议定义了”HTTP 503 Service Unavailable”来表示请求的服务当前不可用,他可以包含其他信息比如,超时等)
- 重复失败请求:当熔断器在断开状态的时候,熔断器可以记录每一次请求的细节,而不是仅仅返回失败信息,这样当远程服务恢复的时候,可以将这些失败的请求再重新请求一次。
2.2 golang的熔断组件
package main
import (
"errors"
"log"
"math/rand"
"third/hystrix"
"time"
)
func init() {
hystrix.ConfigureCommand("seckill", hystrix.CommandConfig{
Timeout: 1, //cmd的超时时间,一旦超时则返回失败
MaxConcurrentRequests: 1, //最大并发请求数
RequestVolumeThreshold: 1, //熔断探测前的调用次数
SleepWindow: 1000, //熔断发生后的等待恢复时间
ErrorPercentThreshold: 10,//失败占比
})
}
var Gcount, Gerror int
func testHystrix() error {
query := func() error {
var err error
r := rand.Float64()
Gcount++
if r < 1 {
err = errors.New("bad luck")
Gerror++
return err
} else {
time.Sleep(20 * time.Millisecond)
}
return nil
}
service := "seckill"
var err error
err = hystrix.Do(service, func() error {
err = query()
return err
}, nil)
return err
}
func main() {
for i := 0; i < 10; i++ {
err := testHystrix()
if err != nil {
log.Printf("testHystrix error:%v", err)
}
}
log.Printf("Gcount:%d Gerror:%d", Gcount, Gerror)
}
结果:调用了一次,失败一次。
2019/05/09 15:15:15 testHystrix error:bad luck
2019/05/09 15:15:15 hystrix-go: opening circuit seckill
2019/05/09 15:15:15 testHystrix error:hystrix: circuit open
2019/05/09 15:15:15 testHystrix error:hystrix: circuit open
2019/05/09 15:15:15 testHystrix error:hystrix: circuit open
2019/05/09 15:15:15 testHystrix error:hystrix: circuit open
2019/05/09 15:15:15 testHystrix error:hystrix: circuit open
2019/05/09 15:15:15 testHystrix error:hystrix: circuit open
2019/05/09 15:15:15 testHystrix error:hystrix: circuit open
2019/05/09 15:15:15 testHystrix error:hystrix: circuit open
2019/05/09 15:15:15 testHystrix error:hystrix: circuit open
2019/05/09 16:02:56 Gcount:1 Gerror:1
三. 服务熔断与服务降级比较
1.服务熔断对服务提供了proxy,防止服务不可能时,出现串联故障(cascading failure),导致雪崩效应。
2.服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑。
3.共性:
- 目的 -> 都是从可用性、可靠性出发,提高系统的容错能力。
- 最终表现->使某一些应用不可达或不可用,来保证整体系统稳定。
- 粒度 -> 一般都是服务级别,但也有细粒度的层面:如做到数据持久层、只许查询不许增删改等。
- 自治 -> 对其自治性要求很高。都要求具有较高的自动处理机制。
4.区别:
- 触发原因 -> 服务熔断通常是下级服务故障引起;服务降级通常为整体系统而考虑。
- 管理目标 -> 熔断是每个微服务都需要的,是一个框架级的处理;而服务降级一般是关注业务,对业务进行考虑,抓住业务的层级,从而决定在哪一层上进行处理:比如在IO层,业务逻辑层,还是在外围进行处理。
- 实现方式 -> 代码实现中的差异。