springcloud通过hystrix做服务降级和服务熔断


前言

为什么要引入hystrix,首先我们看下面这张图。下图是微服务体系调用架构,
假如服务2,3,4要调用服务1,这时候服务提供者1宕机了,那么服务调用者
肯定会出现报错提醒。而把报错信息暴露给用户是十分危险的同时也是很不友好
的,有没有什么方法可以在服务1挂掉的同时让服务234有一个兜底的方法不把
具体错误返回给页面而是出现一个500或者404的界面呢?接下来hystrix就派
上了大用场了。

一、hystrix是什么?

hystrix是Netlifx开源的一款容错框架,防雪崩利器,具备服务降级,服务熔断,依赖隔离,监控(Hystrix Dashboard)等功能。

hystrix是一个库,通过延迟容忍和容错逻辑,控制分布式服务之间的交互。它通过隔离服务间的访问点、防止级联失败和提供回退选项,保证系统的整体弹性。

服务降级:服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

服务熔断:服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。

二、使用步骤

1.通过springcloud做服务降级处理

(1)创建服务提供者cloud-provider-hystrix-payment8001

在这里插入图片描述

(2)导入pom依赖

<dependencies>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

(3)配置yml文件

server:
  port: 8001   #微服务端口号

spring:
  application:
    name: cloud-provider-hystrix-payment  #微服务名称

eureka:
  client:
    register-with-eureka: true    #注册进eureka
    fetch-registry: true  #Server获取注册信息
    service-url:
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
      defaultZone: http://eureka7001.com:7001/eureka

(4)编写主启动类并且加上注解

@SpringBootApplication
@EnableEurekaClient  //注册进eureka客户端
@EnableCircuitBreaker //hystrix的断路器,加上之后才会触发fallback机制
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class, args);
    }
}


(5)加上service和controller

@Service
public class PaymentService {
    public String Payment_ok(Integer id) {
        return "线程:"+Thread.currentThread().getName()+"Payment_ok,id"+id+"(hahaha)";
    }

    @HystrixCommand(fallbackMethod = "Payment_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
    })
    public String Payment_TimeOut(Integer id){
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程:"+Thread.currentThread().getName()+"Payment_ok,id"+id+"(wuwuwu)";
    }

    public String Payment_TimeOutHandler(Integer id){
        return "线程:"+Thread.currentThread().getName()+"超时了!";
    }

}

service中实现了两个接口,一个是Payment_ok,这个接口实现的是正常访问的方法,如果访问成功就会打印当前线程名还有尾部的hahaha。
另一个是Payment_TimeOut,这个接口实现了超时访问的方法,设置线程休眠时间5秒,该方法上面还有一个hystrix服务降级的注解。
该注解的fallbackMethod是降级的兜底方法,还有3000毫秒的有效时间。意思是在3秒钟内方法运行正常则执行方法体的内容,加入超过了3秒则执行兜底的方法Payment_TimeOutHandler。

@HystrixCommand(fallbackMethod = "Payment_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
    })

这是controller层

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    @GetMapping("/payment/ok/{id}")
    public String Payment_Ok(@PathVariable("id") Integer id){
        String result = paymentService.Payment_ok(id);
        return result;
    }
    @GetMapping("/payment/timeout/{id}")
    public String Payment_TimeOut(@PathVariable("id") Integer id){
        String result = paymentService.Payment_TimeOut(id);
        return result;
    }

}

当然,如果每一个方法都需要兜底的话那么用这种方式岂不是要在每一个方法上面都加上注解吗?,这样做显然会让代码冗余。其实还有另一种服务降级的方式。

方式二:
创建客户端侧微服务。,该服务作为客户端通过feign调用8001的服务。不知道如果创建的可以翻我之前写的博客[springcloud通过Feign做服务调用实例]。(https://blog.csdn.net/haokelaicds/article/details/125197476?spm=1001.2014.3001.5502)
在这里插入图片描述
yml为:

server:
  port: 81    #服务调用者的端口号

eureka:
  client:
    register-with-eureka: false   #是否注册进eureka
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

feign:
  hystrix:
    enabled: true    #是否开启feign和hystrix

在主启动类上面添加注解@EnableHystrix。
controller层上通过在类上添加如下注解指定全局的fallback兜底方法。

@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")

当然该方法是需要在类里面实现的。

// 下面是全局fallback方法
    public String payment_Global_FallbackMethod()
    {
        return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
    }

在需要设置全局兜底方法上添加@HystrixCommand注解说明该方法需要做全局兜底处理。
还可以在service层做文章。由于controller最后调用的是接口,所有我们可以把fallback方法设置在service中。只要在接口上添加下面注解。
这里的value是调用的微服务名,fallback是兜底的方法类名称,该类实现了接口

@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT" ,fallback = PaymentFallbackService.class)

实现的方法类。
在这里插入图片描述

2.通过springcloud做服务熔断处理

在第一次创建的服务端8001端的service层添加服务熔断方法。

 //=====服务熔断
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",  //服务熔断兜底方法
    commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 时间窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失败率达到多少后跳闸
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id)
    {
        if(id < 0)
        {
            throw new RuntimeException("******id 不能负数");
        }
        String serialNumber = IdUtil.simpleUUID();

        return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)
    {
        return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~   id: " +id;
    }

具体的参数都有进行标注。
最后在controller层添加方法

/====服务熔断
    @GetMapping("/payment/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id)
    {
        String result = paymentService.paymentCircuitBreaker(id);
        log.info("****result: "+result);
        return result;
    }

这样,当/payment/circuit/{id}这个url在达到了熔断的条件后就会触发服务熔断。

总结

一般我们通常把服务降级设置在客户端而不是服务端。服务熔断在服务端而不是客户端。为什么要这么设置呢?
因为服务熔断主要是为了服务的整体负荷考虑,熔断的最终目的是为了防止服务链路崩溃。而降级之所以不放在服务端原因很简单,服务端的服务对象不可能是一个微服务,肯定是多个服务都在调用着,加入一个服务错误就让服务端出现了错误那其他的客户怎么办呢?所以要么就是服务端和客户端都设置服务降级进行保护,要么就把降级放在客户端。
改文章涉及的知识点较多。可以参考
springcloud将微服务注册进eurekaserver
springcloud通过Feign做服务调用实例

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值