Ribbon实现负载均衡
官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.2.0.M3/reference/html/#spring-cloud-ribbon
概述 下面是截取官方文档的两段话,我们可以大概看一下:
Ribbon是什么:Ribbon是一个客户端负载均衡器。提供了大量的Http和Tcp客户端行为。Feign已经使用了Ribbon,所以,如果使用@FeignClient
,这一部分也被采用。Feign后面也会介绍到。
这一部分使用到了Nacos服务注册中心,所以需要先准备好注册中心,前面有介绍。
项目结构
父级依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.1.9.RELEASE</version > <relativePath /> </parent > <groupId > com.ooyhao</groupId > <artifactId > nacos-feign-parent</artifactId > <version > 0.0.1-SNAPSHOT</version > <packaging > pom</packaging > <properties > <java.version > 1.8</java.version > </properties > <modules > <module > nacos-feign-service-provider-1</module > <module > nacos-feign-service-provider-2</module > <module > nacos-feign-service-provider-3</module > <module > nacos-feign-service-consumer</module > </modules > <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-alibaba-nacos-discovery</artifactId > <version > 0.9.0.RELEASE</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
负载均衡是面向同一服务的集群使用的,所以我们一般都是将同一服务部署到不同的主机上,即拥有不同Ip、相同或不同端口,本地测试,用端口来标识不同的服务。
服务提供者 依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > com.ooyhao</groupId > <artifactId > nacos-feign-parent</artifactId > <version > 0.0.1-SNAPSHOT</version > <relativePath > ../pom.xml</relativePath > </parent > <groupId > com.ooyhao</groupId > <artifactId > nacos-feign-service-provider-1</artifactId > <version > 0.0.1-SNAPSHOT</version > <properties > <java.version > 1.8</java.version > </properties > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
配置文件 下面是三个服务的不同的配置文件:
配置文件一
1 2 3 4 5 6 7 8 9 10 11 12 server: port: 8071 spring: application: name: feign-service-provider cloud: nacos: discovery: server-addr: 127.0 .0 .1 :8848
配置文件二
1 2 3 4 5 6 7 8 9 10 11 12 server: port: 8072 spring: application: name: feign-service-provider cloud: nacos: discovery: server-addr: 127.0 .0 .1 :8848
配置文件三
1 2 3 4 5 6 7 8 9 10 11 12 server: port: 8073 spring: application: name: feign-service-provider cloud: nacos: discovery: server-addr: 127.0 .0 .1 :8848
其实就是指定相同的服务名,不同的端口,并且配置到注册中心,即可。
User 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.ooyhao.nacosfeignserviceprovider.pojo;import lombok.AllArgsConstructor;import lombok.Data;import java.io.Serializable;@Data @AllArgsConstructor public class User implements Serializable { private Integer id; private String username; private String password; private Integer age; }
主程序 1 2 3 4 5 6 7 @SpringBootApplication @EnableDiscoveryClient public class NacosFeignServiceProvider1Application { public static void main (String[] args) { SpringApplication.run(NacosFeignServiceProvider1Application.class , args ) ; } }
说明:这里会发现一个问题,如果没有写@enableDiscoveryClient
注解,也是可以将自身注册到注册中心去的,在网上看到,从Spring Cloud Edgware开始,@EnableDiscoveryClient 或@EnableEurekaClient 可省略 ,测试结果也是如此,于是我翻阅了一下Spring官方文档,看到了下面这段话,即:@EnableDiscoveryClient
不再是必须的了,你可以将DiscoveryClient的实现类置于SpringBoot项目的ClassPath中就可以注册服务。也就是说,我们只需要在POM文件中添加相关依赖,在配置文件中配置相应的配置,就可以自动实现服务注册于发现了。
https://cloud.spring.io/spring-cloud-static/spring-cloud-commons/2.2.0.M3/reference/html/#enablediscoveryclient
Controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @RestController public class IndexController { private static Map<Integer,User> users = new HashMap<>(); static { users.put(1 ,new User(1 ,"张三" ,"provider-1" ,18 )); users.put(2 ,new User(2 ,"李四" ,"provider-1" ,19 )); users.put(3 ,new User(3 ,"王五" ,"provider-1" ,20 )); } @GetMapping ("/user/{id}" ) public User user (@PathVariable("id" ) Integer id) { System.out.println("provider 1 rec" ); return users.get(id); } }
这里需要修改下不同的服务的password,用于后续观察负载均衡的效果。
服务消费者 依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 server: port: 8080 spring: application: name: feign-service-consumer cloud: nacos: discovery: server-addr: 127.0 .0 .1 :8848
User User对象就不再赘述了。将服务提供方的User对象复制过来即可。这里测试发现,服务提供方的User类和服务消费方的User类可以不需要放在相同的包下,这点与RabbitMQ的自动序列化和反序列化要求不同 。
Controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @RestController public class IndexController { @Autowired private RestTemplate restTemplate; @GetMapping ("/user/{id}" ) public User user (@PathVariable("id" ) Integer id) { User user = restTemplate.getForObject("http://feign-service-provider/user/" + id, User.class ) ; System.out.println(user); return user; } }
这里可以发现,使用RestTemplate进行Http接口调用的时候,使用的是服务名feign-service-provider
,而不是IP加端口的形式,这样就把IP地址与服务名的映射关系的任务交给注册中心去维护了。我们只需要知道服务名就可以,至于服务在世界何处不用关心。
主程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @SpringBootApplication @EnableDiscoveryClient public class NacosFeignServiceConsumerApplication { @Bean public IRule iRule () { return new RandomRule(); } @Bean @LoadBalanced public RestTemplate restTemplate () { return new RestTemplate(); } public static void main (String[] args) { SpringApplication.run(NacosFeignServiceConsumerApplication.class , args ) ; } }
测试 随机策略 这里我们先试用随机策略来测试:连续多次刷新访问下面路径:
http://localhost:8080/user/1
可以看出,当前测试结果符合预定随机策略结果。
轮询策略 我们将iRule
方法换成轮询方式,重新启动:
1 2 3 4 @Bean public IRule iRule () { return new RoundRobinRule(); }
测试结果:
测试结果符合轮询规则!