基础知识
开发环境
| 名称 | 版本 |
|---|---|
| 操作系统 | 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
package com.test.invoice.service.hystrix;import com.alibaba.fastjson.JSON;import com.test.invoice.excepiton.InternalApiException;import com.test.invoice.util.ExceptionUtil;import feign.Response;import feign.Util;import feign.codec.ErrorDecoder;import lombok.extern.slf4j.Slf4j;import org.springframework.context.annotation.Bean;import org.springframework.http.HttpStatus;import java.io.IOException;/*** @Author:* @Description: 保留 feign 服务异常信息* @Date: Created in 1:29 2019/7/5* 保留原始异常信息* 当调用服务时,如果服务返回的状态码不是200,就会进入到Feign的ErrorDecoder中,* 因此如果我们要解析异常信息,就要重写ErrorDecoder:* Spring Cloud Feign 熔断机制填坑* http://www.ciphermagic.cn/spring-cloud-feign-hystrix.html* 原理是根据response.body()反序列化为自定义的Result类,提取出里面的message信息,然后抛出RuntimeException,这样当进入到熔断方法中时,获取到的异常就是我们处理过的RuntimeException。*/@Slf4jpublic class KeepErrMsgConfiguration {@Beanpublic ErrorDecoder errorDecoder() {return new UserErrorDecoder();}/*** 自定义错误解码器*/public class UserErrorDecoder implements ErrorDecoder {@Overridepublic Exception decode(String methodKey, Response response) {if (response.status() != HttpStatus.OK.value()) {//if (response.status() == HttpStatus.SERVICE_UNAVAILABLE.value()) {try {// 获取原始的返回内容String json = Util.toString(response.body().asReader());InternalApiException internalApiException = JSON.parseObject(json, InternalApiException.class);log.info(" 【===Server Exception===】,{}", ExceptionUtil.getErrorMessage(internalApiException));RuntimeException exception = new RuntimeException(internalApiException.getMessage());return exception;} catch (IOException e) {log.error(e.getMessage(), e);return new RuntimeException("unknown error");}//}}return new RuntimeException("unknown error");}}}
添加类-NotBreakerConfiguration
package com.test.invoice.service.hystrix;import com.alibaba.fastjson.JSON;import com.netflix.hystrix.exception.HystrixBadRequestException;import com.test.invoice.excepiton.InternalApiException;import com.test.invoice.util.ExceptionUtil;import feign.Response;import feign.Util;import feign.codec.ErrorDecoder;import lombok.extern.slf4j.Slf4j;import org.springframework.context.annotation.Bean;import org.springframework.http.HttpStatus;import java.io.IOException;/*** @Author:* @Description: feign 服务异常不进入熔断* @Date: Created in 1:29 2018/6/2* 不进入熔断,直接抛出异常* 有时我们并不希望方法进入熔断逻辑,只是把异常原样往外抛。* 这种情况我们只需要捉住两个点:不进入熔断、原样。* 原样就是获取原始的异常,而不进入熔断,需要把异常封装成HystrixBadRequestException,* 对于HystrixBadRequestException,Feign会直接抛出,不进入熔断方法。*/@Slf4jpublic class NotBreakerConfiguration {@Beanpublic ErrorDecoder errorDecoder() {return new UserErrorDecoder();}/*** 自定义错误解码器*/public class UserErrorDecoder implements ErrorDecoder {@Overridepublic Exception decode(String methodKey, Response response) {Exception exception = null;if (response.status() != HttpStatus.OK.value()) {//if (response.status() == HttpStatus.SERVICE_UNAVAILABLE.value()) {try {// 获取原始的返回内容(来自GlobalExceptionHandler类中的返回信息)String json = Util.toString(response.body().asReader());InternalApiException internalApiException = JSON.parseObject(json, InternalApiException.class);log.info(" 【===Server Exception===】,{}", ExceptionUtil.getErrorMessage(internalApiException));exception = new HystrixBadRequestException(internalApiException.getMessage());} catch (IOException e) {log.error(e.getMessage(), e);exception = new HystrixBadRequestException(e.getMessage());}//}}return exception;}}}
修改-TRbtTestConsumer-@FeignClient
package com.test.invoice.service.consumer;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.test.invoice.service.hystrix.NotBreakerConfiguration;import com.test.invoice.vo.ResponseVO;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.*;import com.test.invoice.data.TRbtTestData;import com.test.invoice.data.TRbtTestDataParam;import com.test.invoice.service.hystrix.TRbtTestHystrix;/*** Feign-api 接口定义——TRbtTestConsumer** @author:* @version:* @date: 2019-08-08 14:45*///@FeignClient(value = "service-producer")//modify by v_hwhao 20190828 加入熔断处理//@FeignClient(value = "service-producer",fallback = TRbtTestHystrix.class)//modify by v_hwhao 20190927 - 不进入熔断,直接抛出异常@FeignClient(value = "service-producer",configuration = NotBreakerConfiguration.class)public interface TRbtTestConsumer {@PostMapping("/Test/Add")ResponseVO<String> Add(@RequestBody TRbtTestData data);@PostMapping("Test/Update")ResponseVO<Boolean> Update(@RequestBody TRbtTestData data);@RequestMapping(value="Test/Del",method=RequestMethod.GET)ResponseVO<Boolean> Del(@RequestParam(value="name",required = true) String name,@RequestParam(value="version",required = true) String version);@PostMapping("/Test/Get")ResponseVO<TRbtTestData> Get(@RequestBody TRbtTestData data);@PostMapping("/Test/Query")ResponseVO<Page<TRbtTestData>> Query(@RequestBody TRbtTestDataParam param);}
测试
修改test-invoice-service-TRbtTestServiceImpl-Get 方法
@Overridepublic ResponseVO<TRbtTestData> Get(@RequestBody TRbtTestData data){int i = 1 / 0;List<TRbtTestEntity> list = baseMapper.selectList(Wrappers.<TRbtTestEntity>lambdaQuery().eq(TRbtTestEntity::getName, data.getName()));if(!list.isEmpty()){TRbtTestEntity entity = list.get(0);data.setName(entity.getName());data.setVersion(entity.getVersion());return new ResponseVO<>(ResponseCode.OK, data);}else{return new ResponseVO<>(ResponseCode.PARAM_INVALID, data);}}
1.使用熔断处理的情况
(1)
//modify by v_hwhao 20190828 加入熔断处理@FeignClient(value = "service-producer",fallback = TRbtTestHystrix.class)public interface TRbtTestConsumer
(2)使用 Postman 访问 Get 接口
地址:http://localhost:8080/Inv/Api/Test/GetBody Raw 参数:{"id":"","name":"测试数据-菲克-1","version":""}
返回结果
{"code": 5001,"msg": "熔断处理-系统异常","timestamp": 1570608703141}
2.Feign 服务异常不进入熔断 的情况
(1)
//modify by v_hwhao 20190927 - 不进入熔断,直接抛出异常@FeignClient(value = "service-producer",configuration = NotBreakerConfiguration.class)public interface TRbtTestConsumer
(2)使用 Postman 访问 Get 接口
地址:http://localhost:8080/Inv/Api/Test/GetBody Raw 参数:{"id":"","name":"测试数据-菲克-1","version":""}
返回结果
{"timestamp": "2019-10-09T08:08:09.426+0000","status": 500,"error": "Internal Server Error","message": "Request processing failed; nested exception is java.lang.ArithmeticException: / by zero","path": "/Inv/Api/Test/Get"}