Spring Cloud Ribbon
为了实现系统的高可用、缓解网络压力等,往往要考虑使用集群,而谈到集群,就不得不考虑负载均衡的问题。
负载均衡分为 客户端负载均衡 和 服务端负载均衡。
负载均衡器:
维护服务端清单,通过心跳监测来剔除有故障的服务端节点,确保清单中的都是可以正常访问的服务端节点。
当客户端发送请求到负载均衡设备时,该设备按照某种算法(轮询、加权、随机等)从清单中取出一台服务端地址,然后进行转发。
而客户端负载均衡和服务端负载均衡的最大不同点在于 服务清单所存储的位置。
客户端所持有的服务清单来源于服务注册中心,并且需要自己去维护清单的可用性(需要与注册中心配合)。
通常我们所说的负载均衡都指的是服务端负载均衡,不过这次所介绍的,则是如何使用 Ribbon 来实现客户端的负载均衡。
那为什么Spring Cloud 采用了客户端负载均衡,而不是我们常用的服务端负载均衡呢?
客户端负载均衡的优点:对于服务端负载均衡,分为两种,即硬件和软件,硬件类如常见的F5,软件类如Nginx,通常还需再购买一个Nginx服务器,不论是哪一种,都需要额外支出一笔开销。客户端负载均衡可以省去这一部分开销,因为它是在自身处设定了一个调度算法,在向服务器发起请求的时候,利用算法确定向哪台服务器发起请求,随后发起实际请求。这些都是客户端内部程序实现,因此不需要额外的负载均衡器软硬件投入。
步入正题
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具类框架,不需要像Spring Cloud Eureka 那样独立部署。
作用:微服务间的调用、API网关的请求转发等,通过使用ribbon,可以轻松实现客户端负载均衡。
使用Spring Cloud Ribbon,我们在微服务中使用客户端负载均衡非常的简单:
<1> 启动多个服务实例,并注册到服务注册中心。
创建 a1_service 和 a2_service ,服务名称均设置成 aService ,端口分别为8180、8181,如下:
#a1_service 的 application.yml
server:
port: 8180 #服务的端口
spring:
application:
name: aService #服务命名, 注意:不能使用下划线等
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka/ #指定服务注册中心的地址
# a2_service 的 application.yml
server:
port: 8181 #服务的端口
spring:
application:
name: aService #服务命名 注意:不能使用下划线等!
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka/ #指定服务注册中心的地址
分别启动 a1_service 和 a2_service ,访问服务注册中心( http://localhost:1111/eureka/ ),两个服务均注册成功:
<2> 调用服务的一方使用被注解 @LoadBalanced 修饰的RestTemplate 来实现向服务接口的调用
创建子服务 ribbon_service,主程序文件 以及 application.yml 内容如下:
@EnableEurekaClient //激活服务发现
@SpringBootApplication
public class RibbonServiceApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonServiceApplication.class, args);
}
@Bean
@LoadBalanced //使用客户端负载均衡
RestTemplate restTemplate() {
return new RestTemplate();
}
}
spring:
application:
name: ribbonService #服务命名, 注意:不能使用下划线等
server:
port: 9000 #服务端口
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka/ #指定注册中心地址
另外 pom.xml 中也要引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
继续在ribbon_service下创建一个HelloControl.java,来请求 aService 服务的接口:
@RestController
public class HelloControl {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/hi")
public void hiAction() {
for (int i = 0; i < 4; i ++) {
String line = restTemplate.getForEntity("http://ASERVICE/hello",
String.class).getBody() + "\t" +
"time: " + System.currentTimeMillis();
System.out.println(line);
}
}
}
浏览器中访问:http://localhost:9000/hi,会发现控制台打印出如下:
Hello , I am from: aService 8181 time: 1541498155051
Hello , I am from: aService 8180 time: 1541498155145
Hello , I am from: aService 8181 time: 1541498155150
Hello , I am from: aService 8180 time: 1541498155155
可以看出,aService 的两个不同端口(8180、8181)的子服务轮流被调用,即客户端负载均衡。