在平时使用Ribbon时,更多的是将Ribbon与RestTemplate相结合:
@Bean @LoadBalanced RestTemplate restTemplate(){ return new RestTemplate(); } 复制代码
首先定义一个RestTemplate,通过注解注入,同时注解也完成了负载均衡。
同时去使用restTemplate进行Rest调用
@Override public String hiService(String name){ return restTemplate.getForObject("http://SERVER-HI/hi?name="+name,String.class); } 复制代码
首先,在进行getForObject方法后,会将带入的url进行封装,封装成http请求request,然后被拦截器LoadBalancerInterceptor进行拦截,代码分别是:
@Override @Nullable public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } 复制代码
以及拦截部分:将request拦截下,截取其中URL及服务名,用于之后调用负载均衡方法时,选择合适的服务实例
@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方法,先查看一下RibbonLoadBalancerClient类:
其中LoadBalancerClient接口,有如下三个方法,其中excute()为执行请求,reconstructURI()用来重构url。
ServiceInstanceChooser接口,主要有一个方法,用来根据serviceId来获取ServiceInstance。
它继承了ServiceInstanceChooser及LoadBalancerClient类,最终的负载均衡的请求处理,由它来执行
首先,去获取需要加载的负载均衡策略,通过getLoadBalancer方法执行,默认是轮询RoundRobbinRule方式:
在获取到负载均衡策略之后,通过getServer()方法去获取实例,点击进入getServer方法,发现是ILoadBalancer类去选择服务实例。
在ILoadBalancer接口中,addServers()方法是添加一个Server集合;chooseServer()方法是根据key去获取Server;markServerDown()方法用来标记某个服务下线;getReachableServers()获取可用的Server集合;getAllServers()获取所有的Server集合。
chooseServer则是由BaseLoadBalancer类进行实现:
根据代码可以看出,具体choose方法是根据不同的负载均衡策略,会有不同的选择方法,返回具体根据策略得到的服务实例。
最后根据服务实例,进行请求的调用。
IRule用于复杂均衡的策略,它有三个方法,其中choose()是根据key 来获取server,setLoadBalancer()和getLoadBalancer()是用来设置和获取ILoadBalancer的
IRule有很多默认的实现类,这些实现类根据不同的算法和逻辑来处理负载均衡。Ribbon实现的IRule有以下几个。在大多数情况下,这些默认的实现类是可以满足需求的,如果有特性的需求,可以自己实现。
那我们就先看看RoundRobbinRule类中的轮询策略:
①首先获取所有存活的服务列表reachableServers及所有服务列表allServers,判断两个list是否为空。 ②incrementAndGetModulo中则是对一个原子性变量进行+1操作,并同时进行一个CAS操作,去修改nextServerIndex值,保证轮询的可靠性。
③最后判断服务是否可用,如果不可用,则重新进入循环。
④如果在10次循环后,仍然没有可用的服务,则退出循环并进行警告。最后返回服务实例
可重试的轮询策略如下:
可见此处多了两行代码:
long requestTime = System.currentTimeMillis(); long deadline = requestTime + maxRetryMillis; 复制代码
定义了500ms的总重试时间,如果服务实例获取不到,则进入循环,在循环中每次需要判断一下是否超过总时间
if判断中,嵌套了一个定时任务,去判断是否达到预定时间,如果达到了,则对当前线程进行interrupt()操作
while循环中,每次都会去获取一下服务实例,然后进行判断,如果实例仍然没有获取到,则对当前线程进行Thread.yield()操作,此操作的意义是: 让出当前线程时间分片,重新争夺时间片,让定时任务去执行,看是否达到规定时间,如到时间,则执行interrupt()操作,退出循环
这即是可重试的策略
Ribbon + RestTemplate 的负载平衡,流程是: 通过注解后,对url进行封装request,拦截器对request进行拦截,然后根据负载均衡器去调用服务实例,完成负载平衡和服务调用