Skip to content
This repository has been archived by the owner on May 11, 2021. It is now read-only.

Abstraction over RestTemplate: bound with service discovery

Marcin Zajączkowski edited this page May 5, 2015 · 3 revisions

Description

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.

What does it consist of?

ServiceRestClient

The entry point to the abstraction is ServiceRestClient. It takes two parameters to its constructor

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)

HTTP Methods

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

Request body

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.

Request headers

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.

Response

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.

Example of usage

For more examples please check the specifications available under com.ofg.infrastructure.web.resttemplate.fluent package under HTTP method named packages.

Example of sending to URL

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)

Example of sending to collaborator

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)

Asynchronous retry mechanism

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.

Hystrix support

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)

Module configuration

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 {

}