一、概述
1.1、什么是Ribbon
Ribbon是负载均衡器,用于解决服务集群的负载均衡
1.2、常见负载均衡
-
集中式LB 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
-
进程内LB 将LB逻辑到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
-
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
1.3、Ribbon工作流程

二、快速使用
-
导入依赖
springcloud alibaba对Ribbon做了兼容,无需单独引用
```
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
```
-
启动类加注解
// 启动类上开启服务注册与发现功能 @EnableDiscoveryClient
-
配置文件
可以不写,直接使用默认的
ribbon: eager-load: enabled: true # 是否立即加载(项目启动是,是否拉取实例列表),默认是false(项目启动不拉取,第一次请求时拉取). clients: # 指定哪些服务需要立即加载,数组形式 - cloud-goods - cloud-goods2
-
使用
在RestTemplate配置文件中,加@LoadBalanced注解
import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; @Configuration public class ApiConfig { @Bean @LoadBalanced public RestTemplate restTemplate(ClientHttpRequestFactory factory) { return new RestTemplate(factory); } @Bean public ClientHttpRequestFactory simpleClientHttpRequestFactory() { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setReadTimeout(5000); factory.setConnectTimeout(5000); return factory; } }
三、源码简单追踪
3.1、源码简单追踪
在LoadBalancerAutoConfiguration类中有个ribbonInterceptor方法,该方法调了LoadBalancerInterceptor类
public class LoadBalancerAutoConfiguration {
......
static class LoadBalancerInterceptorConfig {
......
@Bean
public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
......
}
}
LoadBalancerInterceptor类中有个关键的方法intercept,调用RibbonLoadBalancerClient的execute方法,获取实例列表
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
......
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
// 获得url
URI originalUri = request.getURI();
// 拿到serviceName
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
// 获取实例列表,实现负载均衡
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
}
RibbonLoadBalancerClient类的execute方法中,获取实例列表,实现负载均衡
public class RibbonLoadBalancerClient implements LoadBalancerClient {
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
// 获取实例列表(先从本地缓存取,没有则从注册中心取)
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
// 实现负载均衡(选取负载均衡策略)
Server server = this.getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
}
}
3.2、Ribbon核心组件
接口 | 作用 | 默认值 |
---|---|---|
IClientConfig | 读取配置 | DefaultClientConfigImpl |
IRule | 负载均衡规则,选择实例 | ZoneAvoidanceRule |
IPing | 筛选掉ping不通的实例 | DummyPing |
ServerList | 交给Ribbon的实例列表 | Ribbon:ConfigurationBasedServerList Spring Cloud Alibaba:NacosServerList |
ServerListFilter | 过滤掉不符合条件的实例 | ZonePreferenceServerListFilter |
ILoadBalance | Ribbon的入口 | ZoneAwareLoadBalance |
ServerListUpdater | 更新交给Ribbon的List的策略 | PollingServerListUpdater |
四、负载均衡策略
4.1、内置的负载均衡策略
Ribbon核心组件IRule: 根据特定算法从服务列表中选取一个需要访问的服务
IRule是一个接口,有7个自带的实现类,可以实现不同的负载均衡算法规则
规则名称 | 特点 |
---|---|
AvailabilityFilteringRule | 过滤掉一直连接失败的被标记为circuit tripped的后端Server,并 过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate 来包含过滤server的逻辑,其实就是检查status里记录的各个server 的运行状态 |
BestAvailableRule | 选择一个最小的并发请求的server,逐个考察server, 如果Server被tripped了,则跳过 |
RandomRule | 随机选择一个Server |
ResponseTimeWeightedRule | 已废弃,作用同WeightedResponseTimeRule |
WeightedResponseTimeRule | 根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低 |
RetryRule | 对选定的负载均衡策略加上重试机制,在一个配置时间段内当 选择Server不成功,则一直尝试使用subRule的方式选择一个 可用的Server |
RoundRobinRule | 轮询选择,轮询index,选择index对应位置的Server |
ZoneAvoidanceRule(默认) | 默认的负载均衡策略,即复合判断Server所在区域的性能和Server的可用性 选择Server,在没有区域的环境下,类似于轮询(RandomRule)【优中选优,再轮询】 |
4.2、切换ribbon负载均衡策略
-
编写配置
在客户端配置
注意:不能在启动类的同级/子集下配置
import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RoundRobinRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyRibbonRule { @Bean public IRule getRule() { return new RoundRobinRule(); } }
-
配置启动类
// 启动类上加该注解即可 @RibbonClient(name = "cloud-goods", configuration = {MyRibbonRule.class})
4.3、自定义策略(使用Nacos权重)
直接在页面上修改权重,是无效的。因为负载均衡中引入了ribbon,没有改ribbon配置,还是使用的ribbon的默认负载均衡策略。
使用nacos的权重,需要自定义负载均衡策略
-
配置权重
- 在页面上配置
* 在代码中配置
```
spring:
application:
name: cloud-goods # 服务名
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 指定nacos-server的地址
username: nacos
password: nacos
weight: 10
server:
port: 9001
```
-
自定义负载均衡策略
拿到nacos的配置,进行自定义的负载均衡策略
注意:不能在启动类的同级/子集下配置
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.ribbon.NacosServer; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.BaseLoadBalancer; import com.netflix.loadbalancer.Server; import org.springframework.beans.factory.annotation.Autowired; public class NacosWeightedRule extends AbstractLoadBalancerRule { /** * NacosDiscoveryProperties内置了基于权重的负载均衡算法 */ @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; /** * 读取配置文件并初始化NacosWeightedRule * @param iClientConfig */ @Override public void initWithNiwsConfig(IClientConfig iClientConfig) { } /** * 实现基于权重的负载均衡算法 * @param o */ @Override public Server choose(Object o) { try { BaseLoadBalancer loadBalancer = (BaseLoadBalancer)this.getLoadBalancer(); //想要请求的微服务名称 String name = loadBalancer.getName(); //拿到服务发现新的相关的api NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); //Nacos client自动通过基于权重的负载均衡算法,给我们选择一个实例 Instance instance = namingService.selectOneHealthyInstance(name); return new NacosServer(instance); } catch (NacosException e) { return null; } } }
-
引用自定义的负载均衡策略
-
引用配置类
import com.netflix.loadbalancer.IRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyRibbonRule { @Bean public IRule getRule() { return new NacosWeightedRule(); } }
-
启动类
// 启动类上加该注解即可 @RibbonClient(name = "cloud-goods", configuration = {MyRibbonRule.class
-
网友评论