#微服务 一些常见的熔断组件

前言:何为熔断?

    在我们进行系统设计时,必须要考虑系统的高性能、高并发、高可用。高可用中有三大利器:熔断、限流、降级。在服务的依赖调用中,被调用方出现故障时,出于自我保护的目的,调用方会主动停止调用,并根据业务需要进行相应处理。调用方这种主动停止调用的行为我们称之为熔断。
#微服务    一些常见的熔断组件
为什么要熔断 ?


    假定服务A依赖服务B,当服务B处于正常状态,整个调用是健康的,服务A可以得到服务B的正常响应。当服务B出现故障时,比如响应缓慢或者响应超时,如果服务A继续请求服务B,那么服务A的响应时间也会增加,进而导致服务A响应缓慢。如果服务A不进行熔断处理,服务B的故障会传导至服务A,最终导致服务A也不可用。



本文将介绍目前市面上比较常见的开源熔断组件!!!


hystrix

1、简单使用

引入依赖
版本说明:sprinboot :2.3.2.RELEASE
springboot:2.6.7版本需要spring-cloud-starter-**,需要3.3.#版本以上的,但是spring-cloud-starter-## 3.1.#版本以上已经将hystrix移除,需要单独引入

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>

启动类注解

@EnableHystrix//开启hystrix
@SpringBootApplication
public class FusingdemoApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(FusingdemoApplication.classargs);
   }
 
}

服务降级

  //单一降级方法
    @RequestMapping("/hystrix")
    @HystrixCommand(fallbackMethod = "errorMethod")
    public String test(@RequestParam("data") String data){
        if (!data.equals("nykj")){
            throw new RuntimeException("运行时异常");
       }
        logger.info("正常执行 data:{}",data);
        return "ok";
   }
 //发生异常或者执行时间超过指定超时时间会执行降级方法
    public String errorMethod(String data){
        logger.info("降级执行,errorMethod data:{}",data);
        return "error";
   }


2、使用fallbackFactory

结合openfeign使用,定义异常回调类
引入openfeign

  <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>

启动类添加@EnableFeignClients注解,启动openfeign

定义feign接口

@Repository
@FeignClient(fallbackFactory = HystrixFallBack.class,url "localhost:8080",name = "feignTest")
public interface FeignTest {
 
    @RequestMapping("/src")
    public String test();
 
}

//fallbackFactory降级回调处理类

回调处理类

@Component
public class HystrixFallBack implements FallbackFactory<FeignTest{
 
    private Logger logger = LoggerFactory.getLogger(HystrixFallBack.class);
 
    @Override
    public FeignTest create(Throwable throwable) {
        return new FeignTest() {
            @Override
            public String test() {
                logger.error("FeignTest test接口调用异常,异常信息:{}",throwable.getMessage());
                logger.info("hystrixFallBack FeignTest");
                return "hystrixFallBack FeignTest";
           }
       };
   }
}

添加配置

feign.hystrix.enabled=true

控制层调用

 @RequestMapping("/test02")
    public String test02() throws InterruptedException {
        String test = feignTest.test();
        logger.info("test02 feignTest.test result,{}",test);
        return test;
   }


3、熔断

启动类添加注解

@EnableFeignClients
@EnableCircuitBreaker//开启熔断
@EnableHystrix
@SpringBootApplication
public class FusingdemoApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(FusingdemoApplication.classargs);
   }
}

示例
#微服务    一些常见的熔断组件
说明:当断路器断开后,每五秒【默认五秒】会接收一次正常请求,如果响应成功,则闭合断路器,失败则重新计时

4、配置信息

//设置隔离策略,THREAD 表示线程池 SEMAPHORE:信号池隔离
execution.isolation.strategy= "THREAD"
//当隔离策略选择信号池隔离的时候,用来设置信号池的大小(最大并发数)
execution.isolation.semaphore.maxConcurrentRequests= "10"
//配置命令执行的超时时间
execution.isolation.thread.timeoutinMilliseconds= "10"
//是否启用超时时间
execution.timeout.enabled= "true"
//执行超时的时候是否中断
execution.isolation.thread.interruptOnTimeout= "true"
//执行被取消的时候是否中断
execution.isolation.thread.interruptOnCancel= "true"
//允许回调方法执行的最大并发数
fallback.isolation.semaphore.maxConcurrentRequests= "10"
//服务降级是否启用,是否执行回调函数
fallback.enabled= "true"
//是否启用断路器
circuitBreaker.enabled= "true"
//该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为 20 的时候,
//如果滚动时间窗(默认10秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。
circuitBreaker.requestVolumeThreshold= "20"
//该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在请求数量超过
circuitBreaker.requestVolumeThreshold 的情况下,如果错误请求数的百分比超过50,
//就把断路器设置为 "打开" 状态,否则就设置为 "关闭" 状态。
circuitBreaker.errorThresholdPercentage= "50"
//该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后,
//会将断路器置为 "半开" 状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为 "打开" 状态,
//如果成功就设置为 "关闭" 状态。
circuitBreaker.sleepWindowinMilliseconds= "5000"
//断路器强制打开
circuitBreaker.forceOpen= "false"
//断路器强制关闭
circuitBreaker.forceClosed= "false"
//滚动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间
metrics.rollingStats.timeinMilliseconds= "10000"
//该属性用来设置滚动时间窗统计指标信息时划分"桶"的数量,断路器在收集指标信息的时候会根据
//设置的时间窗长度拆分成多个 "桶" 来累计各度量值,每个"桶"记录了一段时间内的采集指标。
//比如 10 秒内拆分成 10 个"桶"收集这样,所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常
metrics.rollingStats.numBuckets= "10"
//该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。
metrics.rollingPercentile.enabled= "false"
//该属性用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
metrics.rollingPercentile.timeInMilliseconds= "60000"
//该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。
metrics.rollingPercentile.numBuckets= "60000"
//该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数,
//就从最初的位置开始重写。例如,将该值设置为100, 滚动窗口为10秒,若在10秒内一个 “桶 ”中发生了500次执行,
//那么该 “桶” 中只保留 最后的100次执行的统计。另外,增加该值的大小将会增加内存量的消耗,并增加排序百分位数所需的计算时间。
metrics.rollingPercentile.bucketSize= "100"
//该属性用来设置采集影响断路器状态的健康快照(请求的成功、 错误百分比)的间隔等待时间。
metrics.healthSnapshot.intervalinMilliseconds= "500"
//是否开启请求缓存
requestCache.enabled= "true"
HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中
requestLog.enabled= "true"

官网:

https://github.com/Netflix/Hystrix/wiki/How-To-Use 


文档查阅: 

http://t.zoukankan.com/xiaohanlin-p-8012561.html https://www.cnblogs.com/ming-blogs/p/14596721.html




sentinel:

sentinel工作:

在项目创建资源时,会创建一系列的功能插槽【slot chain】,这些slot chain大致分为两种类型【数据构建部分、判断部分】
基础slot:

  • NodeSelectorSlot: 建立树状结构(调用链路)
  • ClusterBuilderSlot: 根据资源保存统计簇点
  • StatisticSlot: 实时数据统计
  • FlowSlot: 流量控制
  • DegradeSlot: 熔断降级
  • SystemSlot: 系统负载保护

注意: Sentinel 仅支持 JDK 1.8 或者以上版本。

1、sentinel简单使用

springboot 版本:2.3.2.RELEASE
spring-cloud-starter-alibaba-sentinel:2.2.1.RELEASE

1、引入依赖

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  <version>2.2.1.RELEASE</version>
</dependency>


2、配置文件

spring.application.name=fusingdemo #指定服务名,sentinel-dashboard上根据该配置进行显示
spring.cloud.sentinel.transport.dashboard=localhost:8080 #接入的sentinel-dashboard地址
spring.cloud.sentinel.transport.port=8179 #该端口用于与sentinel-dashboard沟通


3、使用SentinelResource注解

@SentinelResource(value = "test02",fallback = "errorMethod",blockHandler = "blockFallback")
@RequestMapping("test02")
public String test02(@RequestParam("data") String data) throws InterruptedException {
    String s = sentinelService.test02();
    Thread.sleep(110);
    System.out.println(s);
    return s;
}

//fallback方法要求入参与接口一直,或者可以加多一个Throuwable,降级处理方法可以在其他类中定义
public String errorMethod(String data,Throwable throwable){
    logger.error("触发降级处理 errorMethod");
    logger.error(throwable.getMessage());
    return "触发降级处理 errorMethod";
}

//blockHandler方法要求入参与接口一直,或者可以加多一个BlockException参数
//流通触发的降级会优先进入blockHandler方法,如果没有则进入fallback方法
public String blockFallback(String data, BlockException ex){
    logger.error("触发流控限流处理,blockFallback");
    ex.printStackTrace();
    return "触发流控限流处理,blockFallback";
}


4、外置类fallbackClass使用

【降级方法与业务代码分离开】

@SentinelResource(value = “test03”,fallback = “test03fallBack”,blockHandler = “test03blockFallback”,
           fallbackClass = SentinelFallBackDemo.class,blockHandlerClass = SentinelFallBackDemo.class)


sentinelFallBackDemo
//外置类定义的降级方法需要时静态方法

public static String test03fallBack(Throwable throwable){
    logger.error("触发降级处理 test03fallBack");
    logger.error(throwable.getMessage());
    return "触发降级处理 test03fallBack";
}
 
public String test03blockFallback(BlockException ex){
    logger.error("触发流控限流处理,test03blockFallback");
    ex.printStackTrace();
    return "触发流控限流处理,test03blockFallback";
}


2、sentinel配合zk使用

使用zk作为配置规则的持久化数据源,需要对sentinel-dashboard进行二次开发,默认情况下sentinel-dashboard将数据保存到内存,以端口的方式【spring.cloud.sentinel.transport.port=8179】与连接端实现降级规则的同步

1、接入方配置zookeeper

import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
 
@Component
public class ZookeeperSentinelConfig {
 
    @Value("${spring.application.name}")
    private String appName;
 
    @Value("${zookeeper.curator.serverUrl}")
    private String zk;
 
 
    @PostConstruct
    public void loadRules() {
 
        //流控规则数据源
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ZookeeperDataSource<>(zk, "/sentinel_rule_config" + "/" + appName + "/" + FlowRule.class.getSimpleName(),
                source -> JSON.parseObject(sourcenew TypeReference<List<FlowRule>>() 
{}));
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
 
        //不同规则之间数据结构可能不一致,如果使用流控规则相同的方式,在获取数据时会获取失败
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowDataSource = new ZookeeperDataSource<>(zk, "/sentinel_rule_config" + "/" + appName + "/" + ParamFlowRule.class.getSimpleName(),
                new Converter<StringList<ParamFlowRule>>() 
{
            @Override
            public List<ParamFlowRule> convert(String source) {
                if(source!=null && !"".equals(source.trim()) && !"[]".equals(source)){
                    List<ParamFlowRule> list = new ArrayList<>();
                    JSONArray array = JSON.parseArray(source);
                    for (int i=0;i<array.size();i++) {
                        JSONObject obj = array.getJSONObject(i);
                        JSONObject rule = obj.getJSONObject("rule");
                        ParamFlowRule r = JSON.toJavaObject(rule, ParamFlowRule.class);
                        list.add(r);
                   }
                    return list;
               }
                return null;
           }
       });
        ParamFlowRuleManager.register2Property(paramFlowDataSource.getProperty());
 
        //熔断规则数据源
        ReadableDataSource<String, List<DegradeRule>> degradeRuleManagerDataSource = new ZookeeperDataSource<>(zk, "/sentinel_rule_config" + "/" + appName + "/" + DegradeRule.class.getSimpleName(),
                source -> JSON.parseObject(sourcenew TypeReference<List<DegradeRule>>() 
{}));
        DegradeRuleManager.register2Property(degradeRuleManagerDataSource.getProperty());
   }
}



3、sentinel-dashboard

#微服务    一些常见的熔断组件

1、流控

#微服务    一些常见的熔断组件

2、熔断

#微服务    一些常见的熔断组件

3、热点

#微服务    一些常见的熔断组件

4、授权

授权需要客户端配和传递参数,标识调用方
#微服务    一些常见的熔断组件

官网文档:https://github.com/alibaba/Sentinel/wiki/Sentinel%E5%B7%A5%E4%BD%9C%E4%B8%BB%E6%B5%81%E7%A8%8B

文档查阅:
https://blog.csdn.net/qq_45932374/article/details/122122786 
https://cloud.tencent.com/developer/article/1456521


写在最后

当然啦,登山的路有很多,这只是其中一条。如果以后我能找到更好的方案,也会再share一下。


我是废物,请多指教!#微服务    一些常见的熔断组件


原文始发于微信公众号(Hephaestuses):#微服务 一些常见的熔断组件

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/45012.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!