spring cloud ribbon是一个基于HTTP 和 TCP 的客户端负载均衡工具,它基于Netflix Ribbon实现。通过spring cloud的封装,可以让我们轻松地将面向服务的REST模板请求自动转换成客户端负载均衡的服务调用。
在客户端负载均衡中,所有客户端节点都维护者自己要访问的服务端地址清单
在Ribbon中自带
随机规则:RandomRule
最可用规则: BestAvailableRule
轮询规则:RoundRobinRule
重试规则:RetryRule
客户端配置:ClientConfigEnabledRoundRobinRule
可用性过滤规则:AvailabilityFilteringRule
RT权重规则:WeightedResponseTimeRule
规避区域规则:ZoneAvoidanceRule
通过spring cloud ribbon的封装,我们在微服务架构中使用客户端负载均衡只需要如下两步:
1 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心
2 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用
版本 spring boot 1.5.14
LoadBalancerInterceptor 将一个普通的RestTemplate 变成客户端负载均衡 |
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; //由于我们在使用RestTemplate时采用了服务名作为host,所以直接从HttpRequest的URI对象中通过getHost, 就可以拿到服务名,然后调用execute函数去根据服务名来选择实例并发起实际的请求 @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution)); } } |
RibbonLoadBalancerClient 下的 execute 通过getServer 选取具体的服务器进行调用
request.apply 最后还是委派 spring 下 ClientHttpRequest 其调用过程是 LoadBalancerRequestFactory#apply --> InterceptingClientHttpRequest#execute --> InterceptingClientHttpRequest# org.springframework.http.client.ClientHttpRequest |
@Override public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException { ILoadBalancer loadBalancer = getLoadBalancer(serviceId); Server server = getServer(loadBalancer); RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));
T returnVal = request.apply(ribbonServer); } |
RibbonLoadBalancerClient 下的 getServer(ILoadBalancer loadBalancer ) |
protected Server getServer(ILoadBalancer loadBalancer) { if (loadBalancer == null) { return null; } return loadBalancer.chooseServer("default"); // TODO: better handling of key } |
eureka 负载均衡器
在具体实现负载均衡是,是通过Ribbon 的 ILoadBalancer接口实现的
在默认情况下,ribbon 采用的是轮询的方式
调用链 |
默认: ZoneAwareLoadBalancer#chooseServer 调用父类 ---> BaseLoadBalancer#chooseServer ---> ZoneAvoidanceRule#choose |
ZoneAvoidanceRule#choose 方法,默认采用的是incrementAndGetModulo 即轮询 |
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; } }
private int incrementAndGetModulo(int modulo) { for (;;) { int current = nextIndex.get(); int next = (current + 1) % modulo; if (nextIndex.compareAndSet(current, next) && current < modulo) return current; } } |