SpringCloud Ribbon(负载均衡)
1.负载均衡LB
全称Load Balance,将用户的请求平摊到多个服务器上,从而达到系统的HA。
1)集中式LB
在服务消费者和服务提供者之间使用独立的LB设施,如硬件,由该设施负责把访问请求通过某种策略转发至服务提供方。
2)进程内LB
将LB逻辑继承到服务消费者,消费者从服务注册中心获知有哪些地址可用,然后从这些地址中选择一个合适的来使用。Ribbon属于进程内LB。
2.Ribbon定义
只使用restTemplate问题是服务的地址是写死的,不利于维护,没有进行负载均衡。而Ribbon基于NetFlix Ribbon实现的一套客户端负载均衡的工具。但它可以结合RestTemplate进行服务之间的调用。
3.项目实战
3.1基础环境搭建
源代码:https://github.com/zhongyushi-git/cloud-ribbon-demo.git。
说明:由于Eureka2.0已停止维护,故这里使用consul作为服务注册中心,使用RestTemplate进入服务的调用,使用Ribbon进行服务的负载均衡。
1)创建一个maven工程名为cloud-ribbon-demo
,删除src目录
2)在pom中导入依赖,对SpringBoot和SpringCloud版本进行锁定
<properties> <spring.boot.version>2.2.2.RELEASE</spring.boot.version> <spring.cloud.version>Hoxton.SR1</spring.cloud.version> </properties> <!-- 依赖管理,父工程锁定版本--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
3.2搭建服务提供者
1)新建maven子模块(cloud-provider8001),导入依赖
<dependencies> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
2)新建启动类ProviderMain8001并添加注解
package com.zys.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class ProviderMain8001 { public static void main(String[] args) { SpringApplication.run(ProviderMain8001.class, args); } }
3)配置application.yml
server: port: 8001 spring: application: name: cloud-consul-provider cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name}
4)新建controller接口
package com.zys.cloud.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Value("${server.port}") private String port; @GetMapping("/get") public String get() { return "我是服务提供者,端口:" + port; } }
5)启动服务,可看到已注册到consul。
6)根据服务提供者ProviderMain8001,再分别创建ProviderMain8002和ProviderMain8003.
3.3搭建服务消费者
1)新建maven子模块(cloud-consumer80),导入依赖
<dependencies> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
2)新建启动类ConsumerMain80并添加注解
package com.zys.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class ConsumerMain80 { public static void main(String[] args) { SpringApplication.run(ConsumerMain80.class, args); } }
3)配置application.yml
server: port: 80 spring: application: name: cloud-consul-consumer cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name}
4)创建配置类,注入RestTemplate
package com.zys.cloud.config; 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 getRestTemplate() { return new RestTemplate(); } }
要在方法上加@LoadBalanced
,否则在服务调用时会报错。添加注解后,在服务调用时,就会根据其负载均衡策略进行服务的负载转发和请求。
5)新建controller接口
package com.zys.cloud.controller;
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
@RequestMapping("/consumer")
public class UserController {
private final String BASE_URL = "http://cloud-consul-provider";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/get")
public String addUser() {
return restTemplate.getForObject(BASE_URL + "/user/get", String.class);
}
}
在设置请求路径时,http后面是cloud-consul-provider
,这就指定了要调用的服务的名字。这样方便维护,又达到了负载均衡的效果。
5)启动服务,可看到已注册到consul。访问http://localhost/consumer/get,第一次调用的是端口为8001的服务,刷新一次调用的是8002服务,再刷新一次调用的是8003服务,依次轮询。原因是其按默认的轮询策略进行负载的。
6)负载分析。虽然全文都在说Ribbon,但却没有引入其依赖,又能达到效果,这是为何?原因是在引入引入consul时就其已引入了Ribbon,就无需再次引入。
4.Ribbon负载均衡策略
4.1负载策略说明
Ribbon的核心组件是IRule,它的作用就是根据特定的算法从服务列表中选择一个要访问的服务。下面是Ribbon中定义的几种策略:
策略 | 描述 |
RandomRule | 随机 |
RoundRobinRule | 轮询,按照顺序选择server,是Ribbon默认的策略 |
RetryRule | 重试,在一个配置时间段内,当选择server不成功,则一直尝试选择一个可用的server |
BestAvailableRule | 最低并发,逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server |
AvailabilityFilteringRule | 可用过滤,过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server |
ResponseTimeWeightedRule | 响应时间加权重,根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。 |
ZoneAvoidanceRule | 区域权重,综合判断server所在区域的性能和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server |
4.2切换默认的策略
有时需要切换默认策略时,也很简单,在服务消费者中进行配置即可。
假设要把轮询策略改为随机策略,步骤如下:
1)在配置类中注入RandomRule
@Bean public IRule RandomRule(){ return new RandomRule(); }
截图如下:
2)重启服务消费者,进行测试,发现已是随机调用各个服务。