3.Feign 服务异常不进入熔断

2019年10月09日 17:17 · 阅读(2720) ·

基础知识

2.Spring Cloud-熔断器 Hystrix

开发环境

名称 版本
操作系统 Windows 10 X64
JDK JDK1.8(jdk-8u151-windows-x64)
IntelliJ IDEA IntelliJ IDEA 2018.3
Maven Maven 3.6.0

代码说明

本文代码基于 2.引用 lombok 使用 log 基础上进行修改

test-invoice-contract

添加下面的文件

添加类-KeepErrMsgConfiguration

  1. package com.test.invoice.service.hystrix;
  2. import com.alibaba.fastjson.JSON;
  3. import com.test.invoice.excepiton.InternalApiException;
  4. import com.test.invoice.util.ExceptionUtil;
  5. import feign.Response;
  6. import feign.Util;
  7. import feign.codec.ErrorDecoder;
  8. import lombok.extern.slf4j.Slf4j;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.http.HttpStatus;
  11. import java.io.IOException;
  12. /**
  13. * @Author:
  14. * @Description: 保留 feign 服务异常信息
  15. * @Date: Created in 1:29 2019/7/5
  16. * 保留原始异常信息
  17. * 当调用服务时,如果服务返回的状态码不是200,就会进入到Feign的ErrorDecoder中,
  18. * 因此如果我们要解析异常信息,就要重写ErrorDecoder:
  19. * Spring Cloud Feign 熔断机制填坑
  20. * http://www.ciphermagic.cn/spring-cloud-feign-hystrix.html
  21. * 原理是根据response.body()反序列化为自定义的Result类,提取出里面的message信息,然后抛出RuntimeException,这样当进入到熔断方法中时,获取到的异常就是我们处理过的RuntimeException。
  22. */
  23. @Slf4j
  24. public class KeepErrMsgConfiguration {
  25. @Bean
  26. public ErrorDecoder errorDecoder() {
  27. return new UserErrorDecoder();
  28. }
  29. /**
  30. * 自定义错误解码器
  31. */
  32. public class UserErrorDecoder implements ErrorDecoder {
  33. @Override
  34. public Exception decode(String methodKey, Response response) {
  35. if (response.status() != HttpStatus.OK.value()) {
  36. //if (response.status() == HttpStatus.SERVICE_UNAVAILABLE.value()) {
  37. try {
  38. // 获取原始的返回内容
  39. String json = Util.toString(response.body().asReader());
  40. InternalApiException internalApiException = JSON.parseObject(json, InternalApiException.class);
  41. log.info(" 【===Server Exception===】,{}", ExceptionUtil.getErrorMessage(internalApiException));
  42. RuntimeException exception = new RuntimeException(internalApiException.getMessage());
  43. return exception;
  44. } catch (IOException e) {
  45. log.error(e.getMessage(), e);
  46. return new RuntimeException("unknown error");
  47. }
  48. //}
  49. }
  50. return new RuntimeException("unknown error");
  51. }
  52. }
  53. }

添加类-NotBreakerConfiguration

  1. package com.test.invoice.service.hystrix;
  2. import com.alibaba.fastjson.JSON;
  3. import com.netflix.hystrix.exception.HystrixBadRequestException;
  4. import com.test.invoice.excepiton.InternalApiException;
  5. import com.test.invoice.util.ExceptionUtil;
  6. import feign.Response;
  7. import feign.Util;
  8. import feign.codec.ErrorDecoder;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.springframework.context.annotation.Bean;
  11. import org.springframework.http.HttpStatus;
  12. import java.io.IOException;
  13. /**
  14. * @Author:
  15. * @Description: feign 服务异常不进入熔断
  16. * @Date: Created in 1:29 2018/6/2
  17. * 不进入熔断,直接抛出异常
  18. * 有时我们并不希望方法进入熔断逻辑,只是把异常原样往外抛。
  19. * 这种情况我们只需要捉住两个点:不进入熔断、原样。
  20. * 原样就是获取原始的异常,而不进入熔断,需要把异常封装成HystrixBadRequestException,
  21. * 对于HystrixBadRequestException,Feign会直接抛出,不进入熔断方法。
  22. */
  23. @Slf4j
  24. public class NotBreakerConfiguration {
  25. @Bean
  26. public ErrorDecoder errorDecoder() {
  27. return new UserErrorDecoder();
  28. }
  29. /**
  30. * 自定义错误解码器
  31. */
  32. public class UserErrorDecoder implements ErrorDecoder {
  33. @Override
  34. public Exception decode(String methodKey, Response response) {
  35. Exception exception = null;
  36. if (response.status() != HttpStatus.OK.value()) {
  37. //if (response.status() == HttpStatus.SERVICE_UNAVAILABLE.value()) {
  38. try {
  39. // 获取原始的返回内容(来自GlobalExceptionHandler类中的返回信息)
  40. String json = Util.toString(response.body().asReader());
  41. InternalApiException internalApiException = JSON.parseObject(json, InternalApiException.class);
  42. log.info(" 【===Server Exception===】,{}", ExceptionUtil.getErrorMessage(internalApiException));
  43. exception = new HystrixBadRequestException(internalApiException.getMessage());
  44. } catch (IOException e) {
  45. log.error(e.getMessage(), e);
  46. exception = new HystrixBadRequestException(e.getMessage());
  47. }
  48. //}
  49. }
  50. return exception;
  51. }
  52. }
  53. }

修改-TRbtTestConsumer-@FeignClient

  1. package com.test.invoice.service.consumer;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.test.invoice.service.hystrix.NotBreakerConfiguration;
  4. import com.test.invoice.vo.ResponseVO;
  5. import org.springframework.cloud.openfeign.FeignClient;
  6. import org.springframework.web.bind.annotation.*;
  7. import com.test.invoice.data.TRbtTestData;
  8. import com.test.invoice.data.TRbtTestDataParam;
  9. import com.test.invoice.service.hystrix.TRbtTestHystrix;
  10. /**
  11. * Feign-api 接口定义——TRbtTestConsumer
  12. *
  13. * @author:
  14. * @version:
  15. * @date: 2019-08-08 14:45
  16. */
  17. //@FeignClient(value = "service-producer")
  18. //modify by v_hwhao 20190828 加入熔断处理
  19. //@FeignClient(value = "service-producer",fallback = TRbtTestHystrix.class)
  20. //modify by v_hwhao 20190927 - 不进入熔断,直接抛出异常
  21. @FeignClient(value = "service-producer",configuration = NotBreakerConfiguration.class)
  22. public interface TRbtTestConsumer {
  23. @PostMapping("/Test/Add")
  24. ResponseVO<String> Add(@RequestBody TRbtTestData data);
  25. @PostMapping("Test/Update")
  26. ResponseVO<Boolean> Update(@RequestBody TRbtTestData data);
  27. @RequestMapping(value="Test/Del",method=RequestMethod.GET)
  28. ResponseVO<Boolean> Del(@RequestParam(value="name",required = true) String name,@RequestParam(value="version",required = true) String version);
  29. @PostMapping("/Test/Get")
  30. ResponseVO<TRbtTestData> Get(@RequestBody TRbtTestData data);
  31. @PostMapping("/Test/Query")
  32. ResponseVO<Page<TRbtTestData>> Query(@RequestBody TRbtTestDataParam param);
  33. }

测试

修改test-invoice-service-TRbtTestServiceImpl-Get 方法

  1. @Override
  2. public ResponseVO<TRbtTestData> Get(@RequestBody TRbtTestData data){
  3. int i = 1 / 0;
  4. List<TRbtTestEntity> list = baseMapper.selectList(Wrappers.<TRbtTestEntity>lambdaQuery().eq(TRbtTestEntity::getName, data.getName()));
  5. if(!list.isEmpty()){
  6. TRbtTestEntity entity = list.get(0);
  7. data.setName(entity.getName());
  8. data.setVersion(entity.getVersion());
  9. return new ResponseVO<>(ResponseCode.OK, data);
  10. }else{
  11. return new ResponseVO<>(ResponseCode.PARAM_INVALID, data);
  12. }
  13. }

1.使用熔断处理的情况

(1)

  1. //modify by v_hwhao 20190828 加入熔断处理
  2. @FeignClient(value = "service-producer",fallback = TRbtTestHystrix.class)
  3. public interface TRbtTestConsumer

(2)使用 Postman 访问 Get 接口

  1. 地址:http://localhost:8080/Inv/Api/Test/Get
  2. Body Raw 参数:{"id":"","name":"测试数据-菲克-1","version":""}

返回结果

  1. {
  2. "code": 5001,
  3. "msg": "熔断处理-系统异常",
  4. "timestamp": 1570608703141
  5. }

2.Feign 服务异常不进入熔断 的情况

(1)

  1. //modify by v_hwhao 20190927 - 不进入熔断,直接抛出异常
  2. @FeignClient(value = "service-producer",configuration = NotBreakerConfiguration.class)
  3. public interface TRbtTestConsumer

(2)使用 Postman 访问 Get 接口

  1. 地址:http://localhost:8080/Inv/Api/Test/Get
  2. Body Raw 参数:{"id":"","name":"测试数据-菲克-1","version":""}

返回结果

  1. {
  2. "timestamp": "2019-10-09T08:08:09.426+0000",
  3. "status": 500,
  4. "error": "Internal Server Error",
  5. "message": "Request processing failed; nested exception is java.lang.ArithmeticException: / by zero",
  6. "path": "/Inv/Api/Test/Get"
  7. }