2016-08-05 31 views
8

Ich verwende Netflix Feign, um eine Operation eines Microservice A zu einer anderen Operation eines Microservice B aufzurufen, der einen Code mit Spring Boot validiert.Netflix Feign - propagate Status und Ausnahme durch Microservices

Der Betrieb von Microservice B löst eine Ausnahme aus, wenn die Validierung schlecht war. Dann behandeln ich in dem Microservice und gebe ein HttpStatus.UNPROCESSABLE_ENTITY (422) wie weiter:

@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; 
    } 

Also, wenn Micro A in einer Schnittstelle, wie neben B ruft:

@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()); 
} 

und die Validierungen nicht Zurückgegeben ein interner Fehler 500 mit dem nächsten Nachricht:

{ 
    "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" 
} 

Aber ich brauche das gleiche wie der Micro Betrieb B.

zurückzukehren

Wich wären die besten Wege oder Techniken, um Status und Ausnahmen durch Microservices mit Netflix Feign zu propagieren?

Antwort

10

könnten Sie verwenden einen feign ErrorDecoder

https://github.com/OpenFeign/feign/wiki/Custom-error-handling

Hier ist ein Beispiel

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); 
    } 

} 

Für das Frühjahr die ErrorDecoder Sie es auf dem Application setzen müssen abholen:

@Bean 
public MyErrorDecoder myErrorDecoder() { 
    return new MyErrorDecoder(); 
} 
+1

Wie könnte ich, wenn der Fehler kennen kommt von otherOperation() oder validate()? Ich teile das Feign.builder() also hätte ich den gleichen ErrorDecoder und meine Codes und die Nachricht werden nicht die gleichen sein :( – Pau

+1

Ich denke, Sie können den 'methodKey' Parameter der' decode' Methode verwenden. Entsprechend dem API doc sollte er '{ @link feign.Feign # configKey} der Java-Methode, die die Anfrage aufgerufen hat, zB {@code IAM # getUser()} 'Damit kann man einen Hinweis auf den Kontext geben –

+0

Ja, schöner Punkt, es ist was ich will – Pau

0

Schreiben Sie Ihre benutzerdefinierte Exception Mapper und registrieren Sie es. Sie können Antworten anpassen.

Komplettes Beispiel ist here

public class GenericExceptionMapper implements ExceptionMapper<Throwable> { 

    @Override 
    public Response toResponse(Throwable ex) { 
     return Response.status(500).entity(YOUR_RETURN_OBJ_HERE).build(); 
    } 

} 
3

Shameless Stecker für eine kleine Bibliothek, die ich, dass Reflexion tat verwendet dynamisch geprüfte Ausnahmen erneut auslösen (und nicht markiert, wenn sie auf der Feign Schnittstelle sind), basierend auf einem Fehlercode zurückgegeben in der Körper der Antwort.

Mehr Informationen über die readme: https://github.com/coveo/feign-error-decoder

0

Was wir tun, ist wie folgt:

Teilen gemeinsamen Glas, die Ausnahmen mit den beiden Microservices enthält.

1.) In Microservices Eine Ausnahme zu einer DTO-Klasse konvertieren, sagen wir ErrorInfo. Das enthält alle Attribute Ihrer benutzerdefinierten Ausnahme mit einem String exceptionType, der den Namen der Ausnahmeklasse enthält.

2.) Wenn bei Micro B empfangen wird es durch ErrorDecoder in Micro B behandelt werden, und es wird versuchen, eine Ausnahmeobjekt von Exception wie unten zu erstellen:

@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); 
}