Connecting a Spring Cloud application to Cloudant Service with Feign and Hystrix

5 min read

By: Jeffrey Ruffolo

Connecting a Spring Cloud application to Cloudant Service with Feign and Hystrix

In this post, we’ll create a simple Spring Cloud application that demonstrates the capabilities of Feign and Hystrix by connecting to a Cloudant service on Bluemix.  Feign is a declarative web service client, which comes with Hystrix built in when you use it with Spring Cloud.  Hystrix is a Netflix OSS library that implements the circuit breaker pattern.

Prerequisites

For this project, you will only need to have Java and Maven installed and configured on your PATH. I will also assume you have setup a Cloudant service in Bluemix and have the credentials available.

Building the Spring Cloud Application

Setup Maven project

To begin, let’s create a project with the following structure:

project/spring-feign-hystrix/src/main/java/application/

Next create a pom.xml file with the following contents:

<br>
<?xml version="1.0" encoding="UTF-8"?><br>
<project xmlns="http://maven.apache.org/POM/4.0.0"<br>
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br>
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><br>
    <modelVersion>4.0.0</modelVersion><p></p>
<p>    <groupId>com.application</groupId><br>
    <artifactId>spring-feign-hystrix</artifactId><br>
    <version>1.0-SNAPSHOT</version></p>
<p>    <parent><br>
        <groupId>org.springframework.boot</groupId><br>
        <artifactId>spring-boot-starter-parent</artifactId><br>
        <version>1.5.2.RELEASE</version><br>
    </parent></p>
<p>    <dependencyManagement><br>
        <dependencies><br>
            <dependency><br>
                <groupId>org.springframework.cloud</groupId><br>
                <artifactId>spring-cloud-dependencies</artifactId><br>
                <version>Dalston.SR1</version><br>
                <type>pom</type><br>
                <scope>import</scope><br>
            </dependency><br>
        </dependencies><br>
    </dependencyManagement></p>
<p>    <dependencies><br>
        <dependency><br>
            <groupId>org.springframework.boot</groupId><br>
            <artifactId>spring-boot-starter-web</artifactId><br>
        </dependency><br>
        <dependency><br>
            <groupId>org.springframework.cloud</groupId><br>
            <artifactId>spring-cloud-starter-config</artifactId><br>
        </dependency><br>
        <dependency><br>
            <groupId>org.springframework.cloud</groupId><br>
            <artifactId>spring-cloud-starter-feign</artifactId><br>
        </dependency><br>
        <dependency><br>
            <groupId>org.springframework.cloud</groupId><br>
            <artifactId>spring-cloud-starter-hystrix</artifactId><br>
        </dependency><br>
    </dependencies></p>
<p>    <properties><br>
        <java.version>1.8</java.version><br>
    </properties></p>
<p>    <build><br>
        <plugins><br>
            <plugin><br>
                <groupId>org.springframework.boot</groupId><br>
                <artifactId>spring-boot-maven-plugin</artifactId><br>
            </plugin><br>
        </plugins><br>
    </build><br>
</project><br>
</p>

Create FeignClient with Hystrix Fallback

The first class we create will be the FeignClient, and it will also contain the Hystrix implementation.  There will be a lot happening in this class, so we’ll break it down and build it one step at a time. I’ll include the completed class at the end, if you want to see it all together.

Create a HystrixClient.java file with the following contents (I’ve left out the imports I used for now, but will provide them in the assembled file contents):

<br>
@FeignClient(name = "hystrixclient", url = "localhost:8090", fallback = HystrixClient.HystrixClientFallback.class)<br>
public interface HystrixClient {<p></p>
<p>    @RequestMapping(value = "/populateCloudant", method = RequestMethod.GET)<br>
    ResponseEntity populateCloudant();<br>
</p>

In this code, we create an interface named HystrixClient and annotate it with @FeignClient. A name is required for our FeignClient, and can be anything you choose. The URL provided points to the service this FeignClient will interface with; in this case, it will be a service that saves documents to a Cloudant database. The fallback describes what should happen if the service does not respond appropriately.

This interface has one method: populateCloudant. The @RequestMapping annotation indicates which REST endpoint the method corresponds to. Next, we’ll setup the fallback for this method.

<br>
@Component<br>
class HystrixClientFallback implements HystrixClient {<p></p>
<p>    @Override<br>
    public ResponseEntity populateCloudant() {<br>
        return new ResponseEntity("Fallback\n", HttpStatus.OK);<br>
    }<br>
}<br>
</p>

Here we have defined a subclass to implement the fallback behavior I mentioned above. The populateCloudant method is overridden and returns a ResponseEntity telling us it has resorted to the fallback method. Near the end of this post, we will demonstrate what circumstances can lead to this fallback method being called.

In a more practical application, you might choose to use the fallback that allows your application to function in the event of failed services. Here, however, we are just interested in seeing that it has used the fallback.

Sometimes, it is useful to know what caused the fallback. In Spring Cloud, we can do this by implementing the FallbackFactory. This code is similar in form and function to the HystrixClientFallback we created previously. However, instead of just informing us that a fallback has occurred, it will give us some information about why it happened.

<br>
@Component<br>
class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {<p></p>
<p>    @Override<br>
    public HystrixClient create(Throwable cause){<br>
        return new HystrixClient(){<br>
            @Override<br>
            public ResponseEntity populateCloudant() {<br>
                return new ResponseEntity("Fallback cause: " + cause.getMessage() + "\n", HttpStatus.OK);<br>
            }<br>
        };<br>
    }<br>
}<br>
</p>

In order to use the FallbackFactory, we will need to change this line from the @FeignClient annotation above:

<br>
@FeignClient(name = "hystrixclient", url = "localhost:8090", fallbackFactory = HystrixClient.HystrixClientFallbackFactory.class)<br>

Here is the HystrixClient.java file all together:

<br>
package application;<p></p>
<p>import feign.hystrix.FallbackFactory;<br>
import org.springframework.cloud.netflix.feign.FeignClient;<br>
import org.springframework.http.HttpStatus;<br>
import org.springframework.http.ResponseEntity;<br>
import org.springframework.stereotype.Component;<br>
import org.springframework.web.bind.annotation.RequestMapping;<br>
import org.springframework.web.bind.annotation.RequestMethod;</p>
<p>@FeignClient(name = "hystrixclient", url = "localhost:8090", fallbackFactory = HystrixClient.HystrixClientFallbackFactory.class)<br>
public interface HystrixClient {</p>
<p>    @RequestMapping(value = "/populateCloudant", method = RequestMethod.GET)<br>
    ResponseEntity populateCloudant();</p>
<p>    @Component<br>
    class HystrixClientFallback implements HystrixClient {</p>
<p>        @Override<br>
        public ResponseEntity populateCloudant() {<br>
            return new ResponseEntity("Fallback\n", HttpStatus.OK);<br>
        }<br>
    }</p>
<p>    @Component<br>
    class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {</p>
<p>        @Override<br>
        public HystrixClient create(Throwable cause){<br>
            return new HystrixClient(){<br>
                @Override<br>
                public ResponseEntity populateCloudant() {<br>
                    return new ResponseEntity("Fallback cause: " + cause.getMessage() + "\n", HttpStatus.OK);<br>
                }<br>
            };<br>
        }<br>
    }<br>
}<br>
</p>

Make the Application Accessible

Now let’s finish up this application by making it executable and creating a REST endpoint. Create a Application.java file with the following contents:

<br>
package application;<p></p>
<p>import org.springframework.beans.factory.annotation.Autowired;<br>
import org.springframework.boot.SpringApplication;<br>
import org.springframework.boot.autoconfigure.SpringBootApplication;<br>
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;<br>
import org.springframework.cloud.netflix.feign.EnableFeignClients;<br>
import org.springframework.http.ResponseEntity;<br>
import org.springframework.web.bind.annotation.RequestMapping;<br>
import org.springframework.web.bind.annotation.RestController;</p>
<p>@SpringBootApplication<br>
@RestController<br>
@EnableFeignClients<br>
@EnableCircuitBreaker<br>
public class Application {</p>
<p>    @Autowired<br>
    private HystrixClient hystrixClient;</p>
<p>    public static void main(String[] args) {<br>
        SpringApplication.run(Application.class, args);<br>
    }</p>
<p>    @RequestMapping("/feign")<br>
    public ResponseEntity feign() {<br>
        return hystrixClient.populateCloudant();<br>
    }<br>
}<br>
</p>

This class is fairly standard for a Spring application, but with the addition of two annotations: @EnableFeignClients and @EnableCircuitBreaker, which do exactly what their names imply.

The REST endpoint (/feign) will be used later to test our application. It calls the populateCloudant method from our FeignClient and returns a ResponseEntity.

Configuring Hystrix

Hystrix is a Netflix library that implements the circuit breaker pattern.  Normally, use of this library would require you to wrap all methods in HystrixCommands. But with a FeignClient, Hystrix functionality comes included. You just need to set a few properties to configure Hystrix to your needs.

Create an application.properties file with the following contents:

<br>
feign.hystrix.enabled=true<br>
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=4000<br>
hystrix.command.default.circuitBreaker.enabled=true<br>
hystrix.command.default.circuitBreaker.requestVolumeThreshold=2<br>

The first property enables Hystrix on the FeignClients in our application. Next, we set the maximum amount of time allowed before a request is considered to be timed out and fail. Finally, we enable Hystrix circuit breakers, as well as setting the number of failed requests that will cause the circuit to open.

Note: To see other available properties, visit the Hystrix Netflix configuration wiki.

Building the Cloudant Service Application

Setup Maven project

Now create a new project with the following structure:

project/cloudant-service/src/main/java/application/

Next create a pom.xml file with the following contents:

<br>
<?xml version="1.0" encoding="UTF-8"?><br>
<project xmlns="http://maven.apache.org/POM/4.0.0"<br>
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br>
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><br>
    <modelVersion>4.0.0</modelVersion><p></p>
<p>    <groupId>com.application</groupId><br>
    <artifactId>cloudant-service</artifactId><br>
    <version>1.0-SNAPSHOT</version></p>
<p>    <parent><br>
        <groupId>org.springframework.boot</groupId><br>
        <artifactId>spring-boot-starter-parent</artifactId><br>
        <version>1.5.2.RELEASE</version><br>
    </parent></p>
<p>    <dependencies><br>
        <dependency><br>
            <groupId>org.springframework.boot</groupId><br>
            <artifactId>spring-boot-starter-web</artifactId><br>
        </dependency><br>
        <dependency><br>
            <groupId>com.cloudant</groupId><br>
            <artifactId>cloudant-client</artifactId><br>
            <version>2.7.0</version><br>
        </dependency><br>
    </dependencies></p>
<p>    <properties><br>
        <java.version>1.8</java.version><br>
    </properties></p>
<p>    <build><br>
        <plugins><br>
            <plugin><br>
                <groupId>org.springframework.boot</groupId><br>
                <artifactId>spring-boot-maven-plugin</artifactId><br>
            </plugin><br>
        </plugins><br>
    </build><br>
</project><br>
</p>

Configure CloudantClient

Find the connection credentials for your Cloudant service in Bluemix (under the Service credentials tab).  Then create an application.properties file with the following contents:

<br>
server.port=8090<p></p>
<p>cloudant.url=CLOUDANT_URL<br>
cloudant.username=CLOUDANT_USERNAME<br>
cloudant.password=CLOUDANT_PASSWORD<br>
</p>

You will need to fill in the values from your own credentials, of course.  Also, be sure to handle your credentials securely if you intend to deploy this application beyond your local runtime.

Next, we will create a configuration class, which will use these credentials to create a CloudantClient bean.  Create a CloudantClientConfig file with the following contents:

<br>
package Application;<p></p>
<p>import com.cloudant.client.api.ClientBuilder;<br>
import com.cloudant.client.api.CloudantClient;<br>
import org.springframework.beans.factory.InjectionPoint;<br>
import org.springframework.beans.factory.annotation.Value;<br>
import org.springframework.beans.factory.config.ConfigurableBeanFactory;<br>
import org.springframework.context.annotation.Bean;<br>
import org.springframework.context.annotation.Configuration;<br>
import org.springframework.context.annotation.Scope;</p>
<p>import java.net.MalformedURLException;<br>
import java.net.URL;</p>
<p>@Configuration<br>
public class CloudantClientConfig {</p>
<p>    @Value("${cloudant.url}")<br>
    protected String cloudantUrl;</p>
<p>    @Value("${cloudant.username}")<br>
    protected String cloudantUsername;</p>
<p>    @Value("${cloudant.password}")<br>
    protected String cloudantPassword;</p>
<p>    @Bean(destroyMethod = "shutdown")<br>
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)<br>
    public CloudantClient cloudantClient(InjectionPoint ip) throws MalformedURLException {<br>
        CloudantClient client = ClientBuilder.url(new URL(cloudantUrl))<br>
                .username(cloudantUsername)<br>
                .password(cloudantPassword)<br>
                .build();<br>
        return client;<br>
    }<br>
}<br>
</p>

Make the Service Accessible

First, let’s create a document that we can store in our Cloudant database.  Create an ExampleDocument.java file with the following contents:

<br>
package Application;<p></p>
<p>import java.util.UUID;</p>
<p>public class ExampleDocument {</p>
<p>    private String id = UUID.randomUUID().toString();</p>
<p>    public String toString() {<br>
        return "{ id: " + id + "}";<br>
    }<br>
}<br>
</p>

Now let’s make the application executable and create the REST endpoint that our FeignClient from above is expecting. Create an Application.java file with the following contents:

<br>
package Application;<p></p>
<p>import com.cloudant.client.api.CloudantClient;<br>
import com.cloudant.client.api.Database;<br>
import com.cloudant.client.org.lightcouch.NoDocumentException;<br>
import com.cloudant.client.org.lightcouch.TooManyRequestsException;<br>
import org.springframework.beans.factory.annotation.Autowired;<br>
import org.springframework.boot.SpringApplication;<br>
import org.springframework.boot.autoconfigure.SpringBootApplication;<br>
import org.springframework.http.HttpStatus;<br>
import org.springframework.http.ResponseEntity;<br>
import org.springframework.web.bind.annotation.RequestMapping;<br>
import org.springframework.web.bind.annotation.RestController;</p>
<p>@SpringBootApplication<br>
@RestController<br>
public class Application {</p>
<p>    public static void main(String[] args) {<br>
        SpringApplication.run(Application.class, args);<br>
    }</p>
<p>    @Autowired<br>
    private CloudantClient client;</p>
<p>    @RequestMapping("/populateCloudant")<br>
    public ResponseEntity cloudant(){<br>
        Database db = client.database("example-db", true);</p>
<p>        try{<br>
            db.save(new ExampleDocument());<br>
        } catch (TooManyRequestsException e){<br>
            e.printStackTrace();<br>
            return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);<br>
        }</p>
<p>        return new ResponseEntity(HttpStatus.OK);<br>
    }<br>
}<br>
</p>

When a request is sent to the /populateCloudant REST endpoint, it will find a database named “example-db” (and create if not found), then try to save an instance of ExampleDocument in it. Because Cloudant is rate-limited, it is possible that our CloudantClient may be unable to write to the database. We catch the corresponding exception here.

Testing the Application

Now we’ll do a couple tests to see if everything is working. Startup both of your Spring applications locally by running the following command in both project directories:

<br>
$ mvn spring-boot:run<br>

First, let’s check that the FeignClient is working with the “cloudant-service” application:

<br>
$ curl localhost:8080/feign<br>

This request should finish after about a second with no message. Next, let’s test the case where the “cloudant-service” does not respond. This scenario is similar to what would occur if you hit the rate-limit for your Cloudant service in Bluemix.

We’ll force our “cloudant-service” to not respond by ending its process. After you do that, let’s test the circuit breaker:

<br>
$ curl localhost:8080/feign<br>
Fallback cause: Connection refused (Connection refused) executing GET http://localhost:8090/populateCloudant<br>
$ curl localhost:8080/feign<br>
Fallback cause: Connection refused (Connection refused) executing GET http://localhost:8090/populateCloudant<br>
$ curl localhost:8080/feign<br>
Fallback cause: Hystrix circuit short-circuited and is OPEN<br>

After running these commands, you should see a message saying “Fallback cause: Hystrix circuit short-circuited and is OPEN.” Because the request has failed more than two times (a threshold we specified in the circuitbreaker properties above), the circuit has opened and will respond to all requests with the fallback method. The default amount of time for the circuit to remain open is 10 seconds. After which, it will close and continue trying to send requests to the service.

Conclusion

If you followed along with me through this post, you have now created a Spring Cloud application that includes Feign and Hystrix functionality.  Our application uses a FeignClient to connect to a Cloudant service on Bluemix, and has Hystrix circuit breaker protection for failed requests.  We demonstrated the Hystrix functionality with a couple simple tests.

Be the first to hear about news, product updates, and innovation from IBM Cloud