您的位置:首页 > 编程语言 > Java开发

springcloud ribbon 拦截resttemplate到实际请求调用源码讲解

2018-08-03 12:47 801 查看

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;

}

}

 

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: