自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 遠程調(diào)用失?。咳绾蝺?yōu)雅的進行重試?

開發(fā) 前端
今天給大家介紹了一下 Spring 的 @Retryable 注解使用,并通過幾個 demo 來帶大家編寫了自己重試攔截器以及回滾方法的時候,是不是感覺用起來會很爽,那還在等什么趕緊用起來吧,其中還有很多細節(jié),只有自己真正的使用過才能體會到。

在日常開發(fā)的過程中我們經(jīng)常會需要調(diào)用第三方組件或者數(shù)據(jù)庫,有的時候可能會因為網(wǎng)絡抖動或者下游服務抖動,導致我們某次查詢失敗。

這種時候我們往往就會進行重試,當重試幾次后依舊還是失敗的話才會向上拋出異常進行失敗。接下來阿粉就給大家演示一下通常是如何做的,以及如何更優(yōu)雅的進行重試。

常規(guī)做法

我們先來看一下常規(guī)做法,常規(guī)做法首先會設置一個重試次數(shù),然后通過 while 循環(huán)的方式進行遍歷,當循環(huán)次數(shù)沒有達到重試次數(shù)的時候,直到有正確結果后就返回,如果重試依舊失敗則會進行睡眠一段時間,再次重試,直到正常返回或者達到重試次數(shù)返回。

package com.example.demo.service;

import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.TimeUnit;

@Service
public class HelloService {
public String sayHello(String name) {
String result = "";
int retryTime = 3;
while (retryTime > 0) {
try {
//
result = name + doSomething();
return result;
} catch (Exception e) {
System.out.println("send message failed. try again in 1's");
retryTime--;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
return result;
}

private int doSomething() {
Random random = new Random();
int i = random.nextInt(3);
System.out.println("i is " + i);
return 10 / i;
}
}

這里為了模擬異常的情況,阿粉在 doSomething? 函數(shù)里面進行了隨機數(shù)的生成和使用,當隨機出來的值為 0 的時候,則會觸發(fā) java.lang.ArithmeticException 異常,因為 0 不能作除數(shù)。

接下來我們再對外提供一個接口用于訪問,代碼如下

package com.example.demo.controller;

import com.example.demo.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

@Autowired
private HelloService helloService;

@GetMapping(value = "/hello")
public String hello(@RequestParam("name") String name) {
return helloService.sayHello(name);
}
}

正常啟動過后,我們通過瀏覽器進行訪問。

圖片

可以看到,我們第一次方法的時候就成功的達到了我們要的效果,隨機數(shù)就是 0 ,在 1 秒后重試后結果正常。在多試了幾次過后,會遇到三次都是 0 的情況,這個時候才會拋出異常,說明服務是真的有問題了。

圖片

圖片

圖片

上面的代碼可以看到是有效果了,雖然不是很好看,特別是在還有一些其他邏輯的情況,看上去會很臃腫,但是確實是可以正常使用的,那么有的小伙伴就要問了,有沒有一種優(yōu)雅的方式呢?總不能在很多地方都重復的這樣寫重試的代碼吧。

注解重試

要知道我們普通人在日常開發(fā)的時候,如果遇到一個問題肯定是別人都遇到過的,什么時候當我們遇到的問題,沒有人遇到過的時候,那說明我們是很前衛(wèi)的。

因此小伙伴能想到的是不是有簡單的方式來進行重試,有的人已經(jīng)幫我們想好了,可以通過 @Retryable 注解來實現(xiàn)一樣的效果,接下來阿粉就給大家演示一下如何使用這個注解。

首先我們需要在啟動類上面加入 @EnableRetry? 注解,表示要開啟重試的功能,這個很好理解,就像我們要開啟定時功能需要添加 @EnableScheduling? 注解一樣,Spring? 的 @Enablexxx 注解也是很有意思的,后面我們再聊。

添加完注解以后,需要加入切面的依賴,如下

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>

如下不加入這個切面依賴,啟動的時候會有如下異常

圖片

添加的注解和依賴過后,我們需要改造 HelloService? 里面的 sayHello()? 方法,簡化成如下,增加  @Retryable 注解,以及設置相應的參數(shù)值。

@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))
public String sayHello(String name){
return name + doSomething();
}

再次通過瀏覽器訪問 http://127.0.0.1:8080/hello?name=ziyou 我們看到效果如下,跟我們自己寫的重試一樣。

圖片

@Retryable 詳解

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.retry.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {
String recover() default "";

String interceptor() default "";

Class<? extends Throwable>[] value() default {};

Class<? extends Throwable>[] include() default {};

Class<? extends Throwable>[] exclude() default {};

String label() default "";

boolean stateful() default false;

int maxAttempts() default 3;

String maxAttemptsExpression() default "";

Backoff backoff() default @Backoff;

String exceptionExpression() default "";

String[] listeners() default {};
}

點到這個注解里面,我們可以看到這個注解的代碼如下,其中有幾個參數(shù)我們來解釋一下

  • recover:  當前類中的回滾方法名稱;
  • interceptor: 重試的攔截器名稱,重試的時候可以配置一個攔截器;
  • value:需要重試的異常類型,跟下面的 include 一致;
  • include:包含的重試的異常類型;
  • exclude:不包含的重試異常類型;
  • label:用于統(tǒng)計的唯一標識;
  • stateful?:標志表示重試是有狀態(tài)的,也就是說,異常被重新拋出,重試策略是否會以相同的策略應用于具有相同參數(shù)的后續(xù)調(diào)用。如果是false,那么可重試的異常就不會被重新拋出。
  • maxAttempts:重試次數(shù);
  • backoff:指定用于重試此操作的屬性;
  • listeners?:重試監(jiān)聽器bean 名稱;

配合上面的一些屬性的使用,我們就可以達到通過注解簡單來實現(xiàn)方法調(diào)用異常后的自動重試,非常好用。我們可以在執(zhí)行重試方法的時候設置自定義的重試攔截器,如下所示,自定義重試攔截器需要實現(xiàn) MethodInterceptor? 接口并實現(xiàn) invoke 方法,不過要注意,如果使用了攔截器的話,那么方法上的參數(shù)就會被覆蓋。

package com.example.demo.pid;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.retry.interceptor.RetryInterceptorBuilder;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.stereotype.Component;

@Component
public class CustomRetryInterceptor implements MethodInterceptor {

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
RetryOperationsInterceptor build = RetryInterceptorBuilder.stateless()
.maxAttempts(2).backOffOptions(3000, 2, 1000).build();
return build.invoke(invocation);
}
}

自定義回滾方法,我們還可以在重試幾次依舊錯誤的情況,編寫自定義的回滾方法。

  @Retryable(value = Exception.class,
recover = "recover", maxAttempts = 2,
backoff = @Backoff(delay = 1000, multiplier = 2))
public String sayHello(String name){
return name + doSomething();
}

@Recover
public String recover(Exception e, String name) {
System.out.println("recover");
return "recover";
}

圖片

要注意:

  • 重試方法必須要使用@Recover 注解;
  • 返回值必須和被重試的函數(shù)返回值一致;
  • 參數(shù)中除了第一個是觸發(fā)的異常外,后面的參數(shù)需要和被重試函數(shù)的參數(shù)列表一致;

上面代碼中的 @Backoff(delay = 1000, multiplier = 2) 表示第一次延遲 1000ms 重試,后面每次重試的延遲時間都翻倍。

總結

阿粉今天給大家介紹了一下 Spring? 的 @Retryable 注解使用,并通過幾個 demo 來帶大家編寫了自己重試攔截器以及回滾方法的時候,是不是感覺用起來會很爽,那還在等什么趕緊用起來吧,其中還有很多細節(jié),只有自己真正的使用過才能體會到。

責任編輯:武曉燕 來源: Java極客技術
相關推薦

2023-10-27 08:20:12

springboot微服務

2019-01-21 09:28:32

版本命令程序員

2020-08-29 19:28:08

版本回退命令代碼

2024-05-29 09:20:41

2024-12-06 09:27:28

2012-04-11 11:10:25

JavaRMI

2023-10-10 13:23:18

空指針異常Java

2023-02-28 08:17:31

Go遠程配置apollo

2010-01-05 13:47:43

Jquery Json

2020-12-11 11:26:47

Spring批處理重試

2022-09-07 09:19:49

Docker健康檢查

2012-02-07 13:21:37

Java

2022-05-10 10:09:12

KubernetesPod網(wǎng)絡抓包

2022-09-15 15:31:50

AndroidHTTPS抓包

2010-02-22 10:06:17

Python調(diào)用

2022-11-29 07:53:50

2023-03-28 08:07:12

2022-08-03 07:07:10

Spring數(shù)據(jù)封裝框架

2010-07-22 13:38:49

SQL Server

2022-04-19 07:51:11

RPC 通信架構
點贊
收藏

51CTO技術棧公眾號