Spring Cloud Rest Client with Netflix Ribbon – Basics
In an earlier blog post I had covered the different options for a REST client in the Spring Cloud world. All the options wrap around a Netflix OSS based component called Ribbon which handles the aspects related to loadbalancing the calls across different instances hosting a service, handling failovers, timeouts etc. Here I will cover a few ways to customize the behavior of underlying Ribbon components when used with Spring Cloud and follow it up with more comprehensive customizations.
Creating a Rest Client
To recap, first consider a case where a simple service needs to be called:
A typical way to make this call using Spring is to inject in a RestTemplate and use it make this call, the following way:
public class RestTemplateBasedPongClient implements PongClient { @Autowired private RestTemplate restTemplate; @Override public MessageAcknowledgement sendMessage(Message message) { String pongServiceUrl = "http://serviceurl/message"; HttpEntity<Message> requestEntity = new HttpEntity<>(message); ResponseEntity<MessageAcknowledgement> response = this.restTemplate.exchange(pongServiceUrl, HttpMethod.POST, requestEntity, MessageAcknowledgement.class, Maps.newHashMap()); return response.getBody(); } }
There is nothing special here. When using Spring Cloud however the same code behaves differently, now the RestTemplate internally uses Netflix OSS Ribbon libraries to make the call. This helps as the typical call flow is to first find the instances running the service and then to loadbalance the calls across the instances and to maintain this state.
Rest Client With Ribbon
Let me digress a little to touch on Ribbon, Ribbon uses an abstraction called a “Named client” to control the behavior of a remote service call – the name by which the service has registered with Eureka, timeout for service calls, how many retries in case of failures etc. These are specified through configuration files, and the entries are typically along these lines, note that the “Named client” here is “samplepong” and the properties have this as a prefix:
samplepong.ribbon.MaxAutoRetries=2 samplepong.ribbon.MaxAutoRetriesNextServer=2 samplepong.ribbon.OkToRetryOnAllOperations=true samplepong.ribbon.ServerListRefreshInterval=2000 samplepong.ribbon.ConnectTimeout=5000 samplepong.ribbon.ReadTimeout=90000 samplepong.ribbon.EnableZoneAffinity=false samplepong.ribbon.DeploymentContextBasedVipAddresses=sample-pong samplepong.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList
Coming back to Spring Cloud, it supports the concept of a “Named Client” in a very clever way through the Url hostname, so the RestTemplate call would now look like this:
ResponseEntity<MessageAcknowledgement> response = this.restTemplate.exchange("http://samplepong/message", HttpMethod.POST, requestEntity, MessageAcknowledgement.class, Maps.newHashMap());
The “samplepong” in the url is the “Named client” and any customization for the behavior of the underlying Ribbon can be made by specifying the properties using this prefix. Since this is a Spring Cloud applications the properties can be specified cleanly in a yaml format along these lines:
samplepong: ribbon: DeploymentContextBasedVipAddresses: sample-pong ReadTimeout: 5000 MaxAutoRetries: 2
Conclusion
This covers the basics of how Spring Cloud abstracts out the underlying the Ribbon libraries to provide a very intuitive facade to make remote service calls in the Cloud environment. There are some details that I have skimmed over on some of the customizations, I will cover these in a newer post.
- Here is my github repo with the code that I have used for the article.
Reference: | Spring Cloud Rest Client with Netflix Ribbon – Basics from our JCG partner Biju Kunjummen at the all and sundry blog. |
Please note that the current version of the RestTemplate will not respect the retry options that you setup in your configuration. Currently, the RestTemplate just uses Eureka to get a node from the logical name. If the Rest call fails (because that node happens to be down), the template will NOT retry or attempt to fail over to the next client. I have an enhancement request logging already here : https://github.com/spring-cloud/spring-cloud-netflix/issues/648
Awesome, I did not know that Retry properties don’t work out of the box, assumed that it would work as it does with Ribbon. I will watch your PR.
Hi,
1. How is ‘samplepong’ resolved? I don’t see a `samplepong.ribbon.listOfServers` configured, are you using Eureka? Ribbon will try to resolve the name from Eureka unless A) Eureka is not on the classpath, or B) samplepong.eureka.enabled=false
2. With the latest version of Spring Cloud, a RestTemplate bean is no longer created via auto configuration. It must be created by the application and qualified with @LoadBalanced.
3. I find Ribbon retry pretty lacking. You can’t do conditional retries (not much point in retrying if you got a 40x), or retries with exponential backoff.
Yes, I am using Eureka @Abhijeet D – the configuration is here, https://github.com/bijukunjummen/spring-cloud-ping-pong-sample/blob/master/sample-ping/src/main/resources/application.yml, it has been a while since I looked at that, so it is likely to be dated. Yes, you are right about it using Eureka if Eureka is not explicitly disabled. At a different place I am using list of servers, but after explicitly overriding the Eureka configuration using this configuration – https://github.com/bijukunjummen/spring-cloud-ping-pong-sample/blob/master/sample-ping/src/main/java/org/bk/noscan/consumer/ribbon/PongDirectCallRibbonConfiguration.java Yes, on your second point, the sample I have is old. Yes, on the third point also, may be a cleaner solution could to not use Ribbon for retries and move it to something… Read more »
Using @postconstruct, autowiring a loadbalancer client does not seem to load the list of servers configured in property file, I get back null servers when I start the embedded server. I need to set an aws endpoint on startup, is there any other alternative