On this page
16. Client Side Load Balancer: Ribbon
Ribbon is a client side load balancer which gives you a lot of control over the behaviour of HTTP and TCP clients. Feign already uses Ribbon, so if you are using @FeignClient
then this section also applies.
A central concept in Ribbon is that of the named client. Each load balancer is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer (e.g. using the @FeignClient
annotation). Spring Cloud creates a new ensemble as an ApplicationContext
on demand for each named client using RibbonClientConfiguration
. This contains (amongst other things) an ILoadBalancer
, a RestClient
, and a ServerListFilter
.
16.1 How to Include Ribbon
To include Ribbon in your project use the starter with group org.springframework.cloud
and artifact id spring-cloud-starter-netflix-ribbon
. See the Spring Cloud Project page for details on setting up your build system with the current Spring Cloud Release Train.
16.2 Customizing the Ribbon Client
You can configure some bits of a Ribbon client using external properties in <client>.ribbon.*
, which is no different than using the Netflix APIs natively, except that you can use Spring Boot configuration files. The native options can be inspected as static fields in CommonClientConfigKey (part of ribbon-core).
Spring Cloud also lets you take full control of the client by declaring additional configuration (on top of the RibbonClientConfiguration
) using @RibbonClient
. Example:
@Configuration
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
public class TestConfiguration {
}
In this case the client is composed from the components already in RibbonClientConfiguration
together with any in FooConfiguration
(where the latter generally will override the former).
The
FooConfiguration
has to be@Configuration
but take care that it is not in a@ComponentScan
for the main application context, otherwise it will be shared by all the@RibbonClients
. If you use@ComponentScan
(or@SpringBootApplication
) you need to take steps to avoid it being included (for instance put it in a separate, non-overlapping package, or specify the packages to scan explicitly in the@ComponentScan
).
Spring Cloud Netflix provides the following beans by default for ribbon ( BeanType
beanName: ClassName
):
IClientConfig
ribbonClientConfig:DefaultClientConfigImpl
IRule
ribbonRule:ZoneAvoidanceRule
IPing
ribbonPing:DummyPing
ServerList<Server>
ribbonServerList:ConfigurationBasedServerList
ServerListFilter<Server>
ribbonServerListFilter:ZonePreferenceServerListFilter
ILoadBalancer
ribbonLoadBalancer:ZoneAwareLoadBalancer
ServerListUpdater
ribbonServerListUpdater:PollingServerListUpdater
Creating a bean of one of those type and placing it in a @RibbonClient
configuration (such as FooConfiguration
above) allows you to override each one of the beans described. Example:
@Configuration
protected static class FooConfiguration {
@Bean
public ZonePreferenceServerListFilter serverListFilter() {
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.setZone("myTestZone");
return filter;
}
@Bean
public IPing ribbonPing() {
return new PingUrl();
}
}
This replaces the NoOpPing
with PingUrl
and provides a custom serverListFilter
16.3 Customizing default for all Ribbon Clients
A default configuration can be provided for all Ribbon Clients using the @RibbonClients
annotation and registering a default configuration as shown in the following example:
@RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)
public class RibbonClientDefaultConfigurationTestsConfig {
public static class BazServiceList extends ConfigurationBasedServerList {
public BazServiceList(IClientConfig config) {
super.initWithNiwsConfig(config);
}
}
}
@Configuration
class DefaultRibbonConfig {
@Bean
public IRule ribbonRule() {
return new BestAvailableRule();
}
@Bean
public IPing ribbonPing() {
return new PingUrl();
}
@Bean
public ServerList<Server> ribbonServerList(IClientConfig config) {
return new RibbonClientDefaultConfigurationTestsConfig.BazServiceList(config);
}
@Bean
public ServerListSubsetFilter serverListFilter() {
ServerListSubsetFilter filter = new ServerListSubsetFilter();
return filter;
}
}
16.4 Customizing the Ribbon Client using properties
Starting with version 1.2.0, Spring Cloud Netflix now supports customizing Ribbon clients using properties to be compatible with the Ribbon documentation .
This allows you to change behavior at start up time in different environments.
The supported properties are listed below and should be prefixed by <clientName>.ribbon.
:
NFLoadBalancerClassName
: should implementILoadBalancer
NFLoadBalancerRuleClassName
: should implementIRule
NFLoadBalancerPingClassName
: should implementIPing
NIWSServerListClassName
: should implementServerList
NIWSServerListFilterClassName
should implementServerListFilter
Classes defined in these properties have precedence over beans defined using
@RibbonClient(configuration=MyRibbonConfig.class)
and the defaults provided by Spring Cloud Netflix.
To set the IRule
for a service name users
you could set the following:
application.yml.
users:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
See the Ribbon documentation for implementations provided by Ribbon.
16.5 Using Ribbon with Eureka
When Eureka is used in conjunction with Ribbon (i.e., both are on the classpath) the ribbonServerList
is overridden with an extension of DiscoveryEnabledNIWSServerList
which populates the list of servers from Eureka. It also replaces the IPing
interface with NIWSDiscoveryPing
which delegates to Eureka to determine if a server is up. The ServerList
that is installed by default is a DomainExtractingServerList
and the purpose of this is to make physical metadata available to the load balancer without using AWS AMI metadata (which is what Netflix relies on). By default the server list will be constructed with "zone" information as provided in the instance metadata (so on the remote clients set eureka.instance.metadataMap.zone
), and if that is missing it can use the domain name from the server hostname as a proxy for zone (if the flag approximateZoneFromHostname
is set). Once the zone information is available it can be used in a ServerListFilter
. By default it will be used to locate a server in the same zone as the client because the default is a ZonePreferenceServerListFilter
. The zone of the client is determined the same way as the remote instances by default, i.e. via eureka.instance.metadataMap.zone
.
The orthodox "archaius" way to set the client zone is via a configuration property called "@zone", and Spring Cloud will use that in preference to all other settings if it is available (note that the key will have to be quoted in YAML configuration).
If there is no other source of zone data then a guess is made based on the client configuration (as opposed to the instance configuration). We take
eureka.client.availabilityZones
, which is a map from region name to a list of zones, and pull out the first zone for the instance’s own region (i.e. theeureka.client.region
, which defaults to "us-east-1" for comatibility with native Netflix).
16.6 Example: How to Use Ribbon Without Eureka
Eureka is a convenient way to abstract the discovery of remote servers so you don’t have to hard code their URLs in clients, but if you prefer not to use it, Ribbon and Feign are still quite amenable. Suppose you have declared a @RibbonClient
for "stores", and Eureka is not in use (and not even on the classpath). The Ribbon client defaults to a configured server list, and you can supply the configuration like this
application.yml.
stores:
ribbon:
listOfServers: example.com,google.com
16.7 Example: Disable Eureka use in Ribbon
Setting the property ribbon.eureka.enabled = false
will explicitly disable the use of Eureka in Ribbon.
application.yml.
ribbon:
eureka:
enabled: false
16.8 Using the Ribbon API Directly
You can also use the LoadBalancerClient
directly. Example:
public class MyClass {
@Autowired
private LoadBalancerClient loadBalancer;
public void doStuff() {
ServiceInstance instance = loadBalancer.choose("stores");
URI storesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
// ... do something with the URI
}
}
16.9 Caching of Ribbon Configuration
Each Ribbon named client has a corresponding child Application Context that Spring Cloud maintains, this application context is lazily loaded up on the first request to the named client. This lazy loading behavior can be changed to instead eagerly load up these child Application contexts at startup by specifying the names of the Ribbon clients.
application.yml.
ribbon:
eager-load:
enabled: true
clients: client1, client2, client3
16.10 How to Configure Hystrix thread pools
If you change zuul.ribbonIsolationStrategy
to THREAD, the thread isolation strategy for Hystrix will be used for all routes. In this case, the HystrixThreadPoolKey is set to "RibbonCommand" as default. It means that HystrixCommands for all routes will be executed in the same Hystrix thread pool. This behavior can be changed using the following configuration and it will result in HystrixCommands being executed in the Hystrix thread pool for each route.
application.yml.
zuul:
threadPool:
useSeparateThreadPools: true
The default HystrixThreadPoolKey in this case is same with service ID for each route. To add a prefix to HystrixThreadPoolKey, set zuul.threadPool.threadPoolKeyPrefix
to a value that you want to add. For example:
application.yml.
zuul:
threadPool:
useSeparateThreadPools: true
threadPoolKeyPrefix: zuulgw
16.11 How to Provide a Key to Ribbon’s IRule
If you need to provide your own IRule
implementation to handle a special routing requirement like a canary test, you probably want to pass some information to the choose
method of IRule
.
com.netflix.loadbalancer.IRule.java.
public interface IRule{
public Server choose(Object key);
:
You can provide some information that will be used to choose a target server by your IRule
implementation like the following:
RequestContext.getCurrentContext()
.set(FilterConstants.LOAD_BALANCER_KEY, "canary-test");
If you put any object into the RequestContext
with a key FilterConstants.LOAD_BALANCER_KEY
, it will be passed to the choose
method of IRule
implementation. Above code must be executed before RibbonRoutingFilter
is executed and Zuul’s pre filter is the best place to do that. You can easily access HTTP headers and query parameters via RequestContext
in pre filter, so it can be used to determine LOAD_BALANCER_KEY
that will be passed to Ribbon. If you don’t put any value with LOAD_BALANCER_KEY
in RequestContext
, null will be passed as a parameter of choose
method.