追踪源码自定义负载均衡策略

我们来了解一下负载均衡的源码,是怎么实现的,它是怎么实现的,开始我们说了第三种方式只是用了一个注解,

实质上是和第二种方式是一模一样的,那么为了方便观察呢,这里使用第二种方式,来查看源码

ServiceInstance serviceInstance = this.loadBalancerClient.choose("PRODUCT");

这里调用了choose这种方法,我们点进来可以看到他的一个接口

/**
 * Implemented by classes which use a load balancer to choose a server to
 * send a request to.
 *
 * @author Ryan Baxter
 */
public interface ServiceInstanceChooser {

    /**
     * Choose a ServiceInstance from the LoadBalancer for the specified service
     * @param serviceId the service id to look up the LoadBalancer
     * @return a ServiceInstance that matches the serviceId
     */
    ServiceInstance choose(String serviceId);
}

按进来跳到他的实现,看一下他的实现类

public class RibbonLoadBalancerClient implements LoadBalancerClient {

可以看到他实现了一个接口,LoadBalancerClient,LoadBalancerClient又继承了一个ServiceInstanceChooser

/**
 * Represents a client side load balancer
 * @author Spencer Gibb
 */
public interface LoadBalancerClient extends ServiceInstanceChooser {

注意我们看源码的时候呢,可以这样子来看,比如我已经进入到这个类里面了,你想看他类之间的关系的话,

回到我们要观察的方法

@Override
public ServiceInstance choose(String serviceId) {
	Server server = getServer(serviceId);
	if (server == null) {
		return null;
	}
	return new RibbonServer(serviceId, server, isSecure(server, serviceId),
			serverIntrospector(serviceId).getMetadata(server));
}

我们知道他第一步要把服务列表要给找出来,你看到这个方法

protected Server getServer(String serviceId) {
	return getServer(getLoadBalancer(serviceId));
}

然后又继续往下找

protected Server getServer(ILoadBalancer loadBalancer) {
	if (loadBalancer == null) {
		return null;
	}
	return loadBalancer.chooseServer("default"); // TODO: better handling of key
}

可以看到他这里用了哪个去找,用的是一个ILoadBalancer,看一下这个

/**
 * Interface that defines the operations for a software loadbalancer. A typical
 * loadbalancer minimally need a set of servers to loadbalance for, a method to
 * mark a particular server to be out of rotation and a call that will choose a
 * server from the existing list of server.
 * 
 * @author stonse
 * 
 */
public interface ILoadBalancer {

他用的这个组件去找,这个组件就是属于Ribbo下面的

com.netflix.loadbalancer.ILoadBalancer

所以说他一旦用了负载均衡的技术,都是Ribbon,我们可以看这个方法

protected Server getServer(ILoadBalancer loadBalancer) {
	if (loadBalancer == null) {
		return null;
	}
	return loadBalancer.chooseServer("default"); // TODO: better handling of key
}

这里有几个选择,我们就选择com.netflix.loadbalancer.BaseLoadBalancer.chooseServer(Object)

/*
 * Get the alive server dedicated to key
 * 
 * @return the dedicated server
 */
public Server chooseServer(Object key) {
	if (counter == null) {
		counter = createCounter();
	}
	counter.increment();
	if (rule == null) {
		return null;
	} else {
		try {
			return rule.choose(key);
		} catch (Exception e) {
			logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
			return null;
		}
	}
}

进来之后同样的可以观察这个类之间的关系,这个就比上一个复杂一些了

public class BaseLoadBalancer extends AbstractLoadBalancer

public abstract class AbstractLoadBalancer implements ILoadBalancer {

看一下ILoadBalancer接口,这里有一个getServerList

/**
 * @deprecated 2016-01-20 This method is deprecated in favor of the
 * cleaner {@link #getReachableServers} (equivalent to availableOnly=true)
 * and {@link #getAllServers} API (equivalent to availableOnly=false).
 *
 * Get the current list of servers.
 *
 * @param availableOnly if true, only live and available servers should be returned
 */
@Deprecated
public List getServerList(boolean availableOnly);

这个已经标记废弃了,getAllServers这个还在用

/**
 * @return All known servers, both reachable and unreachable.
 */
public List getAllServers();

所以我们断定获取所有服务列表,肯定是在这个方法

com.netflix.loadbalancer.BaseLoadBalancer.getAllServers()

@Override
public List getAllServers() {
	return Collections.unmodifiableList(allServerList);
}

这里我们要获取列表,所以我们启动两个实例,由于服务刚刚启动完成,还没来得及获取服务列表,

你就来访问就来刷新,所以会遇到这个问题,你看这个日志刚刚报了个错之后,DiscoveryClient打印出的

日志,他去获取所有已经注册了的服务,这个时候才去获取,这个时候我们再来访问,此时能够获取服务列表了,

就是把这个list作为不能再修改的List

localhost:8081/getProductMsg

http://10.40.8.144:8080/msg

这是第一步获取服务列表,我们再来看一下他的负载均衡策略,

/*
 * Get the alive server dedicated to key
 * 
 * @return the dedicated server
 */
public Server chooseServer(Object key) {
	if (counter == null) {
		counter = createCounter();
	}
	counter.increment();
	if (rule == null) {
		return null;
	} else {
		try {
			return rule.choose(key);
		} catch (Exception e) {
			logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
			return null;
		}
	}
}

我们关心的是规则了

protected IRule rule = DEFAULT_RULE;

有个默认的规则

private final static IRule DEFAULT_RULE = new RoundRobinRule();

默认的规则是什么,通过名字也能够看出来吧,就是轮询的方式,那我们可以来测试一下,看到底是不是轮询,把Product

两个都给启动了,为了以示区别

@RestController
public class ClientController {
	
//	@Autowired
//	private RestTemplate restTemplate;
	
	@Autowired
	private LoadBalancerClient loadBalancerClient;

	@GetMapping("/getProductMsg")
	public String getProductMsg() {
		// 1.第一种方式(直接使用RestTemplate,url写死)
		RestTemplate restTemplate = new RestTemplate();
//		String response = restTemplate.getForObject("http://localhost:8080/msg", String.class);
//		System.out.println(response);
		
		// 第二种方式(利用loadBalancerClient通过应用名获取url,然后再使用restTemplate 注意这里不能用第三种的restTemplate)
		ServiceInstance serviceInstance = this.loadBalancerClient.choose("product");
		String url = String.format("http://%s:%s", serviceInstance.getHost(),serviceInstance.getPort()+"/msg");
		System.out.println(url);
		return restTemplate.getForObject(url, String.class);
		
		// 3.第三种方式(利用@LoadBalanced,可以在restTemplate里使用应用名字)
//		String response = restTemplate.getForObject("http://PRODUCT/msg", String.class);
//		System.out.println(response);		
//		return response;
	}
}

一个是8080,一个是9080

localhost:8080/msg

localhost:9080/msg

可以看到他时轮询出现的,这里有三个负载均衡类,他用的到底是哪一个呢,我们可以重启一下,日志里面也会打印出来,

public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
									 ServerList serverList, ServerListFilter filter,
									 ServerListUpdater serverListUpdater) {
	super(clientConfig, rule, ping);
	this.serverListImpl = serverList;
	this.filter = filter;
	this.serverListUpdater = serverListUpdater;
	if (filter instanceof AbstractServerListFilter) {
		((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
	}
	restOfInit(clientConfig);
}

就是我们刚刚说到的轮询,那假如我们想改变负载均衡的规则,其实一般情况下我们不需要改变,你基本上用这个

轮询就可以了,假如需要自己定义的时候,直接在配置里面加一个配置就行了,这个配置很长,不建议大家去记,

https://cloud.spring.io/spring-cloud-static/Finchley.SR4/multi/multi_spring-cloud.html

我们搜索Ribbon关键字,看目录里面,最好在目录里面搜到,我们直接通过配置文件就可以配置了

https://cloud.spring.io/spring-cloud-static/Finchley.SR4/multi/multi_spring-cloud-ribbon.html#
_customizing_the_default_for_all_ribbon_clients

16.4 Customizing the Ribbon Client by Setting Properties

users:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
	
这个就是表示我们要配置的ClassName,

order.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RoundRobinRule

ZoneAvoidanceRule

 

你可能感兴趣的:(追踪源码自定义负载均衡策略)