基于Eureka搭建Springcloud微服务-5.使用Ribbon实现客户端负载均衡
原创2020年5月14日大约 14 分钟约 4197 字
5.使用Ribbon实现客户端负载均衡
5.1.章节内容概述
本章节涉及主要内容有:
5.1.章节内容概述
5.2.章节内容大纲
5.3.Ribbon简介
5.4.硬编码配置方式使用Ribbon实现负载均衡(使用Ribbon自带的负载均衡策略)
5.5.声明式配置方式使用Ribbon实现负载均衡(使用Ribbon自带的负载均衡策略)
5.6.硬编码配置方式使用Ribbon实现负载均衡(使用自定义的Ribbon负载均衡策略)
5.7.声明式配置方式使用Ribbon实现负载均衡(使用自定义的Ribbon负载均衡策略)
具体每个小节中包含的内容可使通过下面的章节内容大纲进行查看。
5.2.章节内容大纲
5.3.Ribbon简介
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器,也可以使用Ribbon实现自定义的负载均衡算法。
https://github.com/Netflix/ribbon
5.4.硬编码配置方式使用Ribbon实现负载均衡(使用Ribbon自带的负载均衡策略)
5.4.1.模块简介
基于Ribbon以硬编码配置方式实现的服务消费者,使用Ribbon自带的负载均衡策略,启动端口: 80
5.4.2.模块目录结构
springcloud-consumer-loadbalance-ribbon-hardcode-order80
|-- src
| •-- main
| |-- java
| | •-- org
| | •-- openatom
| | |-- myrule
| | | •-- MySelfRule.java
| | •-- springcloud
| | |-- config
| | | •-- ApplicationContextConfig.java
| | |-- controller
| | | •-- OrderConsumerController.java
| | •-- OrderServiceConsumerLoadBalanceRibbonHardcode80.java
| •-- resources
| •-- application.yml
•-- pom.xml
5.4.3.创建模块
在父工程(springcloud-eureka)中创建一个名为springcloud-consumer-loadbalance-ribbon-hardcode-order80的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息
5.4.4.编写模块pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud-eureka</artifactId>
<groupId>org.openatom</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-loadbalance-ribbon-hardcode-order80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--引入公共的工程-->
<dependency>
<groupId>org.openatom</groupId>
<artifactId>springcloud-api-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!--热部署需要加这个-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
<!--打包多环境-->
<resources>
<resource>
<directory>src/main/resources/</directory>
<includes>
<!--不区分环境:直接加载application.yml配置文件-->
<include>application.yml</include>
</includes>
</resource>
</resources>
</build>
</project>
5.4.5.编写模块application.yml
server:
port: 80
spring:
application:
name: SPRINGCLOUD-CONSUMER-LOADBALANCE-RIBBON-HARDCODE-ORDER80 #注意:服务名不要出现_
devtools:
restart:
enabled: true
logging:
level: info
eureka:
client:
register-with-eureka: true #表示是否将自己注册进EurekaServer默认为true。
fetchRegistry: true #是否从EurekaServer抓取已有的注册信息,默认为true。服务提供端是单节点无所谓,是集群必须设置为true才能配合ribbon使用负载均衡,否则报异常No instances available for SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
#集群版
#defaultZone: http://eureka7002:7002/eureka,http://eureka7003:7003/eureka,http://eureka7004:7004/eureka
instance:
instance-id: ${spring.application.name} #Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容
prefer-ip-address: false #访问路径可以显示IP地址,点击Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容地址栏是否显示IP地址
#服务提供端信息
service:
provider:
name: SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER
url: http://${service.provider.name} #服务提供端名称
5.4.6.编写模块config
package org.openatom.springcloud.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 ApplicationContextConfig {
@Bean
//必须加这个注解,不加这个注解访问会报错
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
5.4.7.编写模块controller
package org.openatom.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderConsumerController {
//单机版
// public static final String PAYMENT_URL = "http://localhost:8001";
// public static final String PAYMENT_URL = "http://localhost:8002";
//集群版
//从配置文件中动态获取远程调用地址
@Value("${service.provider.url}")
private String SERVICE_PROVIDER_URL;
//从配置文件中动态获服务提供端名称
@Value("${service.provider.name}")
private String SERVICE_PROVIDER_NAME;
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(SERVICE_PROVIDER_URL +"/provider/payment/create",payment, CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentByIdReturnObject(@PathVariable("id") Long id){
return restTemplate.getForObject(SERVICE_PROVIDER_URL+"/provider/payment/get/"+id,CommonResult.class);
}
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPaymentByIdReturnResponseEntity(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(SERVICE_PROVIDER_URL+"/provider/payment/get/"+id,CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}else{
return new CommonResult<>(444,"操作失败");
}
}
}
5.4.8.编写负载均衡规则配置类
package org.openatom.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自定义Ribbon负载均衡规则,这个MySelfRule编写后在主启动类使用
*/
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
return new RandomRule();//定义为随机
}
}
这里使用return new RandomRule();,这代表使用的负载均衡算法是RandomRule,Ribbon默认提供了七种负载均衡的算法策略,具体使用哪一种,请根据实际需求灵活选择,这里提供关于七种负载均衡算法的介绍
RoundRobinRule(轮询策略,轮询是Ribbon默认使用的负载均衡算法)
第一次到A,第二次就到B,第三次又到A,第四次又到B......
具体实现是一个负载均衡算法: 第N次请求 % 服务器集群的总数 = 实际调用服务器位置的下标
RandomRule(随机策略)
从服务提供者的列表中随机选择一个服务实例进行调用
RetryRule(轮询重试策略)
按照轮询策略来获取服务,如果获取的服务实例为null或已经失效,则在指定的时间之内不断地进行重试来获取服务,如果超过指定时间依然没获取到服务实例则返回null。
WeightedResponseTimeRule(响应速度决定权重策略)
根据每个服务提供者的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性也就越低。它的实现原理是,刚开始使用轮询策略并开启一个计时器,每一段时间收集一次所有服务提供者的平均响应时间,然后再给每个服务提供者附上一个权重,权重越高被选中的概率也越大。
BestAvailableRule(最优可用策略)
判断最优其实用的是并发连接数。选择并发连接数较小的server发送请求。
AvailabilityFilteringRule(可用性敏感策略)
先过滤掉非健康的服务实例,然后再选择连接数较小的服务实例。
ZoneAvoidanceRule(区域内可用性能最优策略)
基于AvailabilityFilteringRule基础上做的,首先判断一个zone的运行性能是否可用.剔除不可用的区域zone的所有server,然后再利用AvailabilityPredicate过滤并发连接过多的server。
5.4.9.编写模块主启动类
package org.openatom.springcloud;
import org.openatom.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
/**
* 使用Ribbon实现负载均衡:Java硬编码方式
*/
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER",configuration= MySelfRule.class)
public class OrderServiceConsumerLoadBalanceRibbonHardcode80 {
public static void main(String[] args) {
SpringApplication.run(OrderServiceConsumerLoadBalanceRibbonHardcode80.class, args);
}
}
5.4.10.测试模块
启动相关服务
测试硬编码配置方式使用Ribbon实现负载均衡(使用Ribbon自带的负载均衡策略)
在浏览器中访问
http://localhost/consumer/payment/get/1
第一次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第四次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
可以看到四次访问返回的结果中,四次返回结果是没有规律的,因为采用的RandomRule(随机策略),实际返回结果可能不是上面的情况,但是一定是随机进行服务调用的
5.5.声明式配置方式使用Ribbon实现负载均衡(使用Ribbon自带的负载均衡策略)
5.5.1.模块简介
基于Ribbon以声明式配置方式实现的服务消费者,使用Ribbon自带的负载均衡策略,启动端口: 80
5.5.2.模块目录结构
springcloud-consumer-loadbalance-ribbon-configuration-order80
|-- src
| •-- main
| |-- java
| | •-- org
| | •-- openatom
| | •-- springcloud
| | |-- config
| | | •-- ApplicationContextConfig.java
| | |-- controller
| | | •-- OrderConsumerController.java
| | •-- OrderServiceConsumerLoadBalanceRibbonConfiguration80.java
| •-- resources
| •-- application.yml
•-- pom.xml
5.5.3.创建模块
在父工程(springcloud-eureka)中创建一个名为springcloud-consumer-loadbalance-ribbon-configuration-order80的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息
5.5.4.编写模块pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud-eureka</artifactId>
<groupId>org.openatom</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-loadbalance-ribbon-configuration-order80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--引入公共的工程-->
<dependency>
<groupId>org.openatom</groupId>
<artifactId>springcloud-api-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!--热部署需要加这个-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
<!--打包多环境-->
<resources>
<resource>
<directory>src/main/resources/</directory>
<includes>
<!--不区分环境:直接加载application.yml配置文件-->
<include>application.yml</include>
</includes>
</resource>
</resources>
</build>
</project>
5.5.5.编写模块application.yml
server:
port: 80
spring:
application:
name: SPRINGCLOUD-CONSUMER-LOADBALANCE-RIBBON-CONFIGURATION-ORDER80 #注意:服务名不要出现_
devtools:
restart:
enabled: true
logging:
level: info
eureka:
client:
register-with-eureka: true #表示是否将自己注册进EurekaServer默认为true。
fetchRegistry: true #是否从EurekaServer抓取已有的注册信息,默认为true。服务提供端是单节点无所谓,是集群必须设置为true才能配合ribbon使用负载均衡,否则报异常No instances available for SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
#集群版
#defaultZone: http://eureka7002:7002/eureka,http://eureka7003:7003/eureka,http://eureka7004:7004/eureka
instance:
instance-id: ${spring.application.name} #Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容
prefer-ip-address: false #访问路径可以显示IP地址,点击Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容地址栏是否显示IP地址
#某个/某些服务的Ribbon配置
SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER: #服务提供端名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #Ribbon负载均衡规则类所在的路径,自带七种规则,也可以是自定位规则的类所在的路径
#服务提供端信息
service:
provider:
name: SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER #服务提供端名称
url: http://${service.provider.name} #服务提供端调用地址
yml中关于Ribbon负载均衡策略的配置
SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
这里使用com.netflix.loadbalancer.RandomRule ,这代表使用的负载均衡算法是RandomRule,Ribbon默认提供了七种负载均衡的算法策略,具体使用哪一种,请根据实际需求灵活选择,这里提供关于七种负载均衡算法的介绍
RoundRobinRule(轮询策略,轮询是Ribbon默认使用的负载均衡算法)
第一次到A,第二次就到B,第三次又到A,第四次又到B......
具体实现是一个负载均衡算法: 第N次请求 % 服务器集群的总数 = 实际调用服务器位置的下标
RandomRule(随机策略)
从服务提供者的列表中随机选择一个服务实例进行调用
RetryRule(轮询重试策略)
按照轮询策略来获取服务,如果获取的服务实例为null或已经失效,则在指定的时间之内不断地进行重试来获取服务,如果超过指定时间依然没获取到服务实例则返回null。
WeightedResponseTimeRule(响应速度决定权重策略)
根据每个服务提供者的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性也就越低。它的实现原理是,刚开始使用轮询策略并开启一个计时器,每一段时间收集一次所有服务提供者的平均响应时间,然后再给每个服务提供者附上一个权重,权重越高被选中的概率也越大。
BestAvailableRule(最优可用策略)
判断最优其实用的是并发连接数。选择并发连接数较小的server发送请求。
AvailabilityFilteringRule(可用性敏感策略)
先过滤掉非健康的服务实例,然后再选择连接数较小的服务实例。
ZoneAvoidanceRule(区域内可用性能最优策略)
基于AvailabilityFilteringRule基础上做的,首先判断一个zone的运行性能是否可用.剔除不可用的区域zone的所有server,然后再利用AvailabilityPredicate过滤并发连接过多的server。
5.5.6.编写模块config
package org.openatom.springcloud.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 ApplicationContextConfig {
@Bean
//必须加这个注解,不加这个注解访问会报错
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
5.5.7.编写模块controller
package org.openatom.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderConsumerController {
//单机版
// public static final String PAYMENT_URL = "http://localhost:8001";
// public static final String PAYMENT_URL = "http://localhost:8002";
//集群版
//从配置文件中动态获取远程调用地址
@Value("${service.provider.url}")
private String SERVICE_PROVIDER_URL;
//从配置文件中动态获服务提供端名称
@Value("${service.provider.name}")
private String SERVICE_PROVIDER_NAME;
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(SERVICE_PROVIDER_URL +"/provider/payment/create",payment, CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentByIdReturnObject(@PathVariable("id") Long id) {
return restTemplate.getForObject(SERVICE_PROVIDER_URL+"/provider/payment/get/"+id,CommonResult.class);
}
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPaymentByIdReturnResponseEntity(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(SERVICE_PROVIDER_URL+"/provider/payment/get/"+id,CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}else{
return new CommonResult<>(444,"操作失败");
}
}
}
5.5.8.编写模块主启动类
package org.openatom.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* 使用Ribbon实现负载均衡:yml配置方式
* 优点:可以在配置文件中动态修改服务提供端名称
*/
@SpringBootApplication
@EnableEurekaClient
public class OrderServiceConsumerLoadBalanceRibbonConfiguration80 {
public static void main(String[] args) {
SpringApplication.run(OrderServiceConsumerLoadBalanceRibbonConfiguration80.class, args);
}
}
5.5.9.测试模块
启动相关服务
测试硬编码配置方式使用Ribbon实现负载均衡(使用Ribbon自带的负载均衡策略)
在浏览器中访问
http://localhost/consumer/payment/get/1
第一次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第三次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第四次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
可以看到四次访问返回的结果中,四次返回结果是没有规律的,因为采用的RandomRule(随机策略),实际返回结果可能不是上面的情况,但是一定是随机进行服务调用的
5.6.硬编码配置方式使用Ribbon实现负载均衡(使用自定义的Ribbon负载均衡策略)
5.6.1.模块简介
基于Ribbon以硬编码式配置方式实现的服务消费者,使用自定义的Ribbon负载均衡策略,启动端口: 80
5.6.2.模块目录结构
springcloud-consumer-loadbalance-ribbon-custom-strategy-hardcode-order80
|-- src
| •-- main
| |-- java
| | •-- org
| | •-- openatom
| | •-- springcloud
| | |-- config
| | | •-- ApplicationContextConfig.java
| | |-- controller
| | | •-- OrderConsumerController.java
| | |-- loadbalance
| | | •-- MyRoundRobinRule.java
| | •-- OrderServiceConsumerLoadBalanceRibbonCustomerStrategyHardcode80.java
| •-- resources
| •-- application.yml
•-- pom.xml
5.6.3.创建模块
在父工程(springcloud-eureka)中创建一个名为springcloud-consumer-loadbalance-ribbon-custom-strategy-hardcode-order80的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息
5.6.4.编写模块pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud-eureka</artifactId>
<groupId>org.openatom</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-loadbalance-ribbon-custom-strategy-hardcode-order80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--引入公共的工程-->
<dependency>
<groupId>org.openatom</groupId>
<artifactId>springcloud-api-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!--热部署需要加这个-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
<!--打包多环境-->
<resources>
<resource>
<directory>src/main/resources/</directory>
<includes>
<!--不区分环境:直接加载application.yml配置文件-->
<include>application.yml</include>
</includes>
</resource>
</resources>
</build>
</project>
5.6.5.编写模块application.yml
server:
port: 80
spring:
application:
name: SPRINGCLOUD-CONSUMER-LOADBALANCE-RIBBON-CUSTOM-STRATEGY-HARDCODE-ORDER80 #注意:服务名不要出现_
devtools:
restart:
enabled: true
logging:
level: info
eureka:
client:
register-with-eureka: true #表示是否将自己注册进EurekaServer默认为true。
fetchRegistry: true #是否从EurekaServer抓取已有的注册信息,默认为true。服务提供端是单节点无所谓,是集群必须设置为true才能配合ribbon使用负载均衡,否则报异常No instances available for SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
#集群版
#defaultZone: http://eureka7002:7002/eureka,http://eureka7003:7003/eureka,http://eureka7004:7004/eureka
instance:
instance-id: ${spring.application.name} #Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容
prefer-ip-address: false #访问路径可以显示IP地址,点击Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容地址栏是否显示IP地址
#服务提供端信息
service:
provider:
name: SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER
url: http://${service.provider.name} #服务提供端名称
5.6.6.编写模块config
package org.openatom.springcloud.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 ApplicationContextConfig {
@Bean
//必须加这个注解,不加这个注解访问会报错
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
5.6.7.编写模块controller
package org.openatom.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderConsumerController {
//单机版
// public static final String PAYMENT_URL = "http://localhost:8001";
// public static final String PAYMENT_URL = "http://localhost:8002";
//集群版
//从配置文件中动态获取远程调用地址
@Value("${service.provider.url}")
private String SERVICE_PROVIDER_URL;
//从配置文件中动态获服务提供端名称
@Value("${service.provider.name}")
private String SERVICE_PROVIDER_NAME;
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(SERVICE_PROVIDER_URL +"/provider/payment/create",payment, CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentByIdReturnObject(@PathVariable("id") Long id){
return restTemplate.getForObject(SERVICE_PROVIDER_URL+"/provider/payment/get/"+id,CommonResult.class);
}
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPaymentByIdReturnResponseEntity(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(SERVICE_PROVIDER_URL+"/provider/payment/get/"+id,CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}else{
return new CommonResult<>(444,"操作失败");
}
}
}
5.6.8.编写自定义的负载均衡算法策略
package org.openatom.springcloud.loadbalance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* 这个自定义的负载均衡算法编写好之后配置在主启动类中
*/
@Component
public class MyRoundRobinRule extends AbstractLoadBalancerRule {
public MyRoundRobinRule() {
}
@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
int index = this.chooseRandomInt(serverCount);
server = (Server)upList.get(index);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
5.6.9.编写模块主启动类
package org.openatom.springcloud;
import org.openatom.springcloud.loadbalance.MyRoundRobinRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
/**
* 使用Ribbon实现负载均衡:Java硬编码方式
* 缺点:不能在配置文件中动态修改服务提供端名称
*/
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "SPRING-CLOUD-PROVIDER-EUREKA-PAYMENT-SERVICE",configuration= MyRoundRobinRule.class)
public class OrderServiceConsumerLoadBalanceRibbonCustomerStrategyHardcode80 {
public static void main(String[] args) {
SpringApplication.run(OrderServiceConsumerLoadBalanceRibbonCustomerStrategyHardcode80.class, args);
}
}
5.6.10.测试模块
启动相关服务
测试硬编码配置方式使用Ribbon实现负载均衡(使用Ribbon自带的负载均衡策略)
在浏览器中访问
http://localhost/consumer/payment/get/1
第一次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第三次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第四次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
可以看到四次访问返回的结果中,四次返回结果是没有规律的,因为采用的MyRoundRobinRule(自定义策略,这个策略的效果也是随机调用),实际返回结果可能不是上面的情况,但是一定是随机进行服务调用的
5.7.声明式配置方式使用Ribbon实现负载均衡(使用自定义的Ribbon负载均衡策略)
5.7.1.模块简介
基于Ribbon以声明式配置方式实现的服务消费者,使用自定义的Ribbon负载均衡策略,启动端口: 80
5.7.2.模块目录结构
springcloud-consumer-loadbalance-ribbon-custom-strategy-configuration-order80
|-- src
| •-- main
| |-- java
| | •-- org
| | •-- openatom
| | •-- springcloud
| | |-- config
| | | •-- ApplicationContextConfig.java
| | |-- controller
| | | •-- OrderConsumerController.java
| | |-- loadbalance
| | | •-- MyRoundRobinRule.java
| | •-- OrderServiceConsumerLoadBalanceRibbonCustomerStrategyConfiguration80.java
| •-- resources
| •-- application.yml
•-- pom.xml
5.7.3.创建模块
在父工程(springcloud-eureka)中创建一个名为springcloud-consumer-loadbalance-ribbon-custom-strategy-configuration-order80的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息
5.7.4.编写模块pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud-eureka</artifactId>
<groupId>org.openatom</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-loadbalance-ribbon-custom-strategy-configuration-order80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--引入公共的工程-->
<dependency>
<groupId>org.openatom</groupId>
<artifactId>springcloud-api-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!--热部署需要加这个-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
<!--打包多环境-->
<resources>
<resource>
<directory>src/main/resources/</directory>
<includes>
<!--不区分环境:直接加载application.yml配置文件-->
<include>application.yml</include>
</includes>
</resource>
</resources>
</build>
</project>
5.7.5.编写模块application.yml
server:
port: 80
spring:
application:
name: SPRINGCLOUD-CONSUMER-LOADBALANCE-RIBBON-CUSTOM-STRATEGY-CONFIGURATION-ORDER80 #注意:服务名不要出现_
devtools:
restart:
enabled: true
logging:
level: info
eureka:
client:
register-with-eureka: true #表示是否将自己注册进EurekaServer默认为true。
fetchRegistry: true #是否从EurekaServer抓取已有的注册信息,默认为true。服务提供端是单节点无所谓,是集群必须设置为true才能配合ribbon使用负载均衡,否则报异常No instances available for SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
#集群版
#defaultZone: http://eureka7002:7002/eureka,http://eureka7003:7003/eureka,http://eureka7004:7004/eureka
instance:
instance-id: ${spring.application.name} #Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容
prefer-ip-address: false #访问路径可以显示IP地址,点击Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容地址栏是否显示IP地址
#某个/某些服务的Ribbon配置
SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER: #服务提供端名称
ribbon:
NFLoadBalancerRuleClassName: org.openatom.springcloud.loadbalance.MyRandomRule #Ribbon负载均衡规则类所在的路径,自带七种规则,也可以是自定位规则的类所在的路径
#服务提供端信息
service:
provider:
name: SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER
url: http://${service.provider.name} #服务提供端名称
5.7.6.编写模块config
package org.openatom.springcloud.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 ApplicationContextConfig {
@Bean
//必须加这个注解,不加这个注解访问会报错
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
5.7.7.编写模块controller
package org.openatom.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderConsumerController {
//单机版
// public static final String PAYMENT_URL = "http://localhost:8001";
// public static final String PAYMENT_URL = "http://localhost:8002";
//集群版
//从配置文件中动态获取远程调用地址
@Value("${service.provider.url}")
private String SERVICE_PROVIDER_URL;
//从配置文件中动态获服务提供端名称
@Value("${service.provider.name}")
private String SERVICE_PROVIDER_NAME;
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(SERVICE_PROVIDER_URL +"/provider/payment/create",payment, CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentByIdReturnObject(@PathVariable("id") Long id){
return restTemplate.getForObject(SERVICE_PROVIDER_URL+"/provider/payment/get/"+id,CommonResult.class);
}
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPaymentByIdReturnResponseEntity(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(SERVICE_PROVIDER_URL+"/provider/payment/get/"+id,CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}else{
return new CommonResult<>(444,"操作失败");
}
}
}
5.7.8.编写自定义的负载均衡算法策略
package org.openatom.springcloud.loadbalance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* 这个自定义的负载均衡算法编写好之后配置在application.yml中
*/
@Component
public class MyRoundRobinRule extends AbstractLoadBalancerRule {
public MyRoundRobinRule() {
}
@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
int index = this.chooseRandomInt(serverCount);
server = (Server)upList.get(index);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
5.7.9.编写模块主启动类
package org.openatom.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* 使用Ribbon实现负载均衡:Java硬编码方式
* 缺点:不能在配置文件中动态修改服务提供端名称
*/
@SpringBootApplication
@EnableEurekaClient
public class OrderServiceConsumerLoadBalanceRibbonCustomerStrategyConfiguration80 {
public static void main(String[] args) {
SpringApplication.run(OrderServiceConsumerLoadBalanceRibbonCustomerStrategyConfiguration80.class, args);
}
}
5.7.10.测试模块
启动相关服务
测试硬编码配置方式使用Ribbon实现负载均衡(使用Ribbon自带的负载均衡策略)
在浏览器中访问
http://localhost/consumer/payment/get/1
第一次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第三次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第四次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
可以看到四次访问返回的结果中,四次返回结果是没有规律的,因为采用的MyRoundRobinRule(自定义策略,这个策略的效果也是随机调用),实际返回结果可能不是上面的情况,但是一定是随机进行服务调用的
评论