介绍
SpringCloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具。
Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 客户端组件提供一系列完善的配置项如连接超时重试等。简单地说,就是在配置文件中列出 Load Balancer(简称 LB)后面所有的机器,Ribbon 会自动帮助你基于某种规则(如简单轮询、随机连接等)去连接这些机器。我们也很容易使用 Ribbon 实现自定义的负载均衡算法。
LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。负载均衡简单地说就是将用户的请求平均的分配到多个服务上,从而达到系统的 HA。
常见的负载均衡方案有软件 Nginx、LVS,硬件 F5 等。
而 LB 又可以分为以下两种:
-
集中式 LB:在服务的消费方和提供方之间使用独立的 LB 设施(可以是硬件,如 F5,也可以是软件,如 Nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方。
-
进程内 LB:将 LB 逻辑集中到消费方,消费方从注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon 就属于进程内 LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
使用
1、修改消费者服务工程,对应占用端口为 80,添加依赖如下:
<!--ribbon 相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
2、修改配置将消费者服务工程作为 Eureka 客户端注册到 Eureka 集群:
# application.yml
server:
port: 80
eureka:
client:
service-url:
defaultZone: http://www.eurekaserver1.com:7001/eureka,http://www.eurekaserver2.com:7002/eureka,http://www.eurekaserver3.com:7003/eureka
instance:
instance-id: microservicecloud-provider-dept
prefer-ip-address: true # 访问路径显示 IP
spring:
application:
name: microservicecloud-consumer-dept
3、启用 Eureka 客户端功能:
// zze.springcloud.Application_80
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class Application_80 {
public static void main(String[] args) {
SpringApplication.run(Application_80.class, args);
}
}
4、修改配置类,注册 RestTemplate
bean 时添加注解启用负载均衡:
// zze.springcloud.cfgbeans.ConfigBean
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced // 客户端负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
5、修改 Controller,修改微服务具体访问 URL 为微服务名称:
// zze.springcloud.controller.DeptController
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import zze.springcloud.entities.Dept;
import java.util.List;
@RestController
@RequestMapping("/consumer/dept")
public class DeptController {
// 微服务 Provider 的服务地址
// private static String REST_URL_PREFIX = "http://localhost:8001";
// 要调用的微服务名称 即微服务工程的 spring.application.name 对应值
private static String REST_URL_PREFIX = "http://microservicecloud-provider-dept";
@Autowired
private RestTemplate restTemplate;
@PostMapping("/add")
public boolean add(@RequestBody Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
@GetMapping("/get/{id}")
public Dept get(@PathVariable Long id) {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}
@GetMapping("/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
到这里其实已经完成了负载均衡的基本配置,依次启动项目是可以正常访问的,但是因为提供者服务只有一个,看不出负载均衡的效果。
6、新建两个数据库,分别名为 "springcloud_8002" 和 "springcloud_8003",接着分别在两个数据库中执行下面 sql 脚本初始化表结构及数据:
-- dept.sql
CREATE TABLE dept
(
dept_no BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(60),
db_source VARCHAR(60)
);
INSERT INTO dept(dept_name, db_source)
VALUES ('开发部', DATABASE());
INSERT INTO dept(dept_name, db_source)
VALUES ('人事部', DATABASE());
INSERT INTO dept(dept_name, db_source)
VALUES ('财务部', DATABASE());
INSERT INTO dept(dept_name, db_source)
VALUES ('市场部', DATABASE());
INSERT INTO dept(dept_name, db_source)
VALUES ('运维部', DATABASE());
SELECT * FROM dept;
7、新建两个子工程作为 Provider 服务工程与 "microservicecloud-provider-dept-8001" 提供相同服务做 Provider 服务集群,分别名为 "microservicecloud-provider-dept-8002" 和 "microservicecloud-provider-dept-8003",三个工程连接不同数据库,对应配置如下:
# application.yml#8001
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis 配置文件路径
type-aliases-package: zze.springcloud.entities # 所有 Entity 别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper 映射文件
spring:
application:
name: microservicecloud-provider-dept # 当前微服务名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql 驱动包
url: jdbc:mysql:///springcloud_8001 # 数据库连接 root
username: root
password: root
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
eureka:
client: # 将当前工程作为 Eureka 客户端
service-url:
# 单机版
# defaultZone: http://localhost:7001/eureka # Eureka 服务端地址
defaultZone: http://www.eurekaserver1.com:7001/eureka,http://www.eurekaserver2.com:7002/eureka,http://www.eurekaserver3.com:7003/eureka
instance:
instance-id: microservicecloud-provider-dept-8001
prefer-ip-address: true # 访问路径显示 IP
info:
host: ${java.rmi.server.hostname}
port: ${server.port}
app.name: microservicecloud-provider-dept-8001
build.artifactId: ${project.artifactId}
build.version: ${project.version}
# application.yml#8002
server:
port: 8002
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis 配置文件路径
type-aliases-package: zze.springcloud.entities # 所有 Entity 别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper 映射文件
spring:
application:
name: microservicecloud-provider-dept # 当前微服务名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql 驱动包
url: jdbc:mysql:///springcloud_8002 # 数据库连接 root
username: root
password: root
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
eureka:
client: # 将当前工程作为 Eureka 客户端
service-url:
# 单机版
# defaultZone: http://localhost:7001/eureka # Eureka 服务端地址
defaultZone: http://www.eurekaserver1.com:7001/eureka,http://www.eurekaserver2.com:7002/eureka,http://www.eurekaserver3.com:7003/eureka
instance:
instance-id: microservicecloud-provider-dept-8002
prefer-ip-address: true # 访问路径显示 IP
info:
host: ${java.rmi.server.hostname}
port: ${server.port}
app.name: microservicecloud-provider-dept-8002
build.artifactId: ${project.artifactId}
build.version: ${project.version}
# application.yml#8003
server:
port: 8003
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis 配置文件路径
type-aliases-package: zze.springcloud.entities # 所有 Entity 别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper 映射文件
spring:
application:
name: microservicecloud-provider-dept # 当前微服务名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql 驱动包
url: jdbc:mysql:///springcloud_8003 # 数据库连接 root
username: root
password: root
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
eureka:
client: # 将当前工程作为 Eureka 客户端
service-url:
# 单机版
# defaultZone: http://localhost:7001/eureka # Eureka 服务端地址
defaultZone: http://www.eurekaserver1.com:7001/eureka,http://www.eurekaserver2.com:7002/eureka,http://www.eurekaserver3.com:7003/eureka
instance:
instance-id: microservicecloud-provider-dept-8003
prefer-ip-address: true # 访问路径显示 IP
info:
host: ${java.rmi.server.hostname}
port: ${server.port}
app.name: microservicecloud-provider-dept-8003
build.artifactId: ${project.artifactId}
build.version: ${project.version}
8、测试:
先启动占用端口 7001、7002、7003 的 EurekaServer 服务,
再启动占用端口 8001、8002、8003 的 Provider 服务,
最后启动占用端口 80 的消费者服务,随便访问一个 EurekaServer 的 WebUI 页面:
会发现消费者服务与提供者服务都注册到了 EurekaServer,并且提供者服务有三个实例分别占用端口 8001、8002、8003,
多次访问 http://localhost/consumer/dept/list
,会发现每次访问返回的数据都是从不同的数据库返回,即负载均衡环境搭建成功。
Ribbon 在工作时分为两步走:
- 选择 EurekaServer,它优先选择在同一个区域内负载较少的 Server。
- 根据用户指定的策略,从 EurekaServer 取到的服务注册列表中选择一个服务。
其中 Ribbon 提供了多种策略,例如轮询(默认)、随机和根据响应时间加权。
若测试机器内存不足,则可以只启动一部分服务测试,例如可以只启动一个 EurekaServer,因为在 EurekaServer 集群中每一个 EurekaServer 都是平等的,还可以只启动两个提供者服务,能看到返回数据的不同即可,内存没有 16G 有点 hold 不住。
评论区