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
.
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.
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).
Warning | |
---|---|
The |
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
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; } }
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 implement ILoadBalancer
NFLoadBalancerRuleClassName
: should implement IRule
NFLoadBalancerPingClassName
: should implement IPing
NIWSServerListClassName
: should implement ServerList
NIWSServerListFilterClassName
should implement ServerListFilter
Note | |
---|---|
Classes defined in these properties have precedence over beans defined using |
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.
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
.
Note | |
---|---|
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). |
Note | |
---|---|
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 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
Setting the property ribbon.eureka.enabled = false
will explicitly
disable the use of Eureka in Ribbon.
application.yml.
ribbon: eureka: enabled: false
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 } }
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
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
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.