WebClient 를 적용하다보니 궁금한점이 생겨서 정리하는 블로깅
API URL
https://jsonplaceholder.typicode.com/todos
https://jsonplaceholder.typicode.com/comments
API 2개를 각각 3가지 방식으로 다르게 호출하고 시간을 측정한 테스트 로직이다.
일단 소스부터 투척
@Test
public void webClient_block() {
long start = System.currentTimeMillis();
String monoResult1 = httpWebClient.httpRequestGet("/todos", null).block();
String monoResult2 = httpWebClient.httpRequestGet("/comments", null).block();
long end = System.currentTimeMillis();
System.out.println("block Elapsed Time: " + (end - start) + "ms");
}
@Test
public void webClient_nonBlock_await() throws InterruptedException {
long start = System.currentTimeMillis();
CountDownLatch cdl = new CountDownLatch(2);
JSONObject jsonObject1 = new JSONObject();
JSONObject jsonObject2 = new JSONObject();
httpWebClient.httpRequestGet("/todos", null)
.doOnSuccess(e -> {
jsonObject1.put("total_result", e);
cdl.countDown(); // 성공적으로 완료되었을 때만 countDown
})
.doOnError(error -> {
// 에러 처리
System.out.println("Error:"+error.getMessage());
cdl.countDown(); // 에러 발생 시에도 countDown
})
.subscribe();
httpWebClient.httpRequestGet("/comments", null)
.doOnSuccess(e -> {
jsonObject2.put("total_result", e);
cdl.countDown(); // 성공적으로 완료되었을 때만 countDown
})
.doOnError(error -> {
// 에러 처리
System.out.println("Error:"+error.getMessage());
cdl.countDown(); // 에러 발생 시에도 countDown
})
.subscribe();
cdl.await();
long end = System.currentTimeMillis();
System.out.println("await Elapsed Time: " + (end - start) + "ms");
}
@Test
public void webClient_nonBlock_tuple() {
long start = System.currentTimeMillis();
Mono<String> result1 = httpWebClient.httpRequestGet("/todos", null);
result1.subscribeOn(Schedulers.boundedElastic());
Mono<String> result2 = httpWebClient.httpRequestGet("/comments", null);
result2.subscribeOn(Schedulers.boundedElastic());
Tuple2<String,String> tuple = Mono.zip(result1,result2).block();
long end = System.currentTimeMillis();
System.out.println("tuple Elapsed Time: " + (end - start) + "ms");
}
1. 블로킹
webClient_block () : 차례대로 호출한 결과
2. 논블로킹
webClient_nonBlock_await - CountDownLatch를 이용하여 호출
CountDownLatch는 언제 쓸까?
쓰레드를 N개 실행했을 때, 일정 개수의 쓰레드가 모두 끝날 때 까지 기다려야지만 다음으로 진행할 수 있거나 다른 쓰레드를 실행시킬 수 있는 경우 사용한다. 예를들어서 리스트에 어떤 자료구조가 있고, 각 자료구조를 병렬로 처리한 후 배치(batch)로 데이터베이스를 업데이트 한다거나 다른 시스템으로 push하는 경우가 있다.
CountDownLatch의 어떤점이 이를 가능하게 하는가?
CountDownLatch를 초기화 할 때 정수값 count를 넣어준다. 쓰레드는 마지막에서 countDown() 메서드를 불러준다. 그러면 초기화 때 넣어준 정수값이 하나 내려간다. 즉 각 쓰레드는 마지막에서 자신이 실행완료했음을 countDown 메서드로 알려준다. 이 쓰레드들이 끝나기를 기다리는 쪽 입장에서는 await()메서드를 불러준다. 그러면 현재 메서드가 실행중이 메인쓰레드는 더이상 진행하지않고 CountDownLatch의 count가 0이 될 때까지 기다린다. 0이라는 정수값이 게이트(Latch)의 역할을 한다. 카운트다운이 되면 게이트(latch)가 열리는 것이다.
즉 한마디로 말해 동시성을 제어하기위해 쓰는거라고 보면된다. 갯수를 정해놓고 그 갯수가 끝날때까지 await. 기다린다.
주로, 이벤트성이나 선착순 프로모션으로 n명이 정해져있는 상태에서 동시성을 체크할때 쓰이는듯 하다.
webClient_nonBlock_tuple - Tuple 를 이용하여 호출
비동기 작업을 병렬로 처리하면서, Mono.zip 및 block을 사용하여 최종 결과를 동기적으로 가져온다.
각각의 Mono가 별도의 스레드에서 실행되므로, 병렬성을 활용할 수 있수있고, 동기적으로 결과가 필요한 경우 쓰면된다.
✔ 3개다 호출했을때 총 걸리는 호출시간은
block Elapsed Time: 1705ms
await Elapsed Time: 1312ms
tuple Elapsed Time: 1105ms
튜플로 병렬처리후 동기적으로 가져왔을때가 젤 빠르다!
상황에 맞춰서 쓰도록 하자.
참고
'web > SpringBoot' 카테고리의 다른 글
[SpringBoot] 생명주기와 메모리관리 (0) | 2023.12.15 |
---|---|
[SpringBoot] 다중 유저 요청처리 (1) | 2023.12.15 |
[SpringBoot] ConvertUtils 클래스 (0) | 2023.12.01 |
[암호화] 개인정보 암호화하기 (0) | 2023.11.30 |
[MSA] Backing service - MOM (1) | 2023.11.28 |