Hystrix(续一)
服务降级(服务生产者)
服务生产者超时或宕机了,服务消费者不能一直卡死等待,服务生产者就必须要有服务降级。
Controller 层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| 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") }) 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) { return "当前线程:" + Thread.currentThread().getName() + " 系统繁忙或者运行报错,请稍后再试。"; } }
|
主启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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 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 方法,如下图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @GetMapping("/payment/timeout/{id}") @HystrixCommand(fallbackMethod = "paymentTimeOutHandler", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") }) public String paymentTimeOut(@PathVariable("id") Integer id) { String s = null;
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 层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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 "支付系统繁忙请稍后再试,或者自己运行出错请检查自己。"; } }
|
主启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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 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 方法,如下图。
1 2 3 4 5 6 7 8
| @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 String.valueOf(1/0); }
|
分别启动 Eureka-Server、Payment5 和 Order3 模块后,浏览器访问:127.0.0.1/consumer/payment/timeout/997,会发现依旧调用的是 fallback 方法。
全局服务降级
每个业务方法对应一个降级方法,代码膨胀。我们希望的是特殊业务才有特殊的降级方法,平常的普通业务只需要个通用的降级方法即可。这样就避免了代码膨胀,合理减少了代码量。
Controller 层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| 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") 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 public String paymentTimeOut(@PathVariable("id") Integer 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 客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。
添加配置
1 2 3 4
| feign: circuitbreaker: enabled: true
|
服务降级实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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 层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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,会发现调用的是服务降级处理的实现类方法。