一、什么是Ribbon
Ribbon是netflix 公司开源的基于客户端的负载均衡组件,是Spring Cloud大家庭中非常重要的一个模块;Ribbon应该也是整个大家庭中相对而言比较复杂的模块,直接影响到服务调度的质量和性能。全面掌握Ribbon可以帮助我们了解在分布式微服务集群工作模式下,服务调度应该考虑到的每个环节。
Ribbon内部提供了一个接口叫做ILoadBalance的接口代表负载均衡器的操作,这个接口包含添加服务器操作、选择服务器操作、获取所有的服务器列表、获取可用服务器列表等功能。
下图展示Ribbon的架构图:
二、Spring Cloud中Ribbon应用
Ribbon使用步骤如下
-
在Eureka Client中添加bom.xml 依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
dependency>
-
RestTemplate配置类添加@LoadBalanced注解
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
-
启动类添加注解@EnableEurekaClient和@RibbonClient(name = “Eureka中spring.application.name”)
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="GoyeerRibbonDemo")
public class GoyeerCloudRibbonAppliction {
public static void main(String[] args){
SpringApplication.run(GoyeerCloudRibbonAppliction.class);
}
}
-
application.yml配置内容
server:
port: 8081
eureka:
client:
service-url:
defaultZone: http://server30000:30000/eureka/,http://server30001:30001/eureka/,http://server30002:30002/eureka/
register-with-eureka: false
spring:
application:
name: GoyeerRibbon
profiles:
active: ribbonClient
-
编写控制器
@Autowired
private RestTemplateConfig restTemplateConfig;
@RequestMapping(value = "/")
public String index(){
String url="http://GOYEERBOM";
RestTemplate restTemplate=restTemplateConfig.getRestTemplate();
String str=restTemplate.getForObject(url,String.class);
return str;
}
-
服务端效果
三、Ribbon负载均衡策略设置
3.1 全局策略设置
@Configuration
public class RibbonGlobalLoadBalancingConfiguration {
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
}
3.2 基于注解的针对单个服务的 Ribbon 负载均衡策略
3.2.1 注解方式
@Configuration
@AvoidScan
public class RibbonRandomLoadBalancingConfiguration {
@Resource
IClientConfig clientConfig;
@Bean
public IRule ribbonRule(IClientConfig clientConfig) {
return new RandomRule();
}
}
3.2.2 IClientConfig针对客户端的配置管理器
@RibbonClient(
name = "goyeer-balance-ribbon", configuration = RibbonRandomLoadBalancingConfiguration.class
)
@ComponentScan(
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = AvoidScan.class)
)
3.3 配置文件方式
goyeer-balance-ribbon:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
四、如何自定义Ribbon负载均衡策略
4.1 自定义算法
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import org.springframework.stereotype.Component;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class RandomRule_ZY extends AbstractLoadBalancerRule {
private AtomicInteger atomicInteger =new AtomicInteger(0);
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null){
return null;
}
int count = 0;
Server server = null;
while (server == null && count++ < 8){
List<Server> allServers = lb.getAllServers();
List<Server> reachableServers = lb.getReachableServers();
int allServersSize = allServers.size();
int reachableServersSize = reachableServers.size();
if(allServersSize == 0 || reachableServersSize == 0){
return null;
}
int next = getServerIndex(allServersSize);
server = reachableServers.get(next);
if (server == null){
continue;
}
if (server.isAlive()){
return server;
}
server=null;
}
return server;
}
public int getServerIndex(int allServersSize){
for (;;) {
int current = this.atomicInteger.get();
int next = (current + 1) % allServersSize;
if (this.atomicInteger.compareAndSet(current, next))
return next;
}
}
public Server choose(Object key) {
return choose(getLoadBalancer(),key);
}
}
4.2 自定义配置类
@Configuration
@RibbonClient(name = "nacos-app-a", configuration = MyRuleConfig.class)
public class MyRuleConfig {
@Bean
public IRule rule() {
return new RandomRule_ZY();
}
}
4.3 设置加载自定义Ribbon配置类
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "Client",configuration = MyRuleConfig.class)
public class GoyeerCloudRibbonAppliction {
public static void main(String[] args){
SpringApplication.run(GoyeerCloudRibbonAppliction.class);
}
}
五、LoadBalancer–负载均衡器的核心
Ribbon实现负载均衡是通过LoadBalancer注解来给RestTemplate标记,来实现负载均衡。LoadBalancer是如何实现负载均衡哪?查看源码我们可以看到有一个接口LoadBalancerClinet来实现的。
5.1 什么是LoadBalancerClient
LoadBalancerClient 是 SpringCloud 提供的一种负载均衡客户端,Ribbon 负载均衡组件内部也是集成了 LoadBalancerClient 来实现负载均衡。
5.2 LoadBalancerClient原理
LoadBalancerClinet在初始化时会通过Euraka Clinet向Eureka服务端获取所有的服务实例的注册信息并缓存到本地,并且每10秒向EurakaClinet发送”Ping”请求,来判断服务的可用性。如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或重新拉取最新到本地。在得到最新服务注册信息后,ILoadBalancer根据IRule的策略进行负载均衡(默认策略为轮询)。
当使用LoadBalancerClient进行远程调用的负载均衡时,LoadBalancerClient先通过目标服务名在本地服务注册清单中获取服务提供方的某个实例,如多个服务器节点,LoadBalancerClient会通过choose()方法获取到多个节点中一个服务,拿到服务的信息之后取出服务IP信息,就可以得到完整的想要访问的IP地址和端口号,最后通过RestTempate访问具体的服务信息。
5.3 LoadBabancerClient源码解析
5.3.1 LoadBalancerClient 类图
LoadBalancerClient 是 Spring Cloud 提供的一个非常重要的接口,它继承ServiceInstanceChooser 接口,该接口的实现类是 RibbonLoadBalanceClient,它们之间的关系如下图所示:
5.3.2 LoadBalancerClient 接口源码
public interface LoadBalancerClient extends ServiceInstanceChooser
{
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
URI reconstructURI(ServiceInstance instance, URI original);
}
可以发现 LoadBalancerClient 接口继承了 ServiceInstanceChooser 接口,包含两个方法: execute和 reconstructURI
-
execute() 用来执行Request请求 -
reconstructURI() 用来重构URL
5.3.3 ServiceInstanceChooser 接口源码
public interface ServiceInstanceChooser
{
ServiceInstance choose(String serviceId);
}
ServiceInstanceChooser 接口中的主要方法为 choose(),该方法用于根据服务的名称 serviceId 来选择其中一个服务实例,即根据 serviceId 获取ServiceInstance。
5.3.4 RibbonLoadBalanceClient 实现类源码
LoadBalancerClient 的实现类 RibbonLoadBalanceClient,它用来执行最终的负载均衡请求。其中,RibbonLoadBalanceClient 的一个 choose() 方法用于选择具体的服务实例,其内部是通过 getServer() 方法交给 ILoadBalancer 完成的。
-
choose(),用来选择具体的服务实例
@Override
public ServiceInstance choose(String serviceId) {
return choose(serviceId, null);
}
public ServiceInstance choose(String serviceId, Object hint) {
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),serverIntrospector(serviceId).getMetadata(server));
}
-
getServer(),获取实例
protected Server getServer(ILoadBalancer loadBalancer) {
return getServer(loadBalancer, null);
}
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
** 5.3.5 BaseLoadBalancer 源码 **** 5.3.6 IRule 接口源码 **
public interface IRule{
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
IRule 接口定义了3个方法,分别是:choose、 setLoadBalancer和 getLoadBalancer
-
choose() 是用来选择实例的 -
setLoadBalancer()用来设置负载均衡规则 -
getLoadBalancer()获取负载均衡规则
实现IRule有个类,分别定义不同的负载均衡规则:
-
随机策略 RandomRule -
轮询策略 RoundRobinRule -
重试策略 RetryRule -
可用过滤策略 PredicateBaseRule -
响应时间权重策略 WeightedRespinseTimeRule -
并发量最小可用策略 BestAvailableRule -
区域权重策略 ZoneAvoidanceRule
** 5.3.7 ILoadBalancer 源码 **
ILoadBalancer 是一个接口,该接口定义了一系列实现负载均衡的方法,LoadBalancerClient 的实现类 RibbonLoadBalanceClient 也将负载均衡的具体实现交给了 ILoadBalancer 来处理,ILoadBalancer 通过配置 IRule、IPing 等,向 EurekaClient 获取注册列表信息,默认每10秒向 EurekaClient 发送一次 “ping”,进而检查是否需要更新服务的注册列表信息。最后,在得到服务注册列表信息后,ILoadBalancer 根据 IRule 的策略进行负载均衡。
查看 BaseLoadBalancer 和 DynamicServerListLoadBalancer 源码,默认情况下实现了以下配置:
-
IClientConfig clientConfig:用于配置负载均衡客户端,默认实现类是 DefaultClientConfigImpl。 -
IRule rule:用于配置负载均衡的策略,默认使用的是 RoundRobinRule 轮询策略。 -
IPing ping:用于检查当前服务是否有响应,从而判断当前服务是否可用,默认实现类是 DummyPing,该实现类的 isAlive() 方法返回值是 true,默认所有服务实例都是可用的。 -
ServerList serverList:用于获取所有 Server 注册列表信息。通过跟踪源码会发现,ServerList 的实现类是 DiscoveryEnabledNIWSServerList,该类定义的 obtainServersViaDiscovery() 方法是根据 eurekaClientProvider.get() 方法获取 EurekaClient,再根据 EurekaClient 获取服务注册列表信息。EurekaClient 的实现类是DiscoveryClient,DiscoveryClient 具有服务注册、获取服务注册列表等功能。 -
ServerListFilter filter:定义了根据配置过滤或者动态获取符合条件的服务列表,默认实现类是 ZonePreferenceServerListFilter,该策略能够优先过滤出与请求调用方处于同区域的服务实例。
六、Ribbon的配置参数
控制参数说明默认值 <service-name></service-name>
.ribbon.NFLoadBalancerPingIntervalPing定时任务周期30s service-name>
.ribbon.NFLoadBalancerMaxTotalPingTimePing超时时间2s <service-name></service-name>
.ribbon.NFLoadBalancerRuleClassNameIRule实现类RoundRobinRule,基于轮询调度算法规则选择服务实例 <service-name></service-name>
.ribbon.NFLoadBalancerPingClassNameIPing实现类DummyPing,直接返回true <service-name></service-name>
.ribbon.NFLoadBalancerClassName负载均衡器实现类2s <service-name></service-name>
.ribbon.NIWSServerListClassNameServerList实现类ConfigurationBasedServerList,基于配置的服务列表 <service-name></service-name>
.ribbon.ServerListUpdaterClassName服务列表更新类PollingServerListUpdater name>
.ribbon.NIWSServerListFilterClassName服务实例过滤器2s <service-name></service-name>
.ribbon.NIWSServerListFilterClassName服务实例过滤器2s <service-name></service-name>
.ribbon.ServerListRefreshInterval服务列表刷新频率2s <service-name></service-name>
.ribbon.NFLoadBalancerClassName自定义负载均衡器实现类2s
七、总结
Ribbon是Spring cloud的核心,负载微服务内负载调用;Ribbon可以脱离Spring Cloud的单独使用。
Ribbon是微服务整个微服务组件最复杂的一环,控制流程上为保证服务的高可用性,有比较多的细节参数控制,在使用的过程中需要深入理清每个环节的处理机制,使之发挥稳定且高效的作用。
原文始发于微信公众号(夏壹分享):Spring Cloud系列 Ribbon详解与实战
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/154822.html