微服务模块-Ribbon及负载均衡

Ribbon简介

Ribbon是Netflix发布的云中间层服务开源项目,其主要功能是提供客户端实现负载均衡算法。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,我们可以在配置文件中Load Balancer后面的所有机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。

ribbon原理

请求会先被LoadBalancerInterceptor(拦截器拦截),获取服务,将服务再委托给LoadBalancerClient,LoadBalancerClient在初始化的时候,会将服务委托给ILoadBalance(负载均衡器),ILoadBalance根据服务信息向Eureka注册中心获取该服务注册列表,并且每10s一次向EurekaClient发送“ping”,来判断服务的可用性,如果服务的可用性发生了改变或者服务数量和之前的不一致,则从注册中心更新或者重新拉取,再根据具体的IRule来进行负载均衡,来获取调用的服务,再将请求进行封装,最后在将请求发送到指定服务。

源码分析

1.RibbonAutoConfiguration

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
		name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
		AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
		ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {

	@Autowired(required = false)
	private List<RibbonClientSpecification> configurations = new ArrayList<>();

	@Autowired
	private RibbonEagerLoadProperties ribbonEagerLoadProperties;

	@Bean
	public HasFeatures ribbonFeature() {
		return HasFeatures.namedFeature("Ribbon", Ribbon.class);
	}

	@Bean
	public SpringClientFactory springClientFactory() {
		SpringClientFactory factory = new SpringClientFactory();
		factory.setConfigurations(this.configurations);
		return factory;
	}

	@Bean
	@ConditionalOnMissingBean(LoadBalancerClient.class)
	public LoadBalancerClient loadBalancerClient() {
		return new RibbonLoadBalancerClient(springClientFactory());
	}
	}

通过源码分析,启动类启动时会注入LoadBalanceClient(负载均衡客户端)

2.LoadBalancerAutoConfiguration

   @Bean
   public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
       return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
   }

通过源码分析,启动类启动时会注入LoadBalancerInterceptor (拦截器)

3.LoadBalancerInterceptor 工作原理

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }


      //拦截请求
    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        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));
    }
}

将服务委托给LoadBalancerClient.execute()

loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));

RibbonLoadBalancerClient 实现LoadBalancerClient

扫描二维码关注公众号,回复: 12926414 查看本文章
public class RibbonLoadBalancerClient implements LoadBalancerClient
@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
			throws IOException {
		return execute(serviceId, request, null);
	}

RibbonLoadBalancerClient将服务信息委托给ILoadBalancer

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		Server server = getServer(loadBalancer, hint);
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		RibbonServer ribbonServer = new RibbonServer(serviceId, server,
				isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));

		return execute(serviceId, ribbonServer, request);
	}

ILoadBalancer 接口

public interface ILoadBalancer {
    void addServers(List<Server> var1);

    //返回一个指定的服务
    Server chooseServer(Object var1);

    void markServerDown(Server var1);

    /** @deprecated */
    @Deprecated
    List<Server> getServerList(boolean var1);
   
    List<Server> getReachableServers();

    List<Server> getAllServers();
}

最终获取指定的服务是通过IRule的choose方法(通过指定的负载均衡策略)
this.rule.choose(key)

public Server chooseServer(Object key) {
        if (this.counter == null) {
            this.counter = this.createCounter();
        }
        this.counter.increment();
        if (this.rule == null) {
            return null;
        } else {
            try {
                return this.rule.choose(key);
            } catch (Exception var3) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", new Object[]{this.name, key, var3});
                return null;
            }
        }
    }

IRule 接口

public interface IRule {
    Server choose(Object var1);

    void setLoadBalancer(ILoadBalancer var1);

    ILoadBalancer getLoadBalancer();
}

IRule 实现类(负载均衡策略)
在这里插入图片描述

负载均衡策略

随机策略RandomRule

在这里插入图片描述
核心代码:
int index = rand.nextInt(serverCount); // 使用jdk内部的Random类随机获取索引值index
server = upList.get(index); // 得到服务器实例

RoundRobinRule轮询策略

表示每次都取下一个服务器,如果失败会继续向下尝试获取10次

在这里插入图片描述
在这里插入图片描述

WeightedResponseTimeRule加权策略

在这里插入图片描述
开始的时候还没有权重列表,采用父类的轮询方式,有一个默认每30秒更新一次权重列表的定时任务,该定时任务会根据实例的响应时间来更新权重列表,choose方法做的事情就是,用一个(0,1)的随机double数乘以最大的权重得到randomWeight,然后遍历权重列表,找出第一个比randomWeight大的实例下标,然后返回该实例

BestAvailableRule策略用来选取最少并发量请求的服务器

在这里插入图片描述

ribbon结合负载均衡使用

上面介绍了ribbon和几种负载均衡策略底层源码实现,下面来讲解ribbon结果负载均衡的使用

创建具有负载均衡功能的RestTemplate实例

添加@LoadBalanced注解

@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}

使用RestTemplate进行rest操作的时候,会自动使用负载均衡策略,它内部会在RestTemplate中加入LoadBalancerInterceptor这个拦截器,这个拦截器的作用就是使用负载均衡。

可以通过下面方法获取负载均衡策略最终选择了哪个服务实例

@Autowired
LoadBalancerClient loadBalancerClient;

//从该服务中获取url列表
ServiceInstance serviceInstance = loadBalancerClient.choose(“spring-cloud-product”);
String url=String.format(“http://%s:%s”,serviceInstance.getHost(),serviceInstance.getPort()+"/way");
return restTemplate.getForObject(url,String.class);

自定义ribbon负载均衡策略

第一种方式:使用已经提供的

  1. 设置单一服务的负载均衡策略(单一服务有效)
    修改配置文件
# HELLO-SERVICE是服务应用名 
HELLO-SERVICE.ribbon.NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

2.修改默认的负载均衡策略(默认轮询)(所有服务有效)

// 定义负载均衡策略
@Bean 
public IRule ribbonRule() { 
   return new RandomRule(); 
}

第二种方式:使用自定义的

第一步:继承AbstractLoadBalancerRule类

第二步:实现方法

public class GPDefined extends AbstractLoadBalancerRule {


    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {


    }


    public Server choose(ILoadBalancer lb,Object o) {
        if (lb == null) {
            return null;
        } else {
            Server server = null;
            while(server == null) {
                if (Thread.interrupted()) {
                    return null;
                }
                //获取存活的实例
                List<Server> upList = lb.getReachableServers();
                //获取所有实例
                List<Server> allList = lb.getAllServers();
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }
                //int index = this.chooseRandomInt(serverCount);
                server = (Server)upList.get(index);
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }
                    server = null;
                    Thread.yield();
                }
            }
            return server;
        }
    }


    @Override
    public Server choose(Object o) {
        return choose(this.getLoadBalancer(),o);
    }
}

第三步:
注入类实例
@Bean
public IRule ribbonRule() {
return new 自定义的类();//自定义负载策略类
}

猜你喜欢

转载自blog.csdn.net/weixin_42371621/article/details/109095891