先来看看ribbon的相关配置,以及用法。
以下是springboot项目中,ribbon中的配置。
ribbon:
# 同一实例最大重试次数,不包括首次调用。默认值为0
MaxAutoRetries: 1
# 同一个微服务其他实例的最大重试次数,不包括第一次调用的实例。默认值为1
MaxAutoRetriesNextServer: 1
# 是否所有操作(GET、POST等)都允许重试。默认值为false
OkToRetryOnAllOperations: false
#响应超时(毫秒) 默认一秒
ReadTimeout: 1000
#连接超时(毫秒)
ConnectTimeout: 1000
先来看看这些配置是如何使用的。
下面先写几个demo。分别是注册中心,以及客户端A,客户端B.
其中客户端A主要是作为服务的提供方。而客户端B是作为服务的调用方。
代码示例
1.注册中心
代码相关
@SpringBootApplication
@EnableEurekaServer //开启eureka服务功能
public class Application {
public static void main(String args[]) {
SpringApplication.run(Application.class,args);
}
}
maven配置
<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>com.gee</groupId>
<artifactId>my-eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
spring:
application:
name: erueka-server
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
2.服务A
maven配置
<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>com.gee</groupId>
<artifactId>my-eureka-client-A</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
启动类
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class ApplicationA {
public static void main(String args[]) {
SpringApplication.run(ApplicationA.class,args);
}
}
接口,给与服务B使用
@RestController
public class HelloController {
@GetMapping("/test/get")
public String get() throws InterruptedException {
System.out.println("get");
Thread.sleep(2000);
return "get";
}
@PostMapping("/test/post")
public String post() throws InterruptedException {
System.out.println("post");
Thread.sleep(2000);
return "post";
}
}
application.yml配置文件
spring:
application:
name: clientA
server:
port: 8081
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
default-zone: http://localhost:8761/eureka/
3.服务B
maven配置文件
<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>com.gee</groupId>
<artifactId>my-eureka-client-B</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
启动类
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class ApplicationB {
public static void main(String args[]) {
SpringApplication.run(ApplicationB.class,args);
}
}
feign
@FeignClient(value = "clientA")
public interface ClientA {
@GetMapping("/test/get")
public String get();
@PostMapping("/test/post")
public String post();
}
测试用的controller
@RestController
public class HelloController {
@Autowired
private ClientA clientA;
@GetMapping("/test/get")
public String get() {
return clientA.get();
}
@PostMapping("/test/post")
public String post() {
return clientA.post();
}
}
application.yml配置文件
spring:
application:
name: clientB
server:
port: 8082
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
default-zone: http://localhost:8761/eureka/
ribbon:
# 同一实例最大重试次数,不包括首次调用。默认值为0
MaxAutoRetries: 1
# 同一个微服务其他实例的最大重试次数,不包括第一次调用的实例。默认值为1
MaxAutoRetriesNextServer: 1
# 是否所有操作(GET、POST等)都允许重试。默认值为false
OkToRetryOnAllOperations: false
ReadTimeout: 1000 #响应超时(毫秒) 默认一秒
ConnectTimeout: 1000 #连接超时(毫秒)
ribbon配置相关
注册中心的配置不变
客户端A的配置也不变
客户端B的配置也不变,客户端b的配置如下
ribbon:
# 同一实例最大重试次数,不包括首次调用。默认值为0
MaxAutoRetries: 5
# 同一个微服务其他实例的最大重试次数,不包括第一次调用的实例。默认值为1
MaxAutoRetriesNextServer: 0
# 是否所有操作(GET、POST等)都允许重试。默认值为false
OkToRetryOnAllOperations: false
ReadTimeout: 1000 #响应超时(毫秒) 默认一秒
ConnectTimeout: 1000 #连接超时(毫秒)
启动注册中心,以及客户端B,A。 A启动后暂停。
访问:http://127.0.0.1:8082/test/get
结果如下图
![](https://img.haomeiwen.com/i22548080/c29a044c0325cfa0.png)
由于A未启动。B去调用A的接口必然会超时。
![](https://img.haomeiwen.com/i22548080/1b2e6aeb47894ca1.png)
从浏览器的响应时长来看,该接口的时间约为6秒 = (第一次访问 + 5次重试) * ConnectTimeout = 6秒
现在我们将配置修改一下 再次运行
将同个微服务中的实例切换次数改为1.
MaxAutoRetriesNextServer: 1
再次运行
访问:http://127.0.0.1:8082/test/get
结果如下图
![](https://img.haomeiwen.com/i22548080/97b7367af1fc4e85.png)
从浏览器的响应时长来看,该接口的时间约为12秒。
由于注册中心中只有一个A实例。哪怕切换次数再多次都还是A。
12秒的过程大概如下:
访问接口:
1.第一次调用A服务的接口失败,最多等1秒。
2.随后在该A实例中,继续重试5次,最多等5秒。
3.负载均衡,切换到其同一个微服务中的其他A服务(由于注册中心只有一个A, 必然还是此前的A实例)
4.第一次访问A服务的接口失败,最多等1秒
5.随后在该A实例中,继续重试5次,最多等5秒。
这样子算下来大概就是12秒了。
针对响应超时其实跟连接超时也是一样的结果。只不过一个是规定时间内没有连接到服务端会进行重试,一个是服务端在规定时间内没有写入数据会进行重试。
从上述的实验结果来看:
1.整个接口的最大重试次数为:
1 + MaxAutoRetries + MaxAutoRetriesNextServer * (1 + MaxAutoRetries)
接口正常响应的最大时间:
最大重试次数 * (ReadTimeout + ConnectTimeout)
熔断器与ribbon的配置
先调整服务B,添加熔断器组件
添加熔断器
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
修改启动类
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class ApplicationB {
public static void main(String args[]) {
SpringApplication.run(ApplicationB.class,args);
}
}
添加fallback的类
@Component
public class HystrixClientA implements ClientA{
@Override
public String get() {
return "hys get";
}
@Override
public String post() {
return "hys post";
}
}
修改clientA
@FeignClient(value = "clientA", fallback = HystrixClientA.class)
public interface ClientA {
@GetMapping("/test/get")
public String get();
@PostMapping("/test/post")
public String post();
}
先验证Hystrix的熔断时间小于超时时间的情况下
application.yml添加配置
hystrix的超时时间为500毫秒,而ribbion的最大响应时间为(1+1)2 = 41000ms=4秒。
明显hystrix的时间会小于ribbon的超时时间。
ribbon:
# 同一实例最大重试次数,不包括首次调用。默认值为0
MaxAutoRetries: 1
# 同一个微服务其他实例的最大重试次数,不包括第一次调用的实例。默认值为1
MaxAutoRetriesNextServer: 1
# 是否所有操作(GET、POST等)都允许重试。默认值为false
OkToRetryOnAllOperations: false
ReadTimeout: 1000 #响应超时(毫秒) 默认一秒
ConnectTimeout: 1000 #连接超时(毫秒)
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 500
先将注册中心启动,随后先启动,再关闭A服务,再启动B服务。
调用127.0.0.1:8082/test/get
运行结果如下
![](https://img.haomeiwen.com/i22548080/2700737417de0c70.png)
从上图可得知,为500毫秒左右,就是说,熔断器的熔断生效了。ribbion的配置失效了。当ribbion的超时时间小于hystrix的时候,当超时时间到达hystrix配置的超时时间,则会直接进入fallback的方法。
再验证Hystrix的熔断时间大于或者ribbon等于超时时间的情况下
先调整B服务的配置
ribbon:
# 同一实例最大重试次数,不包括首次调用。默认值为0
MaxAutoRetries: 1
# 同一个微服务其他实例的最大重试次数,不包括第一次调用的实例。默认值为1
MaxAutoRetriesNextServer: 1
# 是否所有操作(GET、POST等)都允许重试。默认值为false
OkToRetryOnAllOperations: false
ReadTimeout: 1000 #响应超时(毫秒) 默认一秒
ConnectTimeout: 1000 #连接超时(毫秒)
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 100000
再次运行,结果如下
![](https://img.haomeiwen.com/i22548080/ecb25527ec9b2056.png)
结果为4秒。
因为没有连接没有超时,但是响应超时。总共重试4次,所以结果为4秒左右。
综上:
单独配置ribbon的时候,
1.整个接口的最大重试次数为:
1 + MaxAutoRetries + MaxAutoRetriesNextServer * (1 + MaxAutoRetries)
接口正常响应的最大时间:
最大重试次数 * (ReadTimeout + ConnectTimeout)
如果配置了Hystrix的话,若Hystrix的超时时间小于ribbon的 (连接超时时间 + 响应超时时间) * 重试次数。
则会直接fallback,进行熔断。
如果Hystrix的超时时间大于等于ribbon的 (连接超时时间 + 响应超时时间) * 重试次数,只要当ribbon重试完,就会马上fallback.
因此为了保证俩块配置不冲突。hystrix的超时时间 应该 >= ribbon的 (连接超时时间 + 响应超时时间) * 重试次数
网友评论