Ribbon介绍
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。
怎么映入Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!--重试机制-->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
Ribbon 实现了负载均衡
- 它是怎么实现的负载均衡算法,有哪些负载均衡算法
- 怎么通过实例名获取IP的(肯定是依赖于注册中心EnableDiscoveryClient 也是可以脱离注册中型 指定server)
zk注册的例子
{"name":"zk-service","id":"01a5f6f9-9977-4601-90e8-79c5dd7862a3","address":"SF0001371805LA.sf.com","port":8010,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1","name":"zk-service","metadata":{}},"registrationTimeUTC":1560935528331,"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}}
Ribbon的使用
- 使用RestTemplate来做实现的,去访问Rest的资源的,而Ribbon是在 patchForObject 里面的 实现类
RibbonHttpRequest 的
HttpResponse response = client.executeWithLoadBalancer(request, config);
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
.withLoadBalancerContext(this)
.withRetryHandler(handler)
.withLoadBalancerURI(request.getUri())
.build();
try {
return command.submit(
new ServerOperation<T>() {
@Override
public Observable<T> call(Server server) {
URI finalUri = reconstructURIWithServer(server, request.getUri());
S requestForServer = (S) request.replaceUri(finalUri);
try {
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
}
catch (Exception e) {
return Observable.error(e);
}
}
})
.toBlocking()
.single();
} catch (Exception e) {
Throwable t = e.getCause();
if (t instanceof ClientException) {
throw (ClientException) t;
} else {
throw new ClientException(e);
}
}
这里面提现了负载均衡和重试策略,另外体现了响应式编程和流式编程,只不过这里面是用了阻塞的方式toBlocking
Ribbon加载流程(可以和Eureka个Zk继承)
- Ribbon的初始化(使用了autoconfig的)RibbonAutoConfiguration 类
- 如果没有和任何的注册中心集成(集成是直接引入atuoconfig)的话,就使用默认的
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
默认的话实现了RibbonClientConfiguration 维护我们的Server使用的配置server方式
如果使用的是Eureka(EurekaRibbonClientConfiguration)
使用zk(ZookeeperRibbonClientConfiguration)
Ribbon的负载均衡的策略
负载均衡的类的及继承图
负载均衡的规则接口是IRule 实现他的不同的规则类有
默认策略:ZoneAvoidanceRule ===> RibbonClientConfiguration#ribbonRule()
• ZoneAvoidanceRule 规避区域策略
• AbstractLoadBalancerRule 策略的抽象类,它在内部定义了ILoadBalancer对象,这个对象主要是用来在具体选择哪种策略的时候,获取到负载均衡器中维护的信息的。
• AvailabilityFilteringRule该策略继承自抽象策略PredicateBasedRule所以也继承了"先过滤清单,再轮询选择"的基本处理逻辑,该策略通过线性抽样的方式直接尝试可用且较空闲的实例来使用,优化了父类每次都要遍历所有实例的开销。
• BestAvailableRule继承自ClientConfigEnabledRoundRobinRule该策略的特性是可选出最空闲的实例
• ClientConfigEnabledRoundRobinRule该策略较为特殊,我们一般不直接使用它。因为它本身并没有实现什么特殊的处理逻辑。通过继承该策略,默认的choose就实现了线性轮询机制,在子类中做一些高级策略时通常可能存在。一些无法实施的情况,就可以用父类的实现作为备选
• PredicateBasedRule抽象策略,继承自ClientConfigEnabledRoundRobinRule,基于Predicate的策略 Predicateshi Google Guava Collection工具对集合进行过滤的条件接口
• RandomRule 随机数策略,它就是通过一个随机数来获取uplist的某一个下标,再返回。
• RetryRule 带重试机制策略,它在内部还定义了一个IRule,默认使用了RoundRobinRule,在内部实现了反复重试的机制,如果重试能够得到一个服务,就返回,如果不能就会根据之前设置的时间来决定,时间一到就返回null.
• RoundRobinRule 一个轮询策略,通过一个count计数变量,每次循环都会累加,注意,如果一直没有server可供选择达到了10次,就会打印一个警告信息。
• WeightedResponseTimeRule 这个策略是对轮询策略的扩展,增加了根据实例的运行情况来计算权重,并根据权重来挑选实例,用以达到更好的分配结果。
获取服务列表
进入到RibbonLoadBalancerClient中找到reconstructURI()方法,可以看到有一个RibbonLoadBalancerContext类,进入这个类,会发现它在构造器中传入了一个ILoadBalancer
在ILoadBalancer这个接口中存在addServers(List newServers)在内部我们可以得到一个结论,
我们所有的host:port形式的东西都存放在RibbonLoadBalancerContext中,它会去通过一个实例去获取到我们的
在做注册中心的时候会调用这个把注册的服务信息初始化到LoadBalancerContext里面
怎么实现一个简单服务的高可用的方式
高可用的实现点
- 把你们需要高可用的节点地址注册到注册中心(eureka zk redis 数据库)
- 实现高可用的节点断开的一个失效方式(redis采用的key的失效时间 数据库可以采用时间戳间隔设置)
- 实现客户端的负载均衡算法(ribbon 来做)