【spring cloud hoxton】Ribbon 真的能被 spring-cloud-loadbalancer 替代吗
Ribbon简介
官网地址:https://github.com/Netflix/ribbon
Ribbon进入维护状态了。 所以不建议使用了,替代方案是:Spring Cloud loadBalancer
在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于Http Restful
的。SpringCloud有两种服务调用方式,一种是Ribbon+RestTemplate
,另一种是Feign
。
Ribbon 是一个负载均衡客户端,可以很好的控制HTTP和TCP的一些行为。
Feign 默认集成了Ribbon。
Eureka Client 默认也集成了 Ribbon。
架构:
总结: Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中一个实例。
负载均衡
快速入门
创建一个model工程作为服务消费者,即eureka-ribbon-client
。
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 该依赖是可以省略的,因为spring-cloud-starter-netflix-eureka-client中已经包含了spring-cloud-starter-netflix-ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
向程序的IOC注入一个Bean:RestTemplate
;并通过@LoadBalanced
注解表明这个RestTemplate
开启负载均衡的功能。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
public class EurekaRibbonClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaRibbonClientApplication.class, args);
}
@Bean
// 开启负载均衡功能
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
配置文件appication.yml
server:
port: 8082
spring:
application:
name: eureka-ribbon-client
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8100/eureka/
编写控制器,通过之前注入到IOC容器的RestTemplate
来消费服务注册中心的服务,在这里直接用服务名
替代了具体的URL
地址,在Ribbon
中它会根据服务名来选择具体的服务实例,根据服务实例在请求的时候会用具体的URL
替换掉服务名。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class DemoController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/ribbon")
public String demo() {
return restTemplate.getForObject("http://eureka-client/hello", String.class);
}
}
启动测试
将同一个将要被消费的服务注册多个实例到注册中心。
分别启动注册中心,被消费的服务和当前的Ribbon服务。
在浏览器上多次访问http://127.0.0.1:8082/ribbon,浏览器交替显示:
hello port:8091
hello port:8081
Ribbon源码分析
Ribbon 的 负载均衡主要是通过 LoadBalancerClient
来实现的。
根据IRule
的实现类策略进行负载均衡,默认是:RoundRobinRule
即轮询。
设置访问某个服务的具体策略:
serviceId.ribbon.NFLoadBalancerRuleClassName=自定义的负载均衡策略类
# eureka-client是服务ID
eureka-client:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
负载均衡策略:
接口:com.netflix.loadbalancer.IRule
,其实现类:
RoundRobinRule:轮询
RandomRule:随机
在 RestTemplate 加上@LoadBalance 注解后,RestTemplate 对象添加了拦
截器org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor
。在拦截器的方法中 ,将远程调度方法交给了 Ribbon 的 负载均衡器org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
去处理,然后通过serverId查找服务地址,通过一定的负载均衡策略去做真正的请求,从而达到了负载均衡的目的。
拦截器断点:
Ribbon负载均衡客户端断点:
LoadBalancerClient 实现负载均衡的简单原理
LoadBalancerClient
是SpringCloud
提供的负载均衡器客户端抽象接口
LoadBalancerClient
先从提供的服务中获取某一个实例(默认策略为轮询),比如订单服务需要访问商品服务,商品服务有3个节点,LoadBalancerClient会通过choose(),方法获取到3个节点中的一个服务,拿到服务的信息之后取出服务ip信息,就可以得到完成的想要访问的ip地址和接口,最后通过RestTempate访问商品服务。
SpringCloud
提供的组件Ribbon
,内部实现了LoadBalancerClient
接口,实现类RibbonLoadBalancerClient
,通过@LoadBalance
注解开启负载均衡器。
Spring Cloud loadBalancer
内部的实现类是:BlockingLoadBalancerClient
。
解析LoadBalancerClient接口源码:
LoadBalancerClient
接口继承了ServiceInstanceChooser
接口,主要的方法为2个execute方法,均是用来执行请求的,还有个reconstructURI()是用来重构URL的。
进入ServiceInstanceChooser
接口中,可以看到主要的方法为choose()
,用来根据服务的名称,获取来选择其中一个服务实例,也就是根据seriverId
获取ServiceInstance
。
解析LoadBalancerClient接口实现类RibbonLoadBalancerClient:
RibbonLoadBalancerClient
实现了服务的负载和选择,以及负载策略的配置,整个的负载均衡都由它实现。
- choose(),用来选择具体的服务实例。
- getServer(),获取实例。
- 最终通过
ILoadBalancer
去做服务选择实例。
进入ILoadBalancer
实现类BaseLoadBalancer
看下:
从上面我们看到ILoadBalancer
中的chooseServer
方法里面默认值为:default
。
可以看到这个rule
是IRule
接口声明出来的,且默认定义的实现类是RoundRobinRule
,也就是轮询策略。
IRule
接口定义了3个方法,choose是用来选择实例的,setLoadBalancer和getLoadBalance用来设置和获取ILoadBalancer的。
下面是IRule
接口的所有实现类:
通过IRule接口实现来设置负载策略:
总结:
1、LoadBalancerClient——>RibbonLoadBalancerClient中的choose()
首先需要传一个serverId,也就是服务id,通过LoadBalancerClient接口调用实现类RibbonLoadBalancerClient中的 choose(serverid) 方法。
2、choose()---->getServer(ILoadBalancer loadbalance)----->chooseServer()
choose(serverid)内部调用getServer(ILoadBalancer loadBalancer, Object hint),getServer方法通过ILoadBalancer类进行了负载策均衡之旅。
3、实现chooseServer()方法
接着通过实现ILoaddBalancer
接口的chooseServer(Object key)方法,实现类为BaseLoadBalancer
。
4、过IRule接口,自定义负载策略。
其实LoadBalancerClient 还有个核心类IPing
,这里就不多解析了,向EurekaClient获取注册列表信息,默认每10秒向EurekaClient发送一次ping
,进而检查是否需要更新服务的注册列表信息。
负载均衡的实现LoadBalancerClient具体交给了ILoadBalancer
来处理,ILoadBalancer
通过配置IRule、IPing等,向EurekaClient获取注册列表信息,默认每10秒向EurekaClient发送一次ping
,进而检查是否需要更新服务的注册列表信息。最后,在得到服务注册列表信息后,ILoadBalancer根据IRule的策略进行负载均衡。