Netflix Feign – 通过微服务传播状态和exception
我正在使用Netflix Feign将微服务A的一个操作调用到微服务B的其他操作,该操作使用Spring Bootvalidation代码。
如果validation不好,微服务B的操作会抛出exception。 然后我在微服务中处理并返回一个HttpStatus.UNPROCESSABLE_ENTITY
(422),如下所示:
@ExceptionHandler({ ValidateException.class }) @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) @ResponseBody public Object validationException(final HttpServletRequest request, final validateException exception) { log.error(exception.getMessage(), exception); error.setErrorMessage(exception.getMessage()); error.setErrorCode(exception.getCode().toString()); return error; }
因此,当微服务A在接口中调用B作为下一个:
@Headers("Content-Type: " + MediaType.APPLICATION_JSON_UTF8_VALUE) @RequestLine("GET /other") void otherOperation(@Param("other") String other ); @Headers("Content-Type: " + MediaType.APPLICATION_JSON_UTF8_VALUE) @RequestLine("GET /code/validate") Boolean validate(@Param("prefix") String prefix); static PromotionClient connect() { return Feign.builder() .encoder(new GsonEncoder()) .decoder(new GsonDecoder()) .target(PromotionClient.class, Urls.SERVICE_URL.toString()); }
并且validation失败它返回内部错误500并显示下一条消息:
{ "timestamp": "2016-08-05T09:17:49.939+0000", "status": 500, "error": "Internal Server Error", "exception": "feign.FeignException", "message": "status 422 reading Client#validate(String); content:\n{\r\n \"errorCode\" : \"VALIDATION_EXISTS\",\r\n \"errorMessage\" : \"Code already exists.\"\r\n}", "path": "/code/validate" }
但我需要返回与微服务操作B相同的操作。
使用Netflix Feign通过微服务传播状态和例外的最佳方法或技术是什么?
你可以使用假装ErrorDecoder
https://github.com/OpenFeign/feign/wiki/Custom-error-handling
这是一个例子
public class MyErrorDecoder implements ErrorDecoder { private final ErrorDecoder defaultErrorDecoder = new Default(); @Override public Exception decode(String methodKey, Response response) { if (response.status() >= 400 && response.status() <= 499) { return new MyBadRequestException(); } return defaultErrorDecoder.decode(methodKey, response); } }
要使spring获取ErrorDecoder,您必须将它放在ApplicationContext上:
@Bean public MyErrorDecoder myErrorDecoder() { return new MyErrorDecoder(); }
我做的一个小库的无耻插件使用reflection来动态地重新抛出已检查的exception(如果它们在Feign界面上,则取消选中),这是基于响应正文中返回的错误代码。
有关自述文件的更多信息: https : //github.com/coveo/feign-error-decoder
编写自定义exception映射器并进行注册。 您可以自定义回复。
完整的例子在这里
public class GenericExceptionMapper implements ExceptionMapper { @Override public Response toResponse(Throwable ex) { return Response.status(500).entity(YOUR_RETURN_OBJ_HERE).build(); } }
我们做的是如下:
与两个微服务共享包含exception的公共jar。
1.)在微服务中将exception转换为DTO类可以说是ErrorInfo。 其中包含自定义exception的所有属性,其中包含一个String exceptionType,它将包含exception类名。
2.)当它在微服务B上接收时,它将由微服务B中的ErrorDecoder处理,它将尝试从exceptionType创建一个exception对象,如下所示:
@Override public Exception decode(String methodKey, Response response) { ErrorInfo errorInfo = objectMapper.readValue(details, ErrorInfo.class); Class exceptionClass; Exception decodedException; try { exceptionClass = Class.forName(errorInfo.getExceptionType()); decodedException = (Exception) exceptionClass.newInstance(); return decodedException; } catch (ClassNotFoundException e) { return new PlatformExecutionException(details, errorInfo); } return defaultErrorDecoder.decode(methodKey, response); }