Abstraction over RestTemplate: bound with service discovery
You want to send requests to your collaborators. That's good! But do you want to take care of service discovery and request building
manually from scratch? Of course not! That's why we provide a ServiceRestClient
that will take care of everything for you and
gives you a fluent interface to achieve that.
The entry point to the abstraction is ServiceRestClient. It takes two parameters to its constructor
- RestOperations implementation (for example RestTemplate)
- ServiceResolver which is responsible for find an address entry in Zookeeper for given collaborator. Please check 4finance's micro-deps project for more information
Once your ServiceRestClient is created you are ready to send some requests. You have one of two methods to start with
- forExternalService() - if you want to send a request to an URL (for example http://some.address.com/api/whatever/123)
- forService('cola') - if you want to send a request to your collaborator (for example in your microservice.json you have provided a collaborator by name cola)
Ok, so we picked either of them and now we can choose which http method we want to call. You can pick either of these:
delete
get
head
options
post
put
Depending on the method you now can have different methods available. Some HTTP methods accept request bodies others don't. So you may have the body() method but it's not obligatory.
You can use the withHeaders() method to provide additional headers. There are some predefined most commonly used methods, but you can also provide your own custom headers if you want to.
You can receive a response in a number of ways:
- anObject().ofType(String) - converts the body of response to String (you can provide any other type)
- aResponseEntity().ofType(String) - returns the response as a ResponseEntity with body of type String (you can provide any other type)
- ignoringResponse() - doesn't return any type of response
There are also some additional custom response returning methods like forLocation() etc. but they are specific for given HTTP methods.
For more examples please check the specifications available under com.ofg.infrastructure.web.resttemplate.fluent package under HTTP method named packages.
Let's assume that we want to send a request to http://some.address.com/api/whatever/123
serviceRestClient.forExternalService().
post().
onUrl('http://some.address.com/api/whatever/123').
body('''{"some":"json"}''').
withHeaders().
contentTypeJson().
andExecuteFor().
anObject().
ofType(String)
Let's assume that we want to send a request to a service 'cola' (where 'cola' is an alias defined in 'microservice.json') that has registered itself in Zookeeper at address http://some.address.com and we want to call it at path /api/whatever/123
serviceRestClient.forService('cola').
post().
onUrl('/api/whatever/123').
body('''{"some":"json"}''').
withHeaders().
contentTypeJson().
andExecuteFor().
anObject().
ofType(String)
This client has built in retrying mechanism supported:
@Autowired
AsyncRetryExecutor executor
serviceRestClient
.forExternalService()
.retryUsing(
executor
.withMaxRetries(5)
.withFixedBackoff(2_000)
.withUniformJitter())
.delete()
.onUrl(SOME_SERVICE_URL)
.ignoringResponseAsync()
If you are using retry mechanism, another features is enabled - asynchronous invocation. By appending Async
to last method you will get ListenableFuture
instance. This way you can easily run multiple requests
concurrently, combine them, etc.
Every REST call can be optionally wrapped in Hystrix command. In case of retries every retry attempt is a separate command:
serviceRestClient.forExternalService()
.get()
.withCircuitBreaker(
HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("Group"))
.andCommandKey(HystrixCommandKey.Factory.asKey("Command")))
.onUrl("http://localhost:8080/ping")
.andExecuteFor()
.anObject().ofType(String.class)
If you want to use only this module just add a dependency:
repositories {
jcenter()
}
dependencies {
compile 'com.ofg:micro-infra-spring-base:x.y.z'
}
and enable this via annotation:
@Configuration
@EnableServiceRestClient
class MyWebAppConfiguration {
}