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
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号