๐ŸŽ๏ธ [์„ฑ๋Šฅ๊ฐœ์„ ] NํŽ˜์ด ์Šน์ธ ๋Œ€์‚ฌ ์†๋„ ๊ฐœ์„ 

์ด์„œยท2024๋…„ 3์›” 19์ผ
0

๐ŸŒฑ ํšŒ์‚ฌ log

๋ชฉ๋ก ๋ณด๊ธฐ
2/2
post-thumbnail

๊ฐœ์š”

์Šน์ธ ๋Œ€์‚ฌ๋ž€ PG์‚ฌ์˜ ์Šน์ธ ๋ฐ ์ทจ์†Œ๋œ ๋ฐ์ดํ„ฐ์™€ ๋‹น์‚ฌ์—์„œ ๊ด€๋ฆฌ๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์ผ์น˜ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ์ž‘์—…์ด์—์š”.

PG๋ž€ Payment Gateway์˜ ์•ฝ์ž๋กœ ์˜จ๋ผ์ธ ๊ฒฐ์ œ ๋Œ€ํ–‰์‚ฌ๋ฅผ ๋œปํ•ด์š”.

์Šน์ธ๋Œ€์‚ฌ์˜ ์ค‘์š”์„ฑ

  1. ๋ฌด๊ฒฐ์„ฑ ํ™•๋ณด: ๊ฒฐ์ œ ์Šน์ธ๋Œ€์‚ฌ๋Š” ๊ณ ๊ฐ์ด ๊ฒฐ์ œํ•œ ๊ธˆ์•ก๊ณผ ์‹ค์ œ๋กœ ์Šน์ธ๋œ ๊ธˆ์•ก์ด ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•จ์œผ๋กœ์จ ๋ฐ์ดํ„ฐ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ๋ณด์žฅํ•ด์š”.
  2. ์˜ค๋ฅ˜ ๋ฐœ๊ฒฌ ๋ฐ ์ˆ˜์ •: ๊ฒฐ์ œ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋‚˜ ๋ถˆ์ผ์น˜๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜์—ฌ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ณ ๊ฐ์ด ๊ฒฐ์ œํ•œ ๊ธˆ์•ก๊ณผ PG์‚ฌ์—์„œ ์Šน์ธ๋œ ๊ธˆ์•ก์ด ๋‹ค๋ฅผ ๊ฒฝ์šฐ, ์ด๋ฅผ ์‹ ์†ํ•˜๊ฒŒ ์ฐพ์•„๋‚ด์–ด ์ ์ ˆํ•œ ์กฐ์น˜๋ฅผ ์ทจํ•  ์ˆ˜ ์žˆ์–ด์š”.
  3. ์žฌ๋ฌด ์ •๋ณด ์‹ ๋ขฐ์„ฑ ํ–ฅ์ƒ: ์Šน์ธ๋Œ€์‚ฌ๋ฅผ ํ†ตํ•ด ํ™•๋ณด๋œ ์ •ํ™•ํ•œ ๋ฐ์ดํ„ฐ๋Š” ๊ธฐ์—…์˜ ์žฌ๋ฌด ์ •๋ณด๊ฐ€ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ๋กœ ์œ ์ง€๋˜๋„๋ก ํ•ด์š”. ์ด๋Š” ๊ฒฝ์˜์ง„์ด ์˜ฌ๋ฐ”๋ฅธ ์˜์‚ฌ ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ๋ฐ ๋„์›€์„ ์ฃผ๋ฉฐ, ์™ธ๋ถ€ ๊ฐ์‚ฌ๋‚˜ ๊ทœ์ œ ๊ธฐ๊ด€์˜ ์š”๊ตฌ์—๋„ ์ ์ ˆํžˆ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ด๋ฒˆ ์Šน์ธ ๋Œ€์‚ฌ ๊ฐœ์„  ๊ฑด์€ ๋„ค์ด๋ฒ„ ํŽ˜์ด ๊ฒฐ์ œ ์ˆ˜๋‹จ์˜ ์Šน์ธ ๋Œ€์‚ฌ์—์„œ ์œ ๋… ์†๋„๊ฐ€ ์ง€์—ฐ๋˜๊ณ  ์žˆ์–ด ๊ฐœ์„ ํ•˜๊ณ ์ž ํ–ˆ์–ด์š”.

AS-IS

๊ธฐ์กด์˜ ๊ฒฝ์šฐ ์Šน์ธ ๋Œ€์‚ฌ ๋ฐฐ์น˜ ์†๋„๋Š” ์ตœ๋Œ€ 23390ms, ์ตœ์†Œ 29ms, ํ‰๊ท  3442.143ms๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ์žˆ์–ด์š”. ์œ„์˜ ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ๋Š” ์ด๋ฒˆ์— ๊ฐœ์„ ํ•˜๊ณ ์žํ•œ ์Šน์ธ ๋Œ€์‚ฌ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค์–‘ํ•œ ๊ฒฐ์ œ ์ˆ˜๋‹จ์— ๋Œ€ํ•œ ํ†ต๊ณ„ ์ˆ˜์น˜์—์š”. ์ €ํฌ ์„œ๋น„์Šค์—์„œ๋Š” ํ•˜๋‚˜์˜ API๋กœ ๋‹ค์–‘ํ•œ ๊ฒฐ์ œ ์ˆ˜๋‹จ ์Šน์ธ ๋Œ€์‚ฌ๊ฐ€ ๋ฐฐ์น˜๋กœ ์ด๋ฃจ์–ด์ง€๊ณ  ์žˆ์–ด์š”. ํ•˜๋‚˜์˜ API๋กœ ์ด๋ฃจ์–ด์ง€๊ณ  ์žˆ์ง€๋งŒ, ์ €ํฌ ์‹œ์Šคํ…œ์—์„œ๋Š” L์‚ฌ ๋ชจ๋‹ˆํ„ฐ๋ง ์†”๋ฃจ์…˜์„ ํ†ตํ•ด ํŠน์ • ์Šน์ธ๋Œ€์‚ฌ์˜ ๋ชจ๋‹ˆํ„ฐ๋ง์ด ๊ฐ€๋Šฅํ•ด์š”.

์—ฌ๊ธฐ์„œ 24์ดˆ์— ๊ทผ์ ‘ํ•œ ํฌ์ธํŠธ๊ฐ€ ๋ฐ”๋กœ ์ด๋ฒˆ์— ๊ฐœ์„ ํ•˜๋ ค๋Š” ๊ฒฐ์ œ ์ˆ˜๋‹จ์ด์—์š”. ๋‹ค๋ฅธ ๊ฒฐ์ œ ์ˆ˜๋‹จ์˜ ์Šน์ธ ๋Œ€์‚ฌ์— ๋น„ํ•ด ์›”๋“ฑํžˆ ๋†’์€ ์ˆ˜์น˜๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ์žˆ์–ด์š”.

ํ•ด๋‹น ๊ฒฐ์ œ ์ˆ˜๋‹จ์˜ ์Šน์ธ ๋Œ€์‚ฌ๋Š” ๋ฒŒํฌ(bulk)๋กœ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํŽ˜์ด์ง€๋„ค์ด์…˜ API๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ ๋ฐ˜๋ณต๋ฌธ์„ ํ†ตํ•ด API๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ์–ด์š”(์šฐ์ธก ํ•˜๋‹จ ๋ถ€๋ถ„์˜ ๋นจ๊ฐ„ ๋„ค๋ชจ ์ƒ์ž๋“ค์ด API ํ˜ธ์ถœ์„ ์˜๋ฏธํ•ด์š”). ์ด๋กœ ์ธํ•ด ์ˆ˜ ์ฒœ ๊ฑด ์ด์ƒ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ์ˆ˜ ์ฒœ ๊ฑด์—์„œ ํŽ˜์ด์ง€๋„ค์ด์…˜๋œ ๊ฐœ์ˆ˜๋งŒํผ ํ•œ ํŽ˜์ด์ง€, ํ•œ ํŽ˜์ด์ง€ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•ด ์ง€์—ฐ(delay) ๋˜๋Š” ์‹œ๊ฐ„์ด ๊ธธ์–ด ๋ฐœ์ƒ๋œ ์ด์œ ์˜€์–ด์š”.

TO-BE

์šฐ์„  ํ•ด๋‹น ๊ฒฐ์ œ ์ˆ˜๋‹จ์˜ ์Šน์ธ ๋Œ€์‚ฌ API๊ฐ€ ๋ฒŒํฌ API๋ฅผ ์ œ๊ณตํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ–ˆ์–ด์š”. ๋ฒŒํฌ API๋ฅผ ์ œ๊ณตํ•œ๋‹ค๋ฉด ํŽ˜์ด์ง€๋งˆ๋‹ค ๋ฐ˜๋ณต๋ฌธ์„ ํ†ตํ•ด API๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„ ์„œ๋น„์Šค ์†๋„ ํ–ฅ์ƒ์„ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด์—์š”. ํ•˜์ง€๋งŒ ๋ฒŒํฌ API๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์•„ ํ•ด๋‹น์•ˆ์œผ๋กœ๋Š” ์ง„ํ–‰ํ•  ์ˆ˜ ์—†์—ˆ์–ด์š”.

๋‹ค์Œ ์•ˆ์œผ๋กœ๋Š” ๋น„๋™๊ธฐ APIํ˜ธ์ถœ์ด์—ˆ์–ด์š”. ํ•œ ํŽ˜์ด์ง€๋งˆ๋‹ค API Response๋ฅผ ๋Œ€๊ธฐํ•˜์ง€ ์•Š๊ณ  ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด ๋ฒŒํฌ API์™€ ๋น„์Šทํ•œ ์„ฑ๋Šฅ์„ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ์—ˆ์–ด์š”. ๋”ฐ๋ผ์„œ ๋น„๋™๊ธฐ API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ชจ๋“  ํŽ˜์ด์ง€์˜ Response๊ฐ€ ์˜ฌ ๋•Œ ๊นŒ์ง€ ๋Œ€๊ธฐ ์ดํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ง„ํ–‰ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์–ด์š”.

๊ธฐ์กด ์ฝ”๋“œ

๊ธฐ์กด์˜ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๋ฅผ ๋„๊ณ  ์žˆ์—ˆ์–ด์š”.

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static org.assertj.core.api.Assertions.assertThat;

public class AsyncCallTest {
    private static class Response {}

    private static class ApiResponse {
        int totalPage;
        int currentPage;

        public ApiResponse(int currentPage, int totalPage) {
            this.currentPage = currentPage;
            this.totalPage = totalPage;
        }
    }

    private static final Random RANDOM = new Random();

    @Test
    void asIs() {
        List<Response> result = new ArrayList<>();
        List<ApiResponse> apiResponses = new ArrayList<>();
        final int START_PAGE = 1;

        // ์ตœ์ดˆ 1ํšŒ API ํ˜ธ์ถœํ•˜์—ฌ ์ „์ฒด ํŽ˜์ด์ง€ ์ˆ˜ ํš๋“
        ApiResponse firtApiResponse = this.apiCall(START_PAGE);
        apiResponses.add(firtApiResponse);

        // ์ „์ฒด ํŽ˜์ด์ง€ ์ˆ˜ ๋งŒํผ API ํ˜ธ์ถœ
        for (int i = START_PAGE + 1; i <= firtApiResponse.totalPage; i++) {
            apiResponses.add(this.apiCall(i));
        }

        // ์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰
        for (ApiResponse apiResponse : apiResponses) {
            result.add(this.logic(apiResponse));
        }

        assertThat(result).hasSize(10);
    }

    private ApiResponse apiCall(int page) {
        try {
            Thread.sleep(RANDOM.nextInt(1000, 3000));
            final int TOTAL_PAGE = 10;
            return new ApiResponse(page, TOTAL_PAGE);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private Response logic(ApiResponse apiResponse) {
        // ์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰ DB ์ž‘์—… (Insert, Update)
        return new Response();
    }
}
โœ… Tests passed: 1 of 1 test - 20 sec 59 ms

[API ํ˜ธ์ถœ] Current Page: 1 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 2 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 3 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 4 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 5 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 6 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 7 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 8 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 9 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 10 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 1 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 2 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 3 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 4 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 5 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 6 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 7 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 8 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 9 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 10 Thread: Test worker

์‹ค์ œ ๋กœ์ง์€ ์Šน์ธ ๋Œ€์‚ฌ ๋ฐ์ดํ„ฐ ๊ธฐ๊ฐ„์„ ์„ค์ •ํ•˜๊ณ  API ํ˜ธ์ถœ์— ํ•„์š”ํ•œ Header๋ฅผ ์…‹ํŒ… ํ•˜๋Š” ๋“ฑ ๋” ๋ณต์žกํ•˜์ง€๋งŒ, ๊ฐ„๋žตํ•˜๊ฒŒ ํ•ต์‹ฌ ๋‚ด์šฉ๋งŒ ์ถ”๋ฆฌ๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ•ด๋ณด์•˜์–ด์š”.

apiCall์„ ํ†ตํ•ด ์Šน์ธ๋Œ€์‚ฌ API๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, 1~3์ดˆ ์ •๋„ ๊ฑธ๋ฆฐ๋‹ค๋Š” ๊ฐ€์ •ํ•˜์— ์ž‘์„ฑํ•˜์˜€์œผ๋ฉฐ, ApiResponse๋ฅผ ํ†ตํ•ด ์ „์ฒด ํŽ˜์ด์ง€ ์ˆ˜๊ฐ€ ์–ผ๋งˆ์ธ์ง€ ํ‘œ๊ธฐํ•ด์ฃผ์–ด์š”. ํ˜„์žฌ ์ฝ”๋“œ์—์„œ๋Š” 10ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ •ํ•˜์—ฌ ์ž‘์„ฑํ–ˆ์–ด์š”.

์ตœ์ดˆ 1ํšŒ API ํ˜ธ์ถœ์„ ํ†ตํ•ด ์ „์ฒด ํŽ˜์ด์ง€ ์ˆ˜๋ฅผ ์–ป๊ณ , ๋ฐ˜๋ณต๋ฌธ์„ ํ†ตํ•ด API๋ฅผ ํ˜ธ์ถœํ•ด์š”. ์ด ๊ฒฝ์šฐ ์ตœ์•…์˜ ๊ฒฝ์šฐ์— ๋ชจ๋“  API๊ฐ€ 3์ดˆ์˜ ์†Œ์š”์‹œ๊ฐ„์ด ๊ฑธ๋ฆฐ๋‹ค๋ฉด ์„œ๋น„์Šค ์‹œ๊ฐ„์€ API ํ˜ธ์ถœ 30์ดˆ + ์Šน์ธ๋Œ€์‚ฌ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์œผ๋กœ 30์ดˆ ์ด์ƒ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์–ด์š”.

๋”ฐ๋ผ์„œ ๋ฐ˜๋ณต๋ฌธ์„ ํ†ตํ•ด ์Šน์ธ ๋Œ€์‚ฌ๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๋ถ€๋ถ„์„ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋กœ ๊ฐœ์„ ํ•˜๊ณ ์ž ํ•˜์˜€์–ด์š”.

๊ฐœ์„  ์ฝ”๋“œ

@Test
void toBe() {
    List<Response> result = this.asyncApiCall() // ๋น„๋™๊ธฐ API ํ˜ธ์ถœ
            .stream()
            .map(this::logic) // ์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰
            .collect(Collectors.toList());;

    assertThat(result).hasSize(10);
}

private List<ApiResponse> asyncApiCall() {
    final int START_PAGE = 1;
    final int SECOND_PAGE = START_PAGE + 1;

    // ์ตœ์ดˆ 1ํšŒ API ํ˜ธ์ถœํ•˜์—ฌ ์ „์ฒด ํŽ˜์ด์ง€ ์ˆ˜ ํš๋“
    ApiResponse firtApiResponse = this.apiCall(START_PAGE);

    // ์ „์ฒด ํŽ˜์ด์ง€ ์ˆ˜ ๋งŒํผ API ํ˜ธ์ถœ
    List<ApiResponse> apiResponses = IntStream
            .rangeClosed(SECOND_PAGE, firtApiResponse.totalPage)
            .boxed()
            .parallel()
            .map(this::apiCall)
            .collect(Collectors.toList());

    apiResponses.add(firtApiResponse);
    return apiResponses;
}
โœ… Tests passed: 1 of 1 test - 6 sec 102 ms

[API ํ˜ธ์ถœ] Current Page: 1 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 7 Thread: Test worker
[API ํ˜ธ์ถœ] Current Page: 2 Thread: ForkJoinPool.commonPool-worker-8
[API ํ˜ธ์ถœ] Current Page: 4 Thread: ForkJoinPool.commonPool-worker-1
[API ํ˜ธ์ถœ] Current Page: 5 Thread: ForkJoinPool.commonPool-worker-3
[API ํ˜ธ์ถœ] Current Page: 9 Thread: ForkJoinPool.commonPool-worker-4
[API ํ˜ธ์ถœ] Current Page: 10 Thread: ForkJoinPool.commonPool-worker-6
[API ํ˜ธ์ถœ] Current Page: 6 Thread: ForkJoinPool.commonPool-worker-7
[API ํ˜ธ์ถœ] Current Page: 8 Thread: ForkJoinPool.commonPool-worker-5
[API ํ˜ธ์ถœ] Current Page: 3 Thread: ForkJoinPool.commonPool-worker-2
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 2 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 3 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 4 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 5 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 6 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 7 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 8 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 9 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 10 Thread: Test worker
[์Šน์ธ ๋Œ€์‚ฌ ์ง„ํ–‰] Current Page: 1 Thread: Test worker

Parallel Stream์„ ์ด์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ๋กœ API๋ฅผ ํ˜ธ์ถœํ–ˆ์–ด์š”. Java์˜ Parallel Stream์€ ๋‚ด๋ถ€์ ์œผ๋กœ ForkJoinPool์˜ commonPool์„ ์‚ฌ์šฉํ•ด์š”. ForkJoinPool์€ ExecutorService์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›๊ณ  ์žˆ์–ด์š”. ๋‚ด๋ถ€์ ์œผ๋กœ Task์˜ ๊ฐœ์ˆ˜(ํฌ๊ธฐ, ์‚ฌ์ด์ฆˆ)์— ๋”ฐ๋ผ ๋ถ„ํ• (fork)ํ•˜์—ฌ Task์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋ชจ๋“  Task๊ฐ€ ์ฒ˜๋ฆฌ๋˜์—ˆ์„ ๊ฒฝ์šฐ์—๋Š” Join์„ ํ†ตํ•ด ์ทจํ•ฉํ•ด์š”. ForkJoinPool์˜ ๋˜ ๋‹ค๋ฅธ ํŠน์ง•์œผ๋กœ๋Š” ์œ ํœด(๋†€๊ณ  ์žˆ๋Š”) Thread๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์œ ํœด Thread๊ฐ€ ๋‹ค๋ฅธ Thread ์žก๋“ค์„ ๊ฐ€์ ธ์™€ ์ˆ˜ํ–‰ํ•˜์—ฌ ์ตœ๋Œ€ํ•œ ์œ ํœด Thread๊ฐ€ ์—†์ด ๋ชจ๋‘ ์ผ๊ด€๋œ ๊ฐœ์ˆ˜์˜ Task๋ฅผ ์ฒ˜๋ฆฌํ•ด์š”.

ForkJoinPool์€ ์ˆ˜ํ–‰๋˜์•ผํ•˜๋Š” Task๊ฐ€ ๋งŽ์•„์งˆ ์ˆ˜๋ก Worker Thread๊ฐ€ ์ถ”๊ฐ€๋˜์–ด์š”. ๋”ฐ๋ผ์„œ ๋งŒ์•ฝ ForkJoinPool์˜ Thread ๊ฐœ์ˆ˜๋ฅผ ์ œํ•œํ•˜๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง์ ‘ ForkJoinPool์„ ์ƒ์„ฑํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์–ด์š”.

private static final ForkJoinPool EXECUTOR = new ForkJoinPool(10);

ForkJoinPool์˜ parallelism ํŒŒ๋ผ๋ฏธํ„ฐ์— int๊ฐ’์œผ๋กœ ์›ํ•˜๋Š” Thread ์ˆ˜ ๊ฐœ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋„˜๊ฒจ์ฃผ๋ฉด ํ•ด๋‹น ๊ฐœ์ˆ˜ ๋งŒํผ์˜ Thread๋งŒ ์ƒ์„ฑ์ด๋˜์–ด์š”.

public class ForkJoinPoolTest {
    private static final ForkJoinPool EXECUTOR = new ForkJoinPool(10);

    @Test
    void test() {
        EXECUTOR.submit(() -> IntStream.rangeClosed(1, 30)
                .boxed()
                .parallel()
                .peek(i -> {
                    try {
                        Thread.sleep(new Random().nextInt(1000, 3000));
                        System.out.println("[NUMBER: " + i + "] Thread: " + Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                })
                .collect(Collectors.toList())
        ).join();

    }
}
[NUMBER: 30] Thread: ForkJoinPool-1-worker-7
[NUMBER: 25] Thread: ForkJoinPool-1-worker-6
[NUMBER: 22] Thread: ForkJoinPool-1-worker-9
[NUMBER: 27] Thread: ForkJoinPool-1-worker-10
[NUMBER: 28] Thread: ForkJoinPool-1-worker-3
[NUMBER: 20] Thread: ForkJoinPool-1-worker-1
[NUMBER: 17] Thread: ForkJoinPool-1-worker-4
[NUMBER: 16] Thread: ForkJoinPool-1-worker-5
[NUMBER: 10] Thread: ForkJoinPool-1-worker-2
[NUMBER: 5] Thread: ForkJoinPool-1-worker-8
[NUMBER: 29] Thread: ForkJoinPool-1-worker-7
[NUMBER: 21] Thread: ForkJoinPool-1-worker-9
[NUMBER: 19] Thread: ForkJoinPool-1-worker-10
[NUMBER: 26] Thread: ForkJoinPool-1-worker-6
[NUMBER: 2] Thread: ForkJoinPool-1-worker-1
[NUMBER: 24] Thread: ForkJoinPool-1-worker-5
[NUMBER: 4] Thread: ForkJoinPool-1-worker-8
[NUMBER: 1] Thread: ForkJoinPool-1-worker-4
[NUMBER: 18] Thread: ForkJoinPool-1-worker-3
[NUMBER: 11] Thread: ForkJoinPool-1-worker-2
[NUMBER: 6] Thread: ForkJoinPool-1-worker-6
[NUMBER: 7] Thread: ForkJoinPool-1-worker-10
[NUMBER: 3] Thread: ForkJoinPool-1-worker-9
[NUMBER: 23] Thread: ForkJoinPool-1-worker-7
[NUMBER: 15] Thread: ForkJoinPool-1-worker-4
[NUMBER: 13] Thread: ForkJoinPool-1-worker-5
[NUMBER: 14] Thread: ForkJoinPool-1-worker-1
[NUMBER: 9] Thread: ForkJoinPool-1-worker-8
[NUMBER: 8] Thread: ForkJoinPool-1-worker-2
[NUMBER: 12] Thread: ForkJoinPool-1-worker-3

Worker Thread๊ฐ€ 10๊ฐœ ์ด์ƒ ๋Š˜์–ด๋‚˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ด ์ฒ˜๋Ÿผ ์ด๋ฒˆ ๊ฐœ์„ ์—์„œ๋Š” ForkJoinPool์˜ Thread ์ˆ˜๋ฅผ ์ œํ•œํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์—ˆ์–ด์š”.

์™œ ForkJoinPool์„ ์‚ฌ์šฉํ•˜์˜€๋Š”๊ฐ€?

  1. ๊ฒฉ๋ฆฌ๋œ ThreadPool ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜์–ด ๋‹ค๋ฅธ ๋กœ์ง๋“ค์— ์˜ํ•œ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๊ธฐ ์œ„ํ•จ.

    Customํ•œ Executor๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์—ˆ์ง€๋งŒ, ์Šน์ธ ๋Œ€์‚ฌ ๋กœ์ง์ด ๋‹ค๋ฅธ ๋กœ์ง์—์„œ ๋ชจ๋‘ ์‚ฌ์šฉ๋˜๋Š” ๊ณตํ†ต๋œ Executor์—์„œ ์‚ฌ์šฉ๋˜๊ธฐ๋ณด๋‹ค ๊ฒฉ๋ฆฌ๋œ ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜์–ด ๋‹ค๋ฅธ ๋กœ์ง์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๊ธฐ ์œ„ํ•จ์ด ์žˆ์—ˆ์–ด์š”. ๊ณตํ†ต๋œ Executor๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ํŠน์ •ํ•œ ๋กœ์ง์—์„œ Thread๋ฅผ ์žก๊ณ  ๋†“์•„์ฃผ์ง€ ์•Š๊ฑฐ๋‚˜, ํ˜น์€ Exeption์ด ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ๋‹ค๋ฅธ ๋กœ์ง์—๋„ ์˜ํ–ฅ์ด ๊ฐˆ ๊ฒƒ์œผ๋กœ ํŒ๋‹จ๋˜์—ˆ์–ด์š”(๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ณตํ†ต๋œ Executor ์‚ฌ์šฉ ๋„์ค‘ ์ ์ ˆํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ๋ชปํ•˜์˜€๊ฑฐ๋‚˜, ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์—‘์…‰์…˜์ด ๋ฐœ์ƒ๋  ๊ฒฝ์šฐ). Thread ์ž‘์—… ์‹œ๊ฐ„์„ ํ•œ์ • ์ง“๊ณ  ํ•ด๋‹น ์ž‘์—…์‹œ๊ฐ„์„ ์ดˆ๊ณผํ•˜๋ฉด ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ตฌํ˜„ํ•˜๊ณ  Exception์„ Handling ํ•˜์—ฌ ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ์—ˆ์ง€๋งŒ, ๊ฐ€์žฅ ํฐ ์ด์œ ๋Š” ๊ฒฉ๋ฆฌ๋œ ํ™˜๊ฒฝ์—์„œ ์Šน์ธ ๋Œ€์‚ฌ๊ฐ€ ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒƒ์„ ์›ํ–ˆ์–ด์š”.

  1. ์Šน์ธ ๋Œ€์‚ฌ ์„œ๋ฒ„์—์„œ ๋น„๋™๊ธฐ ์ž‘์—…์ด ํ•„์š”ํ•œ ๋กœ์ง์€ ์œ„์˜ ํŠน์ •ํ•œ ์Šน์ธ ๋Œ€์‚ฌ์— ๊ฑฐ์˜ ํ•œ์ •๋จ.

    ์Šน์ธ ๋Œ€์‚ฌ ์„œ๋ฒ„์—์„œ ๋น„๋™๊ธฐ ์ž‘์—…์ด ํ•„์š”ํ•œ ๋กœ์ง์€ ๋งŽ์ง€ ์•Š์•˜์–ด์š”. ๋”ฐ๋ผ์„œ Customํ•œ Executor๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋น ๋ฅด๊ณ  ๊ฐ„ํŽธํ•˜๊ฒŒ ForkJoinPool์„ ์ด์šฉํ•˜๋Š” ๋ฐฉ์•ˆ์„ ์ฑ„ํƒํ•˜๊ฒŒ ๋˜์—ˆ์–ด์š”. ๋งŒ์•ฝ ๋น„๋™๊ธฐ ์ž‘์—…์ด ๋งŽ์ด ํ•„์š”๋กœํ•˜๋Š” ์„œ๋น„์Šค์˜€๋‹ค๋ฉด, Spring MVC๊ฐ€ ์•„๋‹ˆ๋ผ Spring WebFlux๋ฅผ ์ฑ„ํƒํ•˜๋Š” ๋ฐฉ์•ˆ์ด๋‚˜ CustomExecutor๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ”์„ ๊ฑฐ์˜ˆ์š”. ํ•˜์ง€๋งŒ ์ด๋Š” ๋ชจ๋‘ ๊ฐœ๋ฐœ Cost๋กœ ์ด์–ด์ง€์ฃ . ์ด๋Ÿฌํ•œ ๋‹ค์–‘ํ•œ ๋น„์šฉ์„ ์ƒ๊ฐํ•ด๋ณด๊ณ  ForkJoinPool์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์–ด์š”.

๊ฒฐ๊ณผ

1. ์ „์ฒด ์Šน์ธ ๋Œ€์‚ฌ API ์†๋„

๋™์ผ ์‹œ๊ฐ„ ๋Œ€ Matrics - ๊ฐœ์„  ๊ฒฐ์ œ ์ˆ˜๋‹จ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ๊ฒฐ์ œ ์ˆ˜๋‹จ์˜ ์Šน์ธ ๋Œ€์‚ฌ๋„ ํฌํ•จ๋œ ์ˆ˜์น˜

๋น„๊ต ์‹œ๊ฐ„ ๋Œ€

  • AS-IS Time: 2023-10-11 09:08:00 ~ 2023-10-12 09:08:00

  • TO-BE Time: 2023-10-18 09:08:00 ~ 2023-10-19 09:08:00

AS-IS

  • ํ‰๊ท ์‹œ๊ฐ„: 4352.689
  • ์ตœ์†Œ์‹œ๊ฐ„: 30
  • ์ตœ๋Œ€์‹œ๊ฐ„: 28071

TO-BE

  • ํ‰๊ท ์‹œ๊ฐ„: 2906.083
  • ์ตœ์†Œ์‹œ๊ฐ„: 24
  • ์ตœ๋Œ€์‹œ๊ฐ„: 23139

2. 18:00:00 ๋™์ผ ์‹œ๊ฐ„ ๋Œ€ NํŽ˜์ด ์Šน์ธ ๋Œ€์‚ฌ ์†๋„

AS-IS ์„œ๋น„์Šค ์‹œ๊ฐ„: 22838ms

TO-BE ์„œ๋น„์Šค ์‹œ๊ฐ„: 5354s

3. ์Šน์ธ ๋Œ€์‚ฌ ์„œ๋ฒ„ CPU ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰

As-Is

  • ์ตœ๊ณ  CPU ์‚ฌ์šฉ๋Ÿ‰: 3194.222
  • ์ตœ๊ณ  ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰: 11226

To-Be

  • ์ตœ๊ณ  CPU ์‚ฌ์šฉ๋Ÿ‰: 2242.222
  • ์ตœ๊ณ  ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰: 7569.286

๊ฒฐ๊ณผ ์ •๋ฆฌ

1. ์ „์ฒด ์Šน์ธ ๋Œ€์‚ฌ API

  • ํ‰๊ท ์‹œ๊ฐ„: 33.16% ๊ฐ์†Œ

2. ์Šน์ธ ๋Œ€์‚ฌ ์„ฑ๋Šฅ

  • ์„œ๋น„์Šค ์„ฑ๋Šฅ: 76.55% ํ–ฅ์ƒ

3. CPU ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰

  • CPU ์‚ฌ์šฉ๋Ÿ‰: 31.98% ๊ฐ์†Œ
  • ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰: 32.62% ๊ฐ์†Œ

๋งˆ์น˜๋ฉฐ

๋งŽ์€ ์‹œ๊ฐ„์„ ์†Œ๋น„ํ•˜๊ณ  ์žˆ๋˜ ํŠน์ • ๊ฒฐ์ œ์˜ ์Šน์ธ ๋Œ€์‚ฌ๋ฅผ ๊ฐœ์„ ํ•ด ๋ณด์•˜์–ด์š”. ์ด๋ฒˆ ๊ฐœ์„ ์„ ํ†ตํ•ด ๋‹จ ํ•˜๋‚˜์˜ ์Šน์ธ ๋Œ€์‚ฌ๋งŒ ๊ฐœ์„ ํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , CPU ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ํฌ๊ฒŒ ๊ฐ์†Œ ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ์œผ๋ฉฐ, NํŽ˜์ด ์Šน์ธ ๋Œ€์‚ฌ ์†๋„์˜ ๊ฒฝ์šฐ์—๋Š” ์•ฝ 77%์˜ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์ด๋ฃฐ ์ˆ˜ ์žˆ์—ˆ์–ด์š”.

๊ฐ€์žฅ ๋†€๋ผ์šด ์ ์€ CPU์™€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๋„ ํฌ๊ฒŒ ๊ฐ์†Œ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์ด์—ˆ์–ด์š”. ์ด๋Š” ๊ณง ์„œ๋น„์Šค ๋น„์šฉ ์ ˆ๊ฐ์—๋„ ์ด์ ์„ ์ทจํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด๋ผ ๋งค์šฐ ๊ธฐ์˜๊ฒŒ ์—ฌ๊ฒจ์ ธ์š”.

์ด๋ฒˆ ๊ฐœ์„ ์„ ํ†ตํ•ด ํ•˜๋‚˜์˜ ์„œ๋น„์Šค๊ฐ€ ์ „์ฒด ์„œ๋น„์Šค์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์ด ์ด๋ ‡๊ฒŒ ํด ์ˆ˜ ์žˆ๊ตฌ๋‚˜ ๋ชธ์†Œ ๊นจ๋‹ฌ์„ ์ˆ˜ ์žˆ์—ˆ์–ด์š”. ๋”ฐ๋ผ์„œ ํ•˜๋‚˜์˜ ์„œ๋น„์Šค, ๋กœ์ง์„ ์ž‘์„ฑํ•˜๋”๋ผ๋„ ๋‹จ์ˆœํ•˜๊ฒŒ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ „๋ถ€๊ฐ€ ์•„๋‹Œ ๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋กœ ์ธํ•œ ์„œ๋น„์Šค ์˜ํ–ฅ๋„ ํŒŒ์•… ๋˜ํ•œ ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋ฐฐ์› ์–ด์š”.

์„ฑ๋Šฅ ๊ฐœ์„ ์˜ ๋‹ต์ด ํ•ญ์ƒ ๋น„๋™๊ธฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€๋Š” ์•Š์•„์š”. ํ•˜์ง€๋งŒ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ํฐ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด์š”. ๋น„๋™๊ธฐ์ ์ธ ์ ‘๊ทผ๋ฒ• ์ด์™ธ์—๋„ ์ฝ”๋“œ ์ตœ์ ํ™”, ์บ์‹ฑ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ํŠœ๋‹ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋ฒ•์ด ์กด์žฌํ•˜๋‹ˆ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•œ ๊ฑฐ ๊ฐ™์•„์š”.

์•ž์œผ๋กœ๋„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ๋ถ„์„ํ•˜์—ฌ ์„ฑ๋Šฅ ๊ฐœ์„ ์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ์ฐพ์•„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด ๊พธ์ค€ํžˆ ๋…ธ๋ ฅํ•˜๊ณ  ํ•™์Šตํ•  ์˜ˆ์ •์ด์˜ˆ์š”๐Ÿ˜†.

profile
๐ŸŽ๏ธ๐Ÿ’จ Beep Beep

0๊ฐœ์˜ ๋Œ“๊ธ€