上一篇已经实现了springcloud注册中心、生产者和基本的消费者;我们会不会有这样一个疑问?如果生产者是个集群,那么我们消费者如何去获取哪一个地址作为本次的消费?这就涉及到了负载均衡:
我们先看一下我们的程序:
@Autowired LoadBalancerClient loadBalancerClient; @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "fallback") public String consumer() { ServiceInstance serviceInstance = loadBalancerClient.choose("eureka-client"); String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/hello"; return restTemplate.getForObject(url, String.class); }
这里我们看到我们写的代码是从loadBalancerClient.choose("eureka-client");获取的一个服务实例,那么如果是集群环境下,它是如何选择出使用哪一个实例的呢?
我们来看一下实现类RibbonLoadBalancerClient里面choose方法是怎么实现的?这里我们传入了serviceId,那我们来看一下这个方法:
@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 ILoadBalancer getLoadBalancer(String serviceId) { return this.clientFactory.getLoadBalancer(serviceId); }
然后我们看到在获取server的方法中,我们通过getLoadBalancer(serviceId)方法中的clientFactory获取了ILoadBalancer的一个实例;那ILoadBalancer接口是什么呢?它是一个负载均衡器,它的实现类默认是:com.netflix.loadbalancer.BaseLoadBalancer类;
接下来我们获取了ILoadBalancer对象,那我们如何选择出我们想要的服务实例呢?我们接着看它是如何处理的:protected Server getServer(ILoadBalancer loadBalancer) { if (loadBalancer == null) { return null; } return loadBalancer.chooseServer("default"); // TODO: better handling of key }
它调用了ILoadBalancer的chooseServer方法,传递的值是写死的default值;我们去BaseLoadBalancer类看一下它的实现:
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; } } }
这里我们直接关注这个获取方法rule.choose(key);这里的实现类是com.netflix.loadbalancer.PredicateBasedRule
@Override public Server choose(Object key) { ILoadBalancer lb = getLoadBalancer(); Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key); if (server.isPresent()) { return server.get(); } else { return null; } }
这里的getLoadBalancer();是用com.netflix.loadbalancer.AbstractLoadBalancerRule抽象类的方法的,返回一个ILoadBalancer对象;然后通过它里面的getAllServers()获取所有服务;接下来我们重点看一下chooseRoundRobinAfterFiltering(lb.getAllServers(), key)方法:
public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) { List<Server> eligible = getEligibleServers(servers, loadBalancerKey); if (eligible.size() == 0) { return Optional.absent(); } return Optional.of(eligible.get(nextIndex.getAndIncrement() % eligible.size())); }
我们接着看一下getEligibleServers(servers, loadBalancerKey);这个方法就是确定可用的server,方法的实现:
public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) { if (loadBalancerKey == null) { return ImmutableList.copyOf(Iterables.filter(servers, this.getServerOnlyPredicate())); } else { List<Server> results = Lists.newArrayList(); for (Server server: servers) { if (this.apply(new PredicateKey(loadBalancerKey, server))) { results.add(server); } } return results; } }
if (loadBalancerKey == null)这里判断了是否为空,我们前面说了,这里是写死的,就是default这个值;肯定不为空,那么久走else了;这里首先用Lists类的newArrayList()方法创建了一个集合,这里用这个类是因为他是线性安全的;其实这一步就是确定了可用的server有哪些;怎么判断的就不看了,很繁琐;
那到底怎么选的呢?看了这么多,终于到了他如何实现的了:
return Optional.of(eligible.get(nextIndex.getAndIncrement() % eligible.size()));就这个方法:
这里的getAndIncrement()就相当于i++;不过效率比i++底;还有个incrementAndGet()他相当于++i;
在这里使用getAndIncrement()是为了线性安全考虑的;
这句话差不多的意思就是访问这个方法的次数与可用服务器取模所得的第几个server作为这次请求的server;