Ribbon是负载均衡器,用于解决服务集群的负载均衡
集中式LB 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程内LB 将LB逻辑到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
```
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2.2.0.RELEASE
pom
import
org.springframework.cloud
spring-cloud-dependencies
Hoxton.RELEASE
pom
import
```
启动类加注解
// 启动类上开启服务注册与发现功能
@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;
}
}
在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 execute(String serviceId, LoadBalancerRequest 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);
}
}
}
接口 | 作用 | 默认值 |
---|---|---|
IClientConfig | 读取配置 | DefaultClientConfigImpl |
IRule | 负载均衡规则,选择实例 | ZoneAvoidanceRule |
IPing | 筛选掉ping不通的实例 | DummyPing |
ServerList | 交给Ribbon的实例列表 | Ribbon:ConfigurationBasedServerList Spring Cloud Alibaba:NacosServerList |
ServerListFilter | 过滤掉不符合条件的实例 | ZonePreferenceServerListFilter |
ILoadBalance | Ribbon的入口 | ZoneAwareLoadBalance |
ServerListUpdater | 更新交给Ribbon的List的策略 | PollingServerListUpdater |
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)【优中选优,再轮询】 |
编写配置
在客户端配置
注意:不能在启动类的同级/子集下配置
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})
直接在页面上修改权重,是无效的。因为负载均衡中引入了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