SpringCloud 详解(九)

发布于 2022-04-19  127 次阅读


Hystrix(续一)

服务降级(服务生产者)

服务生产者超时或宕机了,服务消费者不能一直卡死等待,服务生产者就必须要有服务降级。

Controller 层

package ml.guest997.Controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class PaymentController {
    @GetMapping("/payment/ok/{id}")
    public String paymentOK(@PathVariable("id") Integer id) {
        String s = "当前线程:" + Thread.currentThread().getName() + " paymentOK id:" + id;
        log.info("result:" + s);
        return s;
    }

    @GetMapping("/payment/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")     //超时2秒将调用 fallbackMethod
    })
    public String paymentTimeOut(@PathVariable("id") Integer id) {
        String s = null;
        try {
            TimeUnit.MILLISECONDS.sleep(3000);
            s = "当前线程:" + Thread.currentThread().getName() + " paymentTimeOut id:" + id + " 耗时3秒";
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("result:" + s);
        return s;
    }

    public String paymentTimeOutHandler(Integer id) {       //fallback 方法一定要与原方法参数一致
        return "当前线程:" + Thread.currentThread().getName() + " 系统繁忙或者运行报错,请稍后再试。";
    }
}

主启动类

package ml.guest997;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

@SpringBootApplication
@EnableEurekaClient
@EnableHystrix      //启动 Hystrix
public class Payment5 {
    public static void main(String[] args) {
        SpringApplication.run(Payment5.class, args);
    }
}

超时测试

分别启动 Eureka-Server、Payment5 和 Order3 模块后,浏览器访问:127.0.0.1/consumer/payment/timeout/997,会发现调用的是 fallback 方法并且显示的线程是 HystrixTimer。

程序错误测试

修改 Controller 层中的 paymentTimeOut 方法,如下图。

@GetMapping("/payment/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutHandler", commandProperties = {
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")     //超时2秒将调用 fallbackMethod 中的方法
})
public String paymentTimeOut(@PathVariable("id") Integer id) {
    String s = null;
//        try {
//            TimeUnit.MILLISECONDS.sleep(3000);
//            s = "当前线程:" + Thread.currentThread().getName() + " paymentTimeOut id:" + id + " 耗时3秒";
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
    s = String.valueOf(1/0);        //程序错误
    log.info("result:" + s);
    return s;
}

分别启动 Eureka-Server、Payment5 和 Order3 模块后,浏览器访问:127.0.0.1/consumer/payment/timeout/997,会发现依旧调用的是 fallback 方法并且显示的线程是 hystrix。

服务降级(服务消费者)

服务生产者没问题,服务消费者自己出了故障或有自我要求(自己的等待时间小于服务生产者响应回来的时间),服务消费者就可以自己进行服务降级。

Controller 层

package ml.guest997.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import ml.guest997.service.PaymentService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderController {
    @Resource
    private PaymentService paymentService;

    @GetMapping("/consumer/payment/ok/{id}")
    String paymentOK(@PathVariable("id") Integer id) {
        return paymentService.paymentOK(id);
    }

    @GetMapping("/consumer/payment/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
    })
    String paymentTimeOut(@PathVariable("id") Integer id) {
        return paymentService.paymentTimeOut(id);
    }

    public String paymentTimeOutHandler(Integer id){
        return "支付系统繁忙请稍后再试,或者自己运行出错请检查自己。";
    }
}

主启动类

package ml.guest997;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients
@EnableHystrix      //启动 Hystrix
public class Order3 {
    public static void main(String[] args) {
        SpringApplication.run(Order3.class, args);
    }
}

超时测试

分别启动 Eureka-Server、Payment5 和 Order3 模块后,浏览器访问:127.0.0.1/consumer/payment/timeout/997,会发现调用的是 fallback 方法。

程序错误测试

修改 Controller 层中的 paymentTimeOut 方法,如下图。

@GetMapping("/consumer/payment/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutHandler", commandProperties = {
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentTimeOut(@PathVariable("id") Integer id) {
//        return paymentService.paymentTimeOut(id);
    return String.valueOf(1/0);
}

分别启动 Eureka-Server、Payment5 和 Order3 模块后,浏览器访问:127.0.0.1/consumer/payment/timeout/997,会发现依旧调用的是 fallback 方法。

全局服务降级

每个业务方法对应一个降级方法,代码膨胀。我们希望的是特殊业务才有特殊的降级方法,平常的普通业务只需要个通用的降级方法即可。这样就避免了代码膨胀,合理减少了代码量。

Controller 层

package ml.guest997.controller;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import ml.guest997.service.PaymentService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "GlobalFallbackMethod")    //注解表示全局的 fallback 方法
public class OrderController {
    @Resource
    private PaymentService paymentService;

    @GetMapping("/consumer/payment/ok/{id}")
    public String paymentOK(@PathVariable("id") Integer id) {
        return paymentService.paymentOK(id);
    }

    @GetMapping("/consumer/payment/timeout/{id}")
//    @HystrixCommand(fallbackMethod = "paymentTimeOutHandler", commandProperties = {
//            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
//    })
    @HystrixCommand         //使用全局的 fallback 方法
    public String paymentTimeOut(@PathVariable("id") Integer id) {
//        return paymentService.paymentTimeOut(id);
        return String.valueOf(10 / 0);
    }

    public String GlobalFallbackMethod() {
        return "异常处理信息,请稍后再试。";
    }

    public String paymentTimeOutHandler(Integer id) {
        return "支付系统繁忙请稍后再试,或者自己运行出错请检查自己。";
    }
}

测试

分别启动 Eureka-Server、Payment5 和 Order3 模块后,浏览器访问:127.0.0.1/consumer/payment/timeout/997,会发现调用的是全局 fallback 方法。

通配服务降级(FeignFallback)

可以从上面的代码看到,Controller 层的代码有异常处理的代码,耦合度高。我们可以通过为 Feign 客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。

添加配置

#启动 feign 断路器
feign:
  circuitbreaker:
    enabled: true

服务降级实现类

package ml.guest997.service;

import org.springframework.stereotype.Component;

@Component
public class PaymentFallbackService implements PaymentService {
    @Override
    public String paymentOK(Integer id) {
        return "服务处理异常,请稍后再试。";
    }

    @Override
    public String paymentTimeOut(Integer id) {
        return "服务处理异常,请稍后再试。";
    }
}

修改 Service 层

package ml.guest997.service;

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;

@Component
@FeignClient(value = "PAYMENT", fallback = PaymentFallbackService.class)        //使用该接口的实现类统一进行服务降级处理
public interface PaymentService {
    @GetMapping("/payment/ok/{id}")
    String paymentOK(@PathVariable("id") Integer id);

    @GetMapping("/payment/timeout/{id}")
    String paymentTimeOut(@PathVariable("id") Integer id);
}

测试

分别启动 Eureka-Server、Payment5 和 Order3 模块后,浏览器访问:127.0.0.1/consumer/payment/ok/997,会发现调用的是服务降级处理的实现类方法。