Spring Cloud Ribbon
简介
Spring Cloud Ribbon 是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud 的封装,可以让我们轻松的将面向服务的Rest模板请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立的部署,但它几乎存在于每一个Spring CLoud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际都是通过Ribbon来实现的,包括Feign它也是基于Ribbon实现的工具。
负载均衡
负载均衡在系统架构中是一个非常重要并且必须实施的内容。负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。
1. 客户端负载均衡
客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,请求发到服务端前就完成负载均衡分配
2. 服务端负载均衡
请求发送到服务端后,由服务端进行负载均衡,决定发往何处
微服务使用Ribbon
- 服务提供者只需要启动多个服务实例并注册到一个注册中心或者多个相关的服务注册中心
- 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用
RestTemplate
1. 注意uri的参数赋值
url拼接:
UriComponentsBuilder uriBuilder =UriComponentsBuilder.fromUriString("http://eureka-client/name");
uriBuilder.queryParam("name","lipeng");
URI uri = uriBuilder.build().toUri();
uri参数赋值:
String uri = "http://hostname:port//name?name={0}&age={1}
String uri = "http://hostname:port//name?name={name}&age={age}"
2. 配置
@Configuration
public class RestTemplateConfiguration
{
@Autowired
private RestTemplateBuilder restTemplateBuilder;
@Bean
public RestTemplate getRestTemplate()
{
restTemplateBuilder.setConnectTimeout(10000);
return restTemplateBuilder.build();
}
}
负载均衡器ILoadBalancer
平衡器接口ILoadBalancer
public interface ILoadBalancer {
/**
* Initial list of servers.
* This API also serves to add additional ones at a later time
* The same logical server (host:port) could essentially be added multiple times
* (helpful in cases where you want to give more "weightage" perhaps ..)
*
* @param newServers new servers to add
*/
public void addServers(List<Server> newServers);
/**
* Choose a server from load balancer.
*
* @param key An object that the load balancer may use to determine which server to return. null if
* the load balancer does not use this parameter.
* @return server chosen
*/
public Server chooseServer(Object key);
/**
* To be called by the clients of the load balancer to notify that a Server is down
* else, the LB will think its still Alive until the next Ping cycle - potentially
* (assuming that the LB Impl does a ping)
*
* @param server Server to mark as down
*/
public void markServerDown(Server server);
/**
* @deprecated 2016-01-20 This method is deprecated in favor of the
* cleaner {@link #getReachableServers} (equivalent to availableOnly=true)
* and {@link #getAllServers} API (equivalent to availableOnly=false).
*
* Get the current list of servers.
*
* @param availableOnly if true, only live and available servers should be returned
*/
@Deprecated
public List<Server> getServerList(boolean availableOnly);
/**
* @return Only the servers that are up and reachable.
*/
public List<Server> getReachableServers();
/**
* @return All known servers, both reachable and unreachable.
*/
public List<Server> getAllServers();
实现:
1. AbstractLoadBalancer
平衡器接口ILoadBalancer的抽象实现
2. BaseLoadBalancer
Ribbon负载均衡器的基础实现类
- 定义并维护两个存储服务实例的Server对象列表,一个用于存储所有服务实例的清单,一个用于存储正常服务的实例清单
- 定义了用来存储负载均衡器的各服务实例属性和统计信息的LoadBalancerStates对象
- 定义了检查服务实例是否正常服务的IPing服务对象,在BaseLoadBalancer中默认是null,需要在构造时注入它的具体实现
- 定义了IPingStrategy检查服务实例操作的执行策略对象
- 定义了负载均衡的处理规则IRule对象,负载均衡器实际将服务实例选择任务委托给了IRule实例的choose函数来实现默认RoundRobbinRule实现了最基本且常用的线性负载均衡规则
3. DynamicServerListLoadBalancer
DynamicServerListLoadBalancer继承BaseLoadBalancer类,是对基础负载均衡器的扩展,在该负载均衡器中实现了服务实例清单在运行期的动态更新能力,同时,它还具备了对服务实例清单的过滤功能,也就是说我们可以通过过滤器来选择性的获取一批服务实例清单。
4. ZoneAwareloadBalancer
ZoneAwareloadBalancer是对DynamicServerListLoadBalancer的扩展,区域感知负载均衡器
负载均衡策略IRule
- AbstractLoadBalancerRule
负载均衡策略的抽象类,在该抽象类中定义了负载均衡器ILoadBalancer对象,该对象能够在具体实现选择服务策略时,获取到一些负载均衡器中维护的信息来作为分配依据,并以此设计一些算法来实现针对特定场景的高效策略 - RandomRule
从服务实例清单中随机选择一个服务实例的功能 - RoundRobinRule
线性轮询的方式依次选择每个服务实例的功能 - RetryRule
具备重试机制的实例选择功能 - WeightedResponseTimeRule
根据实例的运行情况来计算权重,并根据权重来挑选实例,以达到更优的分配效果 - ClientConfigEnabledRoundRobinRule
- BestAvailableRule
继承ClientConfigEnabledRoundRobinRule,
它通过遍历负载均衡器中维护的所有服务实例,会过滤掉故障的实例,并找出并发请求数最小的一个,所以该策略的特性是可选出最空闲的实例。 - PredicateBasedRule
- Availab仆ityFilteringRule
- ZoneAvoidanceRule
配置
代码配置
Creating a bean of one of those type and placing it in a @RibbonClient configuration (such as FooConfiguration above) lets you override each one of the beans described, as shown in the following 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();
}
}
@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();
}
}
对所有ribbon客户端统一配置
@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;
}
}
配置文件配置
<clientName>.ribbon.NFLoadBalancerClassName: Should implement ILoadBalancer
<clientName>.ribbon.NFLoadBalancerRuleClassName: Should implement IRule
<clientName>.ribbon.NFLoadBalancerPingClassName: Should implement IPing
<clientName>.ribbon.NIWSServerListClassName: Should implement ServerList
<clientName>.ribbon.NIWSServerListFilterClassName: Should implement ServerListFilter
To set the IRule for a service name called users, you could set the following properties:
application.yml.
users:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
重试机制
配置
//开启重试机制
spring.cloud.loadbalancer.retry.enabled=true
//断路器的超时时间需要大于Ribbon的超时时间, 不然不会触发
重试。
hystrix.command.default.execution.isolation.thread.timeoutinMilliseconds=lOOOO
hello-service.ribbon.ConnectTimeout=250
hello-service.ribbon.ReadTimeout= lOOO
hello-service.ribbon.OkToRetryOnAllOperations=true
hello-service.ribbon.MaxAutoRe七riesNex七Server=2
hello-service.ribbon.MaxAutoRetries=l
总结
Ribbon客户端负载均衡,其在客户端维护服务列表,请求的分发全在客户端完成,降低服务端负载均衡的压力
Spring Cloud Eureka实现的服务治理机制强调了CAP原理中的AP,即可用性与可靠性,它与ZooKeeper这类强调CP(一致性,可靠性)的服务治理框架最大的区别就是:Eureka为了实现更高的服务可用性,牺牲了一定的一致性,在极端情况下它宁愿接受故障实例也不丢掉健康实例。
示例
@RibbonClient(name = "eureka-client", configuration = com.honddy.eurekaribbon.configuration.RibbonConfiguration.Ribbon.class)
@Configuration
public class RibbonConfiguration
{
private Logger logger = LoggerFactory.getLogger(getClass());
@Configuration
public class Ribbon
{
@Bean
public IRule getRule()
{
logger.info("ribbon use random rule----------------");
return new RandomRule();
}
}
}
实现原理(简述)
每个Eureka客户端从Eureka服务端获取并维护一个服务列表,客户端通过配置的负载均衡器、负载均衡策略挑选目标服务,进行请求分发