🍃
WebClientでリトライ処理
やりたいこと
-
WebClient
のリトライ処理のサンプルコードを書いて動きを確かめてみる - リトライ処理は通常のリトライに加えて、冗長構成をとっているAPIに対して、片方のAPIが失敗したら、別のAPIを叩くようなことも実験してみる
準備
@RestController
public class WebClientController {
private final WebClient webClient;
@Autowired
public WebClientController(WebClient.Builder builder) {
webClient = builder.baseUrl("http://localhost:8080").build();
}
private Mono<String> get(String uri) {
return webClient.get()
.uri(uri)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class);
}
}
このコントローラにいろいろなエンドポイントを追加してみる。
動かしてみる
通常
まずは普通に動いているAPIを呼んでみる。
@GetMapping("ok")
public ResponseEntity<String> getOK() {
return new ResponseEntity<>("OK", new HttpHeaders(), HttpStatus.OK);
}
@GetMapping
public Mono<String> get() {
return get("ok");
}
結果:
% curl -i http://localhost:8080/
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Thu, 17 Feb 2022 04:20:01 GMT
OK
問題なく結果がとれることを確認できました。
同一URLに対してリトライ
一定の確率で失敗するAPIを用意します。
@GetMapping("randError")
public ResponseEntity<String> getRandError() {
if (Math.random() > 0.5) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("OK", new HttpHeaders(), HttpStatus.OK);
}
@GetMapping
public Mono<String> get() {
return get("randError");
}
% curl -i http://localhost:8080/
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Thu, 17 Feb 2022 04:29:32 GMT
OK
% curl -i http://localhost:8080/
HTTP/1.1 400
Content-Length: 0
Date: Thu, 17 Feb 2022 04:29:34 GMT
Connection: close
試行回数を増やすのを実現するのは割と簡単でretry()
を付けるだけ。
@GetMapping
public Mono<String> get() {
return get("randError").retry(10);
}
リトライに間隔を開けたいときはこういうことも(例は、失敗後2秒間隔で10回リトライする)
@GetMapping
public Mono<String> get() {
return get("randError").retryWhen(Retry.fixedDelay(10, Duration.ofSeconds(2)));
}
セカンダリ(別URL)に対してリトライ
APIのエンドポイントが複数用意されている場合、リトライは別のURLを叩きたいというケースもあると思います。例では/primary
(必ず失敗)と/secondary
(必ず成功)を用意し、/primary
を叩いたあとに/secondary
を叩くみたいなことを実験してみる。
@GetMapping("/primary")
public ResponseEntity<String> getError() {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@GetMapping("/secondary")
public ResponseEntity<String> getOK() {
return new ResponseEntity<>("OK", new HttpHeaders(), HttpStatus.OK);
}
@GetMapping
public Mono<String> get() {
return get("primary").onErrorResume(t -> get("secondary"));
}
何事もなくOKが返ってきました。
% curl -i http://localhost:8080/
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Thu, 17 Feb 2022 04:55:53 GMT
OK
プライマリをリトライしたあとに、セカンダリを叩く
先程の応用でOK。ログを埋め込んで動かしてみます。
private Mono<String> get(String uri) {
return webClient.get()
.uri(uri)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class)
.retry(5); // ここにリトライを指定することで各エンドポイントにリトライ処理が走る
}
@GetMapping("primary")
public ResponseEntity<String> getError() {
log.info("primary failed");
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@GetMapping("secondary")
public ResponseEntity<String> getRandError() {
if (Math.random() > 0.3) {
log.info("secondary failed");
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("OK", new HttpHeaders(), HttpStatus.OK);
}
@GetMapping
public Mono<String> get() {
return get("primary").onErrorResume(t -> get("secondary"));
}
アクセス失敗ログが出ているけど、最終的にOKが返ってくることを確認できました。
% curl -i http://localhost:8080/
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Thu, 17 Feb 2022 06:55:26 GMT
OK
2022-02-17 15:55:16.427 INFO 79271 --- [nio-8080-exec-1] WebClientController : primary failed
2022-02-17 15:55:17.434 INFO 79271 --- [nio-8080-exec-2] WebClientController : primary failed
2022-02-17 15:55:18.444 INFO 79271 --- [nio-8080-exec-3] WebClientController : primary failed
2022-02-17 15:55:19.459 INFO 79271 --- [nio-8080-exec-4] WebClientController : primary failed
2022-02-17 15:55:20.473 INFO 79271 --- [nio-8080-exec-5] WebClientController : primary failed
2022-02-17 15:55:21.487 INFO 79271 --- [nio-8080-exec-6] WebClientController : primary failed
2022-02-17 15:55:22.499 INFO 79271 --- [nio-8080-exec-7] WebClientController : secondary failed
2022-02-17 15:55:23.509 INFO 79271 --- [nio-8080-exec-8] WebClientController : secondary failed
2022-02-17 15:55:24.519 INFO 79271 --- [nio-8080-exec-9] WebClientController : secondary failed
2022-02-17 15:55:25.528 INFO 79271 --- [io-8080-exec-10] WebClientController : secondary failed
Discussion