Ribbon是客户端的「负载均衡器」,消费者可以通过服务别名调用服务时,需要Ribbon做负载均衡来以「某种机制」访问实际的服务调用地址。
简单类比,我们去找Tony老师,一般理发店都会有多个Tony老师。但是也会有一个类似前台的工作人员为我们安排有空的Tony老师理发。工作人员就是类似Ribbon,是按照顺序安排呢,还是随机安排呢。
同样创建一个Ribbon的「空模块」,然后在Ribbon空模块下创建一个ribbon-consume9101
「子模块」。在父类也就是空模块的pom文件中加入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>
当你引入了Eureka相关的依赖的时候其实就已经把Ribbon的依赖引入进来了,所以如果使用的是Ribbon + Eureka,可以不用写上面的依赖也能运行。
创建完成后整体的「项目结构」如图所示
application.yml
server:
port: 9101
spring:
application:
name: ribbon-consume
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8001/eureka/
instance:
instance-id: ribbon-consume9101
注意我们的服务的提供者是eureka-provide
服务,这个服务名字可能单取provide
更准确点,以后项目重构的时候可能会修改。
涉及到服务与服务之间的调用,一般会选择使用RestTemplate
,同时需要把它注入Spring容器中,所以选择使用「配置类」
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //负载均衡需要的注解
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
接下来就是「主启动类」
@SpringBootApplication
@EnableEurekaClient
@RestController
public class RibbonConsume9101 {
final String PROVIDE_URL = "http://eureka-provide";
RestTemplate restTemplate;
public RibbonConsume9101(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/ribbon/consume")
public String getInfo() {
return "i am consumer, but actually invoke other service: [" + restTemplate.getForObject(PROVIDE_URL + "/eureka/provide", String.class) + "]";
}
public static void main(String[] args) {
SpringApplication.run(RibbonConsume9101.class, args);
}
}
EurekaServer8001
EurekaProvide7001
,EurekaProvide7002
, EurekaProvide7003
RibbonConsume9101
首先看下Eureka注册中心,可以看到3个服务提供者,1个消费者都已经注册到Eureka。
接着消费端访问接口http://localhost:9101/ribbon/consume,看能不能从服务提供端中获取到服务,可以看到确实能够调用3个不同端口的服务提供端,并且是按照一定顺序轮流调用(「轮询,也是默认规则」)。
上述调用服务的时候明显是轮询的方式,那如果想要其它方式去调用呢,这时候就需要自定义配置类。
Ribbon主要有6个组件
ServerList:
定义获取服务器列表ServerListFilter:
对ServerList列表进行二次过滤ServerListUpdater:
定义服务更新策略Iping:
检查服务列表是否存活IRlue:
根据算法选择调用服务列表中的某一个服务ILoadBalancer:
软件负载均衡器入口,整合以上所有的组件实现负载功能@Configuration
public class RibbonCustomConfig {
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
}
IRlue
接口就是以什么样的「规则」去「调用服务提供者」,可以看下该接口的实现类
怎么让这个配置文件被Ribbon感知到呢,就需要利用@RibbonClient
注解。建立一个空的注解类,加上注解和配置类就能够自定义Ribbon配置。
@Configuration
@RibbonClient(name = "eureka-provide", configuration = RibbonCustomConfig.class)
public class RibbonConfig {
}
这里的名字就是我们「需要调用的服务端的配置文件中的springcloud.application.name
的值」。需要注意的是,在官方文档里面有这样一段话
在这个例子中翻译过来就是RibbonCustomConfig
如果和主启动类在同一个包下,就会被扫描进Spring中,这样会导致配置文件会被所有的@RibbonClients
所共享。当然也可以用@ComponentScan
把配置文件排除在外。
重启RibbonConsume9101
服务,其它的不用动,同样调用消费端访问接口http://localhost:9101/ribbon/consume
点的时候,啥,怎么一直调用的是7001端口,难道其它服务挂掉了,可以看到后面明显加快了点的速度,最后还好没有翻车,确实是改成了随机规则。
除了通过配置类来自定义Ribbon外,还可以通过配置文件来自定义
这里clientName
同样是「需要调用的服务端的配置文件中的springcloud.application.name
的值。」 如果是完全自己写的类呢,需要实现对应的接口,这里同样采用Netflix写好的RandomRule
类。
把RibbonCustomConfig
和RibbonConfig
删掉或者全部注释掉都可以,修改配置文件
server:
port: 9101
spring:
application:
name: ribbon-consume
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8001/eureka/
instance:
instance-id: ribbon-consume9101
#其实就是加了下面的内容
eureka-provide:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
其中eureka-provide
就是clientName。接着一样重启RibbonConsume9101
服务,其它的不用动,同样调用消费端访问接口http://localhost:9101/ribbon/consume
值得注意的是,在配置文件中配置的变量是要比配置类中的优先级要高的。
上面是消费者端和服务端都注册进了Eureka,相当于消费者通过Eureka去找到了其它服务提供者的服务。那在真正业务中接受了新的消费者端,并没有注册进Eureka,怎么解决这个问题呢?
先来看看没有注册进去会发生什么情况,想都不用想肯定是直接报错了
同样在Ribbon父模块下面建立一个子模块
因为「不用注册」进Eureka,所以配置文件也要做相应的修改
server:
port: 9102
spring:
application:
name: ribbon-consume-without-eureka
#禁用掉Eureka,其实禁用不禁用都不影响,因为根本就没导入
ribbon:
eureka:
enabled: false
同样需要导入RestTemplate
所以可以直接「复制」上一个子模块
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
主启动类也可以直接「复制」,不需要用到Eureka,去掉@EnableEurekaClient
注解并修改类名即可
@SpringBootApplication
@RestController
public class RibbonConsumeWithoutEureka9102 {
final String PROVIDE_URL = "http://eureka-provide";
RestTemplate restTemplate;
public RibbonConsumeWithoutEureka9102(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/ribbon/consume")
public String getInfo() {
return "i am consumer, but actually invoke other service: [" + restTemplate.getForObject(PROVIDE_URL + "/eureka/provide", String.class) + "]";
}
public static void main(String[] args) {
SpringApplication.run(RibbonConsumeWithoutEureka9102.class, args);
}
}
那怎样消费者调用的时候怎么知道去哪找提供者的服务呢,就需要动配置文件了
server:
port: 9102
spring:
application:
name: ribbon-consume-without-eureka
ribbon:
eureka:
enabled: false
#以下为增加内容
eureka-provide:
ribbon:
listOfServers: localhost:7001, localhost:7002, localhost:7003
其中eureka-provide
就是「需要调用的服务端的配置文件中的springcloud.application.name
的值」
开启RibbonConsume9102
服务,其它的不用动,调用消费端访问接口http://localhost:9102/ribbon/consume ,可以看到也是能够按照「默认轮询」的方式调用服务。