Spring Event + 非同步實現業務的解耦(結合Spring Retry重試)

語言: CN / TW / HK

theme: channing-cyan

使用場景

  • 專案開發中經常會涉及到非常複雜的業務邏輯,如果全部耦合在一起,一個類的程式碼就會非常長,所以需要我們對業務程式碼進行解耦。通常在一個複雜的邏輯業務裡面,會包含一個核心業務和N個子業務,有些子業務,我們是不需要在當前請求中同步完成的,例如簡訊傳送等。這時,我們可能會想到MQ。但是當我們不想引入MQ時,就可以考慮使用Spring Event,它相當於一個觀察者模式,當一個Bean完成任務之後,會通知另一個Bean去執行相應的任務。
  • Spring Retry也是Spring裡的一個元件,主要實現是發生異常後重新呼叫。當一些瞬時錯誤(例如網路問題)發生時,Spring Retry的重試可以幫我們避免這種異常造成的服務呼叫失敗的情況。

使用案例

自定義事件

@Data @AllArgsConstructor public class MsgEvent { private String msgId; }

定義監聽器

```` @Component @Slf4j public class MsgListener {

/**
 * value值表示當哪些異常的時候觸發重試,
 * maxAttempts表示最大重試次數預設為3,
 * delay表示重試的延遲時間,
 * multiplier表示上一次延時時間是這一次的倍數。
 * @param event
 */
@EventListener
@Async
@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5))
public void sendMsg(MsgEvent event) {
    String msgId = event.getMsgId();
    StopWatch watch = new StopWatch(msgId);
    watch.start();
    log.info("開始發簡訊");
    try {
        Thread.sleep(4000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    watch.stop();
    log.info("簡訊傳送成功, 訊息id:【{}】 | 耗時: ({})", msgId, watch.getLastTaskTimeMillis());
}

} ````

啟動類

新增@EnableAsync和@EnableRetry註解 @SpringBootApplication @EnableAsync @EnableRetry public class LabApplication { public static void main(String[] args) { SpringApplication.run(LabApplication.class, args); } }

單元測試

``` @SpringBootTest @Slf4j public class EventTest {

@Autowired
private ApplicationContext applicationContext;

@Test
public void msgTest() {
    applicationContext.publishEvent(new MsgEvent("123"));
    log.info("簡訊傳送事件釋出成功");
    try {
        Thread.sleep(6000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

} ```

執行結果

1660262103972.jpg