Spring Cloud入门系列(十一)- 服务熔断与降级之Hystrix(已停更,建议切换到Sentinel)

前言

服务降级,既可以配置在客户端,也可以配置在服务端,需要根据具体的业务需求,进行灵活配置。

本文模拟的情况,是在服务端80进行配置。

服务降级

服务降级,是通过注解 @HystrixCommand 来实现的。

/**
     * 模拟超时操作
     * fallbackMethod:服务降级后的回调方法(超时异常or运行异常都会触发)
     * commandProperties:
     *      isolation.thread.timeoutInMilliseconds:配置HystrixCommand执行的超时时间,单位为毫秒。
     * 本类的配置:该方法如果执行超过三秒钟没有返回,将不再继续等待,转而执行paymentTimeOutHandler方法)
     * @param id
     * @return String
     */
    @Override
    @HystrixCommand(fallbackMethod = "paymentTimeOutHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentTimeOut(Integer id) {
        int timeNumber = 5000;
        //timeNumber =  10/0;
        try {
            TimeUnit.MILLISECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程名称: " + Thread.currentThread().getName() +
                " ,方法名:paymentTimeOut, id: " + id + " ,超时了~ 耗时:" + timeNumber + "毫秒";
    }
    /**
    * =========================== 服务降级 ===========================
    * 服务降级的处理方法
    * paymentTimeOutHandler与paymentTimeOut的参数列表要对应上,否则会报错
    */
    public String paymentTimeOutHandler(Integer id) {
        return "线程名称: " + Thread.currentThread().getName() +
                "  ,系统繁忙系统报错 ,paymentInfo_TimeOutHandler  ,请稍后再试 ,id: " + id + "\t " + "o(╥﹏╥)o";
    }
  • tips:服务降级方法的参数列表,需要与业务的方法中的参数列表保持一致

可以看到,凡是存在隐患的业务方法,都需要配置服务降级。

如果每个方法,都要配置服务降级,如果由100个方法,1000个方法呢。

因此,引申出一个问题——能不能针对业务,配置一个全局的fallback方法,由他统一管理服务降级?

全局服务降级配置

全局服务降级配置,是通过在类上添加注解 @DefaultProperties 指定

@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OpenFeignOrderController {
	/**
	* 模拟异常
    * HystrixCommand注解没有配置属性,走全局fallback方法
    */
    @GetMapping("/global/fallback")
    @HystrixCommand
    public String feignGlobalFallback(){
        String result = null;
        return result.toString();
    }
	/**
     * 全局fallback 降级方法
     */
    public String payment_Global_FallbackMethod() {
        return "Global异常处理信息,请稍后再试,o(╥﹏╥)o";
    }
}

到了这一步,我们又发现了一个让人难受的地方,虽然每个业务都配置了全局的降级方法。

但是业务逻辑和服务降级,耦合在了一起。

代码非常的不优雅,如何解决?

全局服务降级(解耦)

package com.banana.springcloud.service;

import com.banana.springcloud.service.fallback.OpenFeignOrderFallbackService;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * FeignClient注解中的value属性指定了要调用的{服务}名
 * @author layman
 * @date 2021/1/18
 * fallback为value指定的服务名,提供统一的服务降级处理类OpenFeignOrderFallbackService。
 *      如果访问正常,不会走fallback指定的OpenFeignOrderFallbackService类,而是直接访问。
 *      如果访问异常,需要服务降级,就会走fallback指定的OpenFeignOrderFallbackService。
 *          A:如果是feignSuccess方法出现异常,那么就会去OpenFeignOrderFallbackService.class找对应的feignSuccess方法,处理fallback(方法名需一一对应)
 */
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = OpenFeignOrderFallbackService.class)
public interface OpenFeignOrderService {

    /**
     * 模拟成功访问
     */
    @GetMapping(value = "/payment/hystrix/success/{id}")
    String feignSuccess(@PathVariable("id") Long id);
    /**
     * 模拟openFeign访问超时
     */
    @GetMapping("/payment/hystrix/timeout/{id}")
    String feignTimeout(@PathVariable("id") Long id);
}

服务熔断

服务熔断的定义在这里插入图片描述
服务熔断的架构图:
在这里插入图片描述
代码演示(服务熔断的代码,放在服务端8001)

PaymentServiceImpl

/**
     * =========================== 服务熔断 ===========================
     * 服务的降级->进而熔断->恢复调用链路(会回复调用链路,而不是永久熔断)
     * 本类的配置详解:如果在10秒钟的时间窗口期内,8次请求失败率达到60%,触发熔断,5秒钟(默认)后,进入半开状态,尝试放行请求
     */
    @Override
    @HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback", commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), //是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "8"), //请求次数,默认为20
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), //时间窗口期,默认为10秒
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),//失败率达到多少后触发熔断,默认为50
    })
    public String paymentCircuitBreaker(Integer id) {
        if (id < 0) {
            throw new RuntimeException("******id 不能为负数");
        }
        //不带-的UUID
        String serialNumber = IdUtil.simpleUUID();
        return Thread.currentThread().getName() + "\t" + "调用成功,流水号:" + serialNumber;
    }

@HystrixCommand注解中的参数配置

都在com.netflix.hystrix.HystrixCommandProperties这个类里

有兴趣的同学,可以去围观一下。

测试服务熔断

服务端8001进行自测。

PaymentController

package com.banana.springcloud.controller;

import com.banana.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author layman
 * @date 2021/1/18
 */
@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
/**
     * 测试服务熔断
     * @param id
     * @return
     */
    @GetMapping("/hystrix/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
        String result = paymentService.paymentCircuitBreaker(id);
        log.info("------------result: " + result);
        return result;
    }
}

模拟的逻辑是,如果id是负数,抛出异常,如果是正数,则放行。
在这里插入图片描述

服务熔断的开启和关闭条件

在这里插入图片描述
在这里插入图片描述

服务熔断的调用流程

在这里插入图片描述
在这里插入图片描述

服务限流

详情请参见:Alibaba的sentinel

posted @ 2021-10-09 08:33  layman~  阅读(52)  评论(0编辑  收藏  举报