浅谈Springcloud-Ribbon负载均衡原理及源码

前言:Eureka的服务发现是基于服务名称获取服务列表,然后在对服务列表做负载均衡。那么,这其中负载均衡的过程原理又是什么呢?

在使用RestTemplate发起请求需要在RestTemplate上添加@LoadBalanced注解,这个注解发起的请求会被Ribbon的拦截器给拦截和处理。这个拦截器为LoadBalancerInterceptor,其实现了ClientHttpRequestInterceptor。

LoadBalancerInterceptor源码如下:

package org.springframework.cloud.client.loadbalancer;

import ...

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

	//ClientHttpRequestInterceptor的实现方法
    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));
    }
}

而这个ClientHttpRequestInterceptor接口它会拦截客户端HTTP请求。而RestTemplate正好是个发送HTTP请求的客户端,所以就会被ClientHttpRequestInterceptor拦截。注释翻译过来大概意思就是:(可以使用RestTemplate注册此接口的实现,以修改传出的ClientHttpRequest和/或传入的ClientHttPreponse。)

ClientHttpRequestInterceptor源码如下:

package org.springframework.http.client;

import ...
/**
Intercepts client-side HTTP requests. 
Implementations of this interface can be registered with the RestTemplate,
as to modify the outgoing ClientHttpRequest and/or the incoming ClientHttpResponse.
*/
@FunctionalInterface
public interface ClientHttpRequestInterceptor {
    ClientHttpResponse intercept(HttpRequest var1, byte[] var2, ClientHttpRequestExecution var3) throws IOException;
}

ClientHttpRequestInterceptor是个接口,接口里面定义了一个方法叫interceptor。上文提到过LoadBalancerInterceptor实现了ClientHttpRequestInterceptor,LoadBalancerInterceptor那么一定会实现interceptor方法。

所以当Ribbon收到HTTP请求后就会被LoadBalancerInterceptor的interceptor方法给拦截。

interceptor方法拦截后会获取请求地址→根据刚才获取的地址获取服务名称→判断服务名称是否为空,如果为空则抛出异常;不为空则执行并返回excute方法。

就是怎么个玩意儿↓

	//ClientHttpRequestInterceptor的实现方法
    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);//判断服务名称是否为空,如果为空则抛出异常
        //不为空则执行并返回excute方法
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }

而这个excute方法为RibbonLoadBalancerClient的方法,执行这个方法会获得一个LoadBalancer对象,这个对象的名字叫DynamicServerListLoadBalancer(动态服务列表负载均衡器)它里边有个allServerList来保存目前拉取到的服务列表(根据服务名称找Eureka获取服务列表)然后根据getServer方法选择一个服务。

package org.springframework.cloud.netflix.ribbon;
import...

public class RibbonLoadBalancerClient implements LoadBalancerClient {
	//这上头还有若干个方法
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);//根据服务名称找Eureka获取服务列表
        Server server = this.getServer(loadBalancer, hint);//根据getServer方法选择一个服务
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
        }
    }
    //下边也是
}

这个getServer也是RibbonLoadBalancerClient的方法,其会执行并返回loadBalancer.chooseServer方法。

public class RibbonLoadBalancerClient implements LoadBalancerClient {
	//这上头还有若干个方法
	protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
        return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
    }
    //下边也是
}

而这个chooseServer方法为zoneAwareLoadBalancer的方法,它调用了super的chooseServer方法。而BaseLoadBalancer的chooseServer方法调用并返回rule的choose方法选择一个负载均衡算法来挑选服务。默认算法为ZoneAvoidanceRule,即服务消费者会优先选择和自己同一个区域的服务,然后在对这些服务进行轮询。

public class BaseLoadBalancer extends AbstractLoadBalancer implements PrimeConnectionListener, IClientConfigAware {
	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;
	            }
	        }
	    }
}

rule是个IRule类型的对象,IRule顾名思义是个接口,他有很多的实现类。
在这里插入图片描述
常见规则详解:
在这里插入图片描述

最后,在选择一个算法后通过层层返回,回到RibbonLoadBalancerClient,用拿到服务地址替换原来的服务名称地址。如原来的localhost://userservice/user/1 转换为 localhost://8001/userservice/1
过程图例
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值