系统之高可用(二):熔断降级

简介: 分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为级联故障效应,最终结果导致整个系统服务不可用

1.背景

分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为级联故障效应,最终结果导致整个系统服务不可用。

2.断路器

所谓 熔断 就是服务雪崩的一种有效解决方案。当指定时间窗内的请求失败率达到设定阈值时,系统将通过 断路器 直接将此请求链路断开。也就是我们上面商品服务调用库存服务在指定时间窗内,调用的失败率到达了一定的值,那么 Hystrix 则会自动将商品服务与库存服务之间的请求都断了,以免导致服务雪崩现象。降级的目的是为了解决整体项目的压力,而牺牲掉某一服务模块而采取的措施。比如说商品详情页面可以暂时不显示推荐商品信息、商品评论等等功能来保证用户能正常查看商品顺利下单。

其实断路器思想借鉴了生活中电路保险丝的原理,在电路出现问题时(电压过高、短路),保险丝自动跳闸,以保护电路中电器。实现熔断降级的比较成熟的框架有:Netflix的Hystrix,阿里巴巴的sentinel。

项目推荐:基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企业级系统架构底层框架封装,解决业务开发时常见的非功能性需求,防止重复造轮子,方便业务快速开发和企业技术栈框架统一管理。引入组件化的思想实现高内聚低耦合并且高度可配置化,做到可插拔。严格控制包依赖和统一版本管理,做到最少化依赖。注重代码规范和注释,非常适合个人学习和企业使用

Github地址https://github.com/plasticene/plasticene-boot-starter-parent

Gitee地址https://gitee.com/plasticene3/plasticene-boot-starter-parent

微信公众号Shepherd进阶笔记

交流探讨群:Shepherd_126

3.Hystrix

Netflix开源了Hystrix组件,实现了断路器模式,SpringCloud对这一组件进行了整合。 在微服务架构使用我们只需要引入如下依赖即可:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 </dependency>

然后再项目启动类加上加@EnableHystrix注解开启Hystrix。

restTemplate调用下游服务接口

使用注解@HystrixCommand注解,配置超时时间,失败的降级方法等等

    // @HystrixCommand: fallbackMethod 熔断降级调的方法必须在同一个类中
    @HystrixCommand(commandProperties = {
   
   @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "50")},   fallbackMethod = "anotherBackMethod")
    public String testHystrix(Long skuId) {
   
   
        try {
   
   
            String result = restTemplate.getForObject("http://mall-product-service/api/mall/product/sku/" + skuId, String.class);
            return result;
        } catch (Exception e) {
   
   
            log.error("调用商品详情接口失败", e);
            throw new BusinessException("调用商品详情接口失败");
        }
    }

feign调用下游服务接口

springCloud的feign注解整合了Hystrix,我们只需要在配置文件中开启即可:

feign:
  client:
    config:
      default:
        connectTimeout: 60000
        readTimeout: 60000
  hystrix:
    enabled: true

注意:我们都知道feign的底层使用ribbon实现的,设置调用接口超时时间既可以设置ribbon,也可以设置feign的,两者都设置了去feign的,但是设置feign时必须connectTimeout和readTimeout同时设置,否则不生效。同时如果开启hystrix,Feign的请求方式是先Hystrix然后Ribbon,Hystrix在最外层则熔断时间必须大于Ribbon的(ConnectTimeout + ReadTimeout)。而且如果Ribbon开启了重试机制,还需要计算对应的重试次数,保证在Ribbon的超时时间*重试次数这个时间范围内,Hystrix的熔断时间不会超时,hystrix的默认超时时间是1000ms,超过时间就会降级处理。

/**
 * 在网络请求时,可能会出现异常请求,如果还想再异常情况下使系统可用,那么就需要容错处理,比如:网络请求超时时给用户提示“稍后重试”或使用本地快照数据等等。
 *
 * Spring Cloud Feign就是通过Fallback实现的,有两种方式:
 *
 * 1、@FeignClient.fallback = ProductFeignFallback.class指定一个实现Feign接口的实现类。
 *
 * 2、@FeignClient.fallbackFactory = UserFeignFactory.class指定一个实现FallbackFactory<T>工厂接口类
 *
 * 因为Fallback是通过Hystrix实现的, 所以需要开启Hystrix,spring boot application.properties文件配置feign.hystrix.enabled=true,这样就开启了Fallback

 */
@FeignClient(name = "${micro-server.mall-product}", path = "/api/mall/product", fallback = ProductFeignFallback.class)
//@FeignClient(name = "${micro-server.mall-product}", path = "/api/mall/product", fallbackFactory = ProductFeignFactory.class)
//@FeignClient(name = "${micro-server.mall-product}", path = "/api/mall/product")
public interface ProductService {
   
   

    @GetMapping("/sku/{skuId}")
    ResponseVO<SkuInfo> getSku(@PathVariable("skuId") Long skuId);
}
@Component
@Slf4j
public class ProductFeignFallback implements ProductService {
   
   
    @Override
    public ResponseVO<SkuInfo> getSku(Long skuId) {
   
   
        log.info("执行feign接口对应的fallback方法了");
        ResponseVO responseVO = new ResponseVO();
        responseVO.setCode(200);
        responseVO.setMsg("success");
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setBrandName("自定义");
        skuInfo.setName("name");
        responseVO.setData(skuInfo);
        return responseVO;
    }
}
@Component
@Slf4j
public class ProductFeignFactory implements FallbackFactory<ProductService> {
   
   
    @Resource
    private ProductFeignFallback productFeignFallback;

    @Override
    public ProductService create(Throwable throwable) {
   
   
        log.info("执行ProductFeignFactory了");
        throwable.printStackTrace();
        log.error("异常信息:{}", throwable.getMessage());
        return productFeignFallback;
    }
}

hystrix实现原理

架构图如下:

Hystrix整个工作流如下:

1)构造一个 HystrixCommand或HystrixObservableCommand对象,用于封装请求,并在构造方法配置请求被执行需要的参数;

2)执行命令,Hystrix提供了4种执行命令的方法;

3)判断是否使用缓存响应请求,若启用了缓存,且缓存可用,直接使用缓存响应请求。Hystrix支持请求缓存,但需要用户自定义启动;

4)判断熔断器是否打开,如果打开,跳到第8步;

5)判断线程池/队列/信号量是否已满,已满则跳到第8步;

6)执行HystrixObservableCommand.construct()或HystrixCommand.run(),如果执行失败或者超时,跳到第8步;否则,跳到第9步;

7)统计熔断器监控指标;

8)走Fallback备用逻辑

9)返回请求响应

Hystrix提供了4种执行命令的方法:

execute()和queue() 适用于HystrixCommand对象,而observe()和toObservable()适用于HystrixObservableCommand对象。

execute()

以同步堵塞方式执行run(),只支持接收一个值对象。hystrix会从线程池中取一个线程来执行run(),并等待返回值。

queue()

以异步非阻塞方式执行run(),只支持接收一个值对象。调用queue()就直接返回一个Future对象。可通过 Future.get()拿到run()的返回结果,但Future.get()是阻塞执行的。若执行成功,Future.get()返回单个返回值。当执行失败时,如果没有重写fallback,Future.get()抛出异常。

observe()

事件注册前执行run()/construct(),支持接收多个值对象,取决于发射源。调用observe()会返回一个hot Observable,也就是说,调用observe()自动触发执行run()/construct(),无论是否存在订阅者。

如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run();如果继承的HystrixObservableCommand,将以调用线程阻塞执行construct()。

observe()使用方法:

1)调用observe()会返回一个Observable对象

2)调用这个Observable对象的subscribe()方法完成事件注册,从而获取结果

toObservable()

事件注册后执行run()/construct(),支持接收多个值对象,取决于发射源。调用toObservable()会返回一个cold Observable,也就是说,调用toObservable()不会立即触发执行run()/construct(),必须有订阅者订阅Observable时才会执行。

如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run(),调用线程不必等待run();如果继承的是HystrixObservableCommand,将以调用线程堵塞执行construct(),调用线程需等待construct()执行完才能继续往下走。

toObservable()使用方法:

1)调用observe()会返回一个Observable对象

2)调用这个Observable对象的subscribe()方法完成事件注册,从而获取结果

需注意的是,HystrixCommand也支持toObservable()和observe(),但是即使将HystrixCommand转换成Observable,它也只能发射一个值对象。只有HystrixObservableCommand才支持发射多个值对象。

4.sentinel

Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

sentinel基本概念

资源

资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

规则

围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

sentinel提供了控制台管理界面,可以实时监控资源以及管理规则

使用案例

流控

通过在控制台对服务某个服务的某个接口添加流控规则,例如对下面接口做流控,我们只需要找到对应服务在簇点链路下找到要限流的接口添加规则即可,当然我们也可以在流控规则新增,资源名为路径即可

image-20211027155950081

/**
 * @author fjzheng
 * @version 1.0
 * @date 2021/9/2 00:19
 */
@RestController
@RequestMapping("/sentinel")
@ResponseResultBody
@Slf4j
public class TestSentinelController {
   
   
    @Resource
    private TestSentinelService testSentinelService;

    @GetMapping("/flow/control/1")
    @ApiOperation("测试限流")
    public String limitFlow() {
   
   
        log.info("执行sentinel限流的接口的代码逻辑了");
        String str = " hello, sentinel";
        return str;
    }

}

这时候我们一秒内只能调用一次接口,超出次数就会被限流

熔断降级

例如我们在秒杀服务中调商品服务的根据商品id查询商品接口,这时候我们在秒杀服务调用链路的查询商品接口添加降级规则如下:

配置的RT(响应时间)为1ms,当多次调用超时达到系统熔断比率,这时候接下来的10s就会熔断,不会在调用该接口。

image-20211027170952065

/**
 * @author fjzheng
 * @version 1.0
 * @date 2021/9/2 00:19
 */
@RestController
@RequestMapping("/sentinel")
@ResponseResultBody
@Slf4j
public class TestSentinelController {
   
   
    @Resource
    private TestSentinelService testSentinelService;


    @GetMapping("/degrade")
    @ApiOperation("熔断降级")
    public String degrade() {
   
   
        String s = testSentinelService.degrade(1l);
        return s;
    }

}
@Service
@Slf4j
public class TestSentinelServiceImp implements TestSentinelService {
   
   
    @Resource
    private ProductService productService;


    @Override
    public String degrade(Long skuId) {
   
   
        try {
   
   
            ResponseVO<SkuInfo> responseVO = productService.getSku(skuId);
            SkuInfo skuInfo = responseVO.getData();
            log.info("skuInfo: {}", skuInfo);
            return JSONObject.toJSONString(skuInfo);
        } catch (Exception e) {
   
   
            log.error("调用商品详情接口失败", e);
            throw new BusinessException("调用商品详情接口失败");
        }
    }

    @Override
    public String customResource() {
   
   
        log.info("执行测试自定义的保护资源咯");
        StringBuilder s = new StringBuilder();
        try(Entry entry = SphU.entry("customSource")) {
   
   
            for (int i =1; i<=5; i++) {
   
   
                s.append(i);
                TimeUnit.MILLISECONDS.sleep(100);
            }
            s.append("上山打老虎");
            return s.toString();
        } catch (BlockedException | BlockException | InterruptedException e) {
   
   
            log.error("自定义资源异常信息:{}", e);
            throw new BusinessException("自定义资源被限流了");
        }
    }

    @Override
    @SentinelResource(value = "annotationCustomResource", blockHandler = "blockHandler")
    public String annotationCustomResource() throws InterruptedException {
   
   

        StringBuilder s = new StringBuilder();
        for (int i =1; i<=5; i++) {
   
   
            s.append(i);
            TimeUnit.MILLISECONDS.sleep(10);
        }
        s.append("上山打老虎1111111");
        return s.toString();
    }

    public String blockHandler(BlockException e) {
   
   
        log.error("异常信息:{}", e);
        log.info("注解的降级方法执行了");
        return "block handler";
    }
}

同时我们可以使用try抛出异常的方式、注解方式定义资源,然后在控制台根据自定义的资源名配置对应的规则即可,代码如上所示。

5.hystrix和sentinel区别

目录
相关文章
【IntelliJ IDEA】IDEA编辑器控制台显示中文乱码的解决方案
【IntelliJ IDEA】IDEA编辑器控制台显示中文乱码的解决方案
762 0
|
3月前
|
机器学习/深度学习 算法 数据可视化
脑机接口(BCI):从信号到交互的工程实践
蒋星熠Jaxonic以“星际旅人”之姿,深耕脑机接口(BCI)工程实践。本文从系统架构、信号处理到解码算法,融合代码示例与可视化,剖析EEG/EMG非侵入式方案的落地挑战。聚焦延迟、准确率与用户体验,在噪声中构建稳定闭环,探索意念交互的可解释性与可靠性,助力极客穿越“噪声星云”,驶向人脑的奇妙行星。(238字)
|
关系型数据库 PostgreSQL
PostgreSQL listagg within group (order by) 聚合兼容用法 string_agg ( order by) - 行列变换,CSV构造...
标签 PostgreSQL , order-set agg , listagg , string_agg , order 背景 listagg — Rows to Delimited Strings The listagg function transforms values from a g...
6868 0
|
10月前
|
人工智能 自然语言处理 搜索推荐
办公方式革新,手把手教你打造超实用 AI 助理
在AI重塑企业办公的时代,钉钉AI助理成为智能化变革的关键工具。通过连接企业知识库,提供智能问答服务,它能高效解答员工的各种问题,提升工作效率与体验。搭建钉钉AI助理需先整理企业知识,登录钉钉创建并个性化设置助理,添加知识文档,确保其稳定运行。这一智能助手不仅能提高沟通效率,还能降低人力成本,助力企业迈向智能化管理。
745 1
|
12月前
|
编解码 固态存储 openCL
Mastercam 2025 官方电脑配置推荐
Mastercam 推荐配置:建议使用最新版 Windows Professional;推荐 Intel i7 处理器,至少 8GB 内存(建议 32GB),NVIDIA RTX 或 AMD FirePro™/Radeon Pro 专业显卡(4GB 以上内存);双显示器设置,主显示器分辨率 1920x1080;使用 SSD 作为主驱动器;可选 3D 鼠标提升操作体验;注意杀毒软件可能影响软件运行。
1703 7
|
存储 JavaScript
vue组件的五种传值方法(父子\兄弟\跨组件)
vue组件的五种传值方法(父子\兄弟\跨组件)
|
算法 安全 大数据
隐私计算实训营第5讲-------隐私求交和隐语PSI介绍以及开发实践
隐私求交(Private Set Intersection, PSI)是利用密码学技术在不暴露数据集以外信息的情况下找到两集合的交集。隐语SPU支持三种PSI算法:ECDH(适合小数据集)、KKRT(基于Cuckoo Hashing和OT Extension,适合大数据集)和BC22PCG(使用伪随机相关生成器)。ECDH基于椭圆曲线 Diffie-Hellman,KKRT利用OT Extension实现高效处理,而BC22PCG通过压缩满足特定相关性的随机数减少通信量。此外,还有基于Oblivious Pseudo-Random Function (OPRF)的PSI协议。
1387 0
|
关系型数据库 MySQL 数据处理
针对MySQL亿级数据的高效插入策略与性能优化技巧
在处理MySQL亿级数据的高效插入和性能优化时,以上提到的策略和技巧可以显著提升数据处理速度,减少系统负担,并保持数据的稳定性和一致性。正确实施这些策略需要深入理解MySQL的工作原理和业务需求,以便做出最适合的配置调整。
1523 6
|
Java
Java list中的对象转为list,list中的对象转为map
Java list中的对象转为list,list中的对象转为map
839 1
|
监控 Java 数据处理
Spring Cloud Data Flow的实时数据处理详解
Spring Cloud Data Flow的实时数据处理详解