SpringCloud中使用Ribbon的方法详解
springcloud之ribbon介绍
ribbon是负责负载均衡的,属于进程内负载均衡。请求发送到ribbon然后由ribbon发送到各个服务。
服务器端负载均衡:Nginx与F5集中式负载均衡只位于因特网与服务提供者之间,并负责把网络请求转发到各个提供单位。
进程内负载均衡是指从一个实例库选取一个实例进行流量导入,在微服务的范畴内,实例库一般是存储在Eureka、Consul、Zookeeper、etcd这样的注册中心,而此时的负载均衡器就是类似于Ribbon的IPC(Inter-Process Communication,进程间通信)组件,因此,进程内,因此,进程内负载均衡也叫做客户端负载均衡。
Ribbon负载均衡策略
策略类 | 命令 | 描述 |
RandomRule | 随机策略 | 随机选择server |
RoundRobinRule | 轮询策略 | 按照顺序循环选择server |
RetryRule | 重试策略 |
在一个配置时间段内当选择server不成功,则一直尝试选择一个可用的server |
BestAvailableRule | 最低并发策略 | 逐个考察server,如果server断路器打开,则忽略,再选择其中并发连接最低的server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直连接失败并被标记位circuit tripped的server,过滤掉那些高并发连接的server(active connections超过配置的阈值) |
ResponseTimeWeightedRule | 响应时间加权策略 | 根据server的响应时间分配权重。响应时间越长,权重越低,被选择到的概率就越低;响应时间越短,权重越高,被选择到的概率就越高。这个策略很贴切,综合了各种因素,如:网络、磁盘、IO等,这些因素直接影响着响应时间。 |
ZoneAvoidanceRule | 区域权衡策略 | 综合判断server所在区域的性能和server的可用性轮询选择server,并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server |
可以在java配置文件中添加自己所需要的负载均衡策略如:
import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TestConfiguration { @Bean public IRule ribbonRule(){ return new RandomRule(); } }
也可以在yml文件中配置负载均衡的实现类:(注意:yml的配置高于java配置文件,若同时配置了Java和yml则会选择yml的配置)
client-a: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
application.yml中配置ribbon的其他配置
client-a: ribbon: ConnectTimeout: 300000 ReadTimeout: 300000 MaxAutoRetries: 1 #对第一次请求的服务的重试次数 MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包括第一个服务) OkToRetryOnAllOperations: true
ribbon默认是懒加载,所以第一次请求会慢很多(他需要从注册中心拿去服务实例列表,在进行自身策略的调整),为了避免这个问题使懒加载变为饿加载让其在项目启动时就加载,我们采用如下配置:
ribbon: eager-load: enable: true clients: client-a, client-b, client-c
ribbon1.2.0之后的版本支持了yml配置ribbon的行为,也就是说通过配置文件能决定让ribbon使用哪些实现类来完成负载均衡:
ribbon各个功能的接口:这些接口在用到的时候可查有哪些实现类,若有兴趣也可自己实现具体实现类来配置。
自定义配置实现类的列表:
Ribbon实例
搭建Eureka服务器
pom.xml文件
<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> <groupId>eureka-client-ribbon</groupId> <artifactId>eureka-client-ribbon</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>Provider8003</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8 </project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.yml
server: port: 7001 eureka: instance: hostname: localhost client: register-with-eureka: false fetchRegistry: false service-url: defaultZone: http://localhost:7001/eureka
Application.java
package com.zk.ribbon; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableEurekaServer public class Application { @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
搭建服务提供者
pom.xml
<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> <groupId>eureka-client-ribbon</groupId> <artifactId>eureka-client-ribbon</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>Provider8003</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8 </project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Police.java
package com.zk.springribbon; public class Police { private String id;// 警察编号,用来保存用户输入的参数 private String url;// 处理请求的服务器url private String message;// 提示信息 public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
PoliceApplication.java
package com.zk.springribbon; import java.util.Scanner; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class PoliceApplication { public static void main(String[] args) { Scanner scan = new Scanner(System.in); String port = scan.nextLine(); new SpringApplicationBuilder(PoliceApplication.class).properties("server.port=" + port).run(args); } }
PoliceController.java
package com.zk.springribbon; import org.apache.catalina.servlet4preview.http.HttpServletRequest; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class PoliceController { @RequestMapping(value="/getPolice", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE) public Police getPolice(HttpServletRequest request){ Police p = new Police(); p.setUrl(request.getRequestURL().toString()); p.setMessage("警察派出成功"); return p; } @RequestMapping(value="/getPoliceById/{id}", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE) public Police getPolice(HttpServletRequest request, @PathVariable("id") String id){ Police p = new Police(); p.setId(id); p.setUrl(request.getRequestURL().toString()); p.setMessage("指定警察派出成功"); return p; } }
application.yml
server: port: 8003 spring: application: name: springCloud-ribbon-police eureka: client: register-with-eureka: true fetchRegistry: false service-url: defaultZone: http://localhost:7001/eureka
因为需要测试负载均衡,服务提供者需要开启多个服务实例。我们使用手动读取端口号的方式,启动多个服务实例,下面启动8002、8003端口,输入localhost:7001
服务调用者
配置 pom.xml,加入springCloud核心依赖、配置及eureka客户端依赖、Ribbon依赖
pom.xml
<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> <groupId>eureka-client-ribbon</groupId> <artifactId>eureka-client-ribbon</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eureka-client-ribbon</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8 </project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
PersonApplication.java
package com.zk.springribbon; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class PersonApplication { public static void main(String[] args) { new SpringApplicationBuilder(PersonApplication.class).web(true).run(args); } }
PersonController.java
package com.zk.springribbon; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @Configuration public class PersonController { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } @RequestMapping("/getPolice") public String getPolice(){ RestTemplate rt = getRestTemplate(); String result = rt.getForObject("http://springCloud-ribbon-police/getPolice", String.class); return result; } @RequestMapping("/getPoliceById/{id}") public String getPoliceById(@PathVariable("id") String id){ RestTemplate rt = getRestTemplate(); String result = rt.getForObject("http://springCloud-ribbon-police/getPoliceById/"+id, String.class); return result; } }
Application.yml
server: port: 9090 spring: application: name: springCloud-ribbon-person eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
启动http://localhost:9090/getPolice,发现负载均衡服务接口已经调通了。
参考文档如下:https://www.cnblogs.com/li-yan-long/p/15989103.html