If you have the requirement of validating the response of a rest interface, a good opportunity in Java is to use the bean validation api and its annotations. On top, if you use OpenFeign wrapper to define the client in your application, you can now assign those validation annotations directly to your interface.
If you have already seen in my previous blogpost about Feign Outbound metrics, I am trying to extend feign with some decorator classes to add missing functionality. metrics-feign is a library, which can be used to report outbound-metrics out of any invocation to the feign wrapper.
Now I created another library, which makes it possible to validate the response using bean validation api (JSR349). Its called feign-validation and you can find it at github or maven central.
The only thing you have to do after setting it up is to add annotations to the interface class you are using. Here is an example:
interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
@Size(min = 1, max = 3)
@Valid
List contributors(@Param("owner") String owner, @Param("repo") String repo);
}
static class Contributor {
@NotNull
@Size(min = 10)
String login;
int contributions;
}
public static void main(String... args) {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
GitHub github = ExtendedFeign.builder(validator)//
.decoder(new GsonDecoder()).target(GitHub.class, "https://api.github.com");
// Fetch and print a list of the contributors to this library.
try {
List contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
} catch (ConstraintViolationException ex) {
ex.getConstraintViolations().forEach(System.out::println);
}
}
If you would run this class, you can see, that the response is being validated against the given annotations and all violations a printed to stdout.
First, the @Size annotation leads to a constraint violation, because the size of the list is checked, whether it has at least one or maximim three items.
Because of @Valid, each item in the list of contributors is validated
@Size in Contributor class is set to verify, that the login name must have at least 10 characters. Because not all contributors have such a long name, a few more violations are reported
as seen, you can use any annotation from the api to fulfil the need
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The metrics can easily been reported to graphite database and visualized via Kibana.
WYIIWYG – What you instrument, is what you get!
On the other hand, a microservice often contains client libraries to access other services via http. Feign is client library, which provides a wrapper and simplifies the api to communicate to the target services.
In contrast to the inbound metrics from the example above, it is also desirable to monitor the outbound metrics of each of the targeted operations.
Looking at the third-party libraries of http://metrics.dropwizard.io/3.2.3/manual/third-party.html there is already something to retrieve metrics on http level. So in case you are using okhttp as http client implementation you can use https://github.com/raskasa/metrics-okhttp and you will receive information about request durations and connection pools. Same holds good for Apache httpclient instrumentation.
okhttp example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
As you can see, the provided metrics only provid information on http level, not really showing differences between different service endpoints. The only differentation is available on the httpclient metrics, which shows metrics based on host and http methods.
Closing the gap
What was missing in my eyes was a way to instrument metrics on the interface level, which is provided from the Feign builder. In my example below I am calling the github API on two different resource endpoints, contributors and repositorySearch. With the instrumentation on http, one is not able to see and monitor those one by one.
Therefore I created a library, which makes it possible to instrument metrics on method or interface level by using annotations like you do it in jersey resource classes.
Using this instrumentation you are able to retrieve metrics based on the interface and methods the client is calling. So for example when you start reporting via JMX, you are able to see the metrics in jconsole.
Usage of the library
To instrument the feign interfaces you basically have to do three things:
add the maven dependency to the pom.xml of your project.
add FeignOutboundMetricsDecorator as invocationHandlerFactory in Feign.builder
add the metric annotations @Timed, @Metered and @ExceptionMetered to the interface you are using with feign.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Feign is a library, which makes it easier to implement a http client. Recently more and more people start writing http clients, because they are creating microservices which communicate with http protocol. So there are all sorts of libraries supporting this task like Jersey, Resteasy and others – and there is Feign.
Today I do not want to explain the basic functionality, this is all done on the Readme page itself. Today I want to get into the details of a feature, which becomes more and more important, because in modern distributed systems, you want to have resilient behaviour, which means that you want to design your service in the way, that it can handle unexpected situations without noticing on user’s site. For example an API you are calling is not reachable at the moment, the request times out or the requested resource is not yet available. To solve this issue, you need to apply a retry pattern, so that you increase the chance that the service request is successfull after the first, the second or the nth attempt.
What most developers don’t know, Feign has a default retryer built-in.
Now I show a few code examples, what you can expect from this feature. What I am showing are junit tests with a client mock, so that we are able to stub certain errors and verify, how many retries have been made.
Case 1) Success
no retry needed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In this case, we can see the Default Retryer working, which ends up doing 5 attempts, but finally the client invocation throws an exception.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Taking the same error scenario from case 2, this example shows how to configure the retryer to stop trying after the 3rd attempt.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
For some (restful) services, http status code 409 (conflict) is used to express a wrong state of the target resource, that might change after resubmitting the request. We simulate, that the first retry will lead to a successfull response.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If no error decoder is configured, no retry is executed by Feign.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In contrast to the cases 4 and 4a, any response having a Retry-After header, which is a standard header defined in http protocol, the default Feign behavior is to honor this and trigger a retry at the date given.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters