基础知识
开发环境
名称 | 版本 |
---|---|
操作系统 | 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。
*/
@Slf4j
public class KeepErrMsgConfiguration {
@Bean
public ErrorDecoder errorDecoder() {
return new UserErrorDecoder();
}
/**
* 自定义错误解码器
*/
public class UserErrorDecoder implements ErrorDecoder {
@Override
public 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会直接抛出,不进入熔断方法。
*/
@Slf4j
public class NotBreakerConfiguration {
@Bean
public ErrorDecoder errorDecoder() {
return new UserErrorDecoder();
}
/**
* 自定义错误解码器
*/
public class UserErrorDecoder implements ErrorDecoder {
@Override
public 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 方法
@Override
public 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/Get
Body 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/Get
Body 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"
}