當我們使用Spring Cloud Ribbon實作用戶端負載均衡的時候,通常都會利用@LoadBalanced來讓RestTemplate具備用戶端負載功能,進而實作面向服務名的接口通路。 大多數情況下,上面的實作沒有任何問題,但是總有一些意外發生,比如:有一個執行個體發生了故障而該情況還沒有被服務治理機制及時的發現和摘除,這時候用戶端通路該節點的時候自然會失敗。是以,為了建構更為健壯的應用系統,我們希望當請求失敗的時候能夠有一定政策的重試機制,而不是直接傳回失敗。這個時候就需要開發人員人工的來為上面的RestTemplate調用實作重試機制。 不過,從Spring Cloud Camden SR2版本開始,我們就不用那麼麻煩了。從該版本開始,Spring Cloud整合了Spring Retry來實作重試邏輯,而對于開發者隻需要做一些配置即可。
注意:一半重試時間要不超過斷路器時間
pom檔案引入jar:
<?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>com.zx.dt2b.erp</artifactId>
<groupId>com.zx.dt2b.erp</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>erp</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 辨別這個工程是一個服務,需要引入此jar -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 動态重新整理的一個子產品jar -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 引入ribbon元件負載均衡 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!--重試政策-->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
</dependencies>
<!--eureka版本-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<!-- <version>Dalston.SR5</version> -->
<version>Edgware.SR4</version>
<!-- <version>Finchley.SR1</version> -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>erp</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.zx.dt2b.ErpApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置檔案:
##針對于某一個微服務進行重試政策配置: erp-service為服務
##對所有的請求都進行重試
erp-service.ribbon.OkToRetryOnAllOperations=true
##切換執行個體的次數
erp-service.ribbon.MaxAutoRetriesNextServer=1
##對目前執行個體重試的次數
erp-service.ribbon.MaxAutoRetries=2
spring.application.name=erp-service
server.context-path=/erpservice
server.port=7003
##需要引入eureka注冊中心的位址
eureka.instance.prefer-ip-address=true
#注冊後能顯示自己的ip位址
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ipAddress}:${server.port}
##租期更新時間間隔
eureka.instance.lease-renewal-interval-in-seconds=10
##租期的到期時間間隔
eureka.instance.lease-expiration-duration-in-seconds=30
##開啟健康檢查(必須要引入spring-boot-starter-actuator)
eureka.client.healthcheck.enabled=true
#注冊到對應注冊中心
eureka.client.service-url.defaultZone=http://eureka1:8001/eureka
重試政策方法:
出現各種異常時候重試:
注解
@Retryable
其中:
value = {RemoteAccessException.class, NullPointerException.class}, //需要在捕獲什麼異常的情況下進行重試
maxAttempts = 3, //重試的次數
backoff = @Backoff(delay = 4000, multiplier = 1)//重試延遲4秒鐘執行multiplier:1 單線程
注解
@Recover//最後一次“對應異常”後需要執行的方法
上面出現的異常需要怎麼處理
package com.zx.dt2b.service;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
//需要進行重試的方法
@Retryable(value = {RemoteAccessException.class, NullPointerException.class}, //需要在捕獲什麼異常的情況下進行重試
maxAttempts = 3, //重試的次數
backoff = @Backoff(delay = 4000, multiplier = 1)
)
public void call() throws Exception {
System.err.println("do something.........");
throw new NullPointerException("空指針異常..");
//throw new RemoteAccessException("RPC調用異常..");
}
@Recover
public void recover(RemoteAccessException e) {
System.err.println("最終處理的結果1: " + e.getMessage());
}
@Recover
public void recover(NullPointerException e) {
System.err.println("最終處理的結果2: " + e.getMessage());
}
}
用測試用例測試service即可
package com.bfxy.springcloud.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.bfxy.springcloud.Application;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RetryTests {
@Autowired
private UserService userService;
@Test
public void retryTest() throws Exception {
//userService.call();
}
}