코딩은 내일부터

Thread 와 톰캣 튜닝하기 본문

우아한 테크 코스(우테코)/우테코 공부

Thread 와 톰캣 튜닝하기

zl존 비버 2023. 9. 9. 22:43
728x90

Thread를 이해하기 위해서는 Process를 이해해야하는데

 

Process

Process는 실행된 프로그램을 가리키는 말이고,

프로세스는 OS에서 실행될때 필요한 자원을 할당 받게된다.(데이터 or 메모리 or CPU)

프로세스가 할당받은 자원은 다른프로세스와 공유가 안된다.(!!!)

 

Thread

Thread는 Process가 실행되면 관련 로직(연산, 화면처리 등등..)을 수행할때

작업을 수행해주는 일꾼이라고 생각하면 편할거같다.

여기서 중요한 점은 Process는 자원을 공유하지않는다고 위에서 얘기했지만,

Thread는 자원을 공유한다(!!!!!!!!)

 

 

Thread가 여러개일때 문제점 

그래서 다중 스레드 환경에서 두 개 이상의 스레드가 변경 가능한(mutable) 공유 데이터를

동시에 업데이트하면 경쟁 조건(race condition)이 발생한다.

예를 들면

 

 

A라는 사람은 500만원을 가지고있는 상태에서 300만원을

B한테 입금하기버튼을 찰나의 순간에 2번 누른거다!

 

그러면 Thread1은 은행에서 300만원있어?

확인하고 송금절차를 수행하는 순간

Thread2가 또 확인을 하는거다 300만원있어?

그러면 아직 송금이 안됐기 때문에Thread2도 송금 절차를 밟을 것이다.

 

 

 

이러한 문제를 경쟁 조건(race condition)이라고 하는데

 

 

 

 

자바에서는 공유 데이터에 대한 스레드 접근을 동기화(synchronization)하여 경쟁 조건을 방지할 수 있다.

동기화된 블록은 하나의 스레드만 접근하여 실행할 수 있다.

동기화(synchronization)할 수 있는 방법을 하나하나 알아보면

 

 

메서드 동기화

 

 

 

testSynchronized메서드를 보면 0,999번 calculate(++1)를 반복해서 1000이나오면 통과가 되는 테스트 코드다.

 

 

하지만 쓰레드를 3개만들어서 1000번의 calculate를 진행하는건데 효율적으로 처리할거같지만

1000이라는 값을 보장받을 수 없다.

 

왜냐하면 위에서 예를든 경쟁 조건(race condition)이 발생한거다.

 

 

 

 

 

 

 

 

즉, 친구들 3명이 홈페이지의 버튼을 1000번 누르는건데 3명이 같이 빠른속도로 누르다보면 정확히 1000번 클릭하기 어려울것이다.

이러한 문제를 해결하는법은 다음과같이 메소드에 synchronized라는 키워드를 붙혀주면 끝이다.

 

블록 동기화

다음으로는 블록 동기화다.

이방법도 쉽다. 메서드 동기화는 synchronized키워드를 붙혀줬다면 블록동기화는

 

 

 

 

원하는 블록(구간)에 synchronized로 감싸주면 끝이다.

 

 

 

 

지금까지 Thread를 알아보았는데 톰캣에서는 이러한 쓰레드를 설정할 수 있는데 변경할 수 있는 속성이 어떤게 있는지 살펴보겠다.

 

 

쓰레드 설정 속성

maxConnections(기본: 8192)

서버가 허용하고 처리할 수 있는 최대 연결 개수를 의미한다.

만약 서버의 연결 개수가 maxConnections 에 설정한 값과 똑같아지면,

그 이후의 요청은 서버가 계속 수락하지만 처리는 하지 않는다.

처리중인 연결 개수가 maxConnections 아래로 내려가야 서버는 다시 연결 작업을 시작한다.

기본값은 8192이다.

accpetCount(기본: 100)

들어오는 요청이 maxConnections에 도달했을때 대기할 수 있는 크기를 의미한다.

accpetCount 를 설정하지 않으면 기본적으로 accpetCount는 100이다.

maxThread(기본: 200)

Tomcat이 동시에 처리할 수 있는 쓰레드의 개수를 의미한다.

기본값은 200으로 설정을 하지않으면 동시에 200개의요청이 왔을때 한번에 요청을 처리할 수 있다는 뜻이다.

 

 

 

구조

 

 

그래서 이거를 어떻게 써? (실습)

@Test
    void test() throws Exception {
        final var NUMBER_OF_THREAD = 10;
        var threads = new Thread[NUMBER_OF_THREAD];

        for (int i = 0; i < NUMBER_OF_THREAD; i++) {
            threads[i] = new Thread(() -> incrementIfOk(TestHttpUtils.send("/test")));
        }

        for (final var thread : threads) {
            thread.start();
            Thread.sleep(50);
        }

        for (final var thread : threads) {
            thread.join();
        }

        assertThat(count.intValue()).isEqualTo(2);
    }

    private static void incrementIfOk(final HttpResponse<String> response) {
        if (response.statusCode() == 200) {
            count.incrementAndGet();
        }
    }

위에 테스트코드는 10개의 Thread를 준비시켜 /test로 요청을 보내는 것이다.

@Controller
public class SampleController {

****    private static final Logger log = LoggerFactory.getLogger(SampleController.class);

    private static final AtomicInteger count = new AtomicInteger(0);

    private final HelloWorldService helloWorldService;

    @Autowired
    public SampleController(final HelloWorldService helloWorldService) {
        this.helloWorldService = helloWorldService;
    }

    @GetMapping("/test")
    @ResponseBody
    public String helloWorld() throws InterruptedException {
        Thread.sleep(500);
        log.info("http call count : {}", count.incrementAndGet());
        return helloWorldService.helloWorld();
    }
}

test에서 보낸 요청은 여기 SampleController에서 받아 처리한다.

다음과같이 설정해 주었고 test를 실행해보면

 

 

 

기본 설정 (acceptCount : 1, maxConections : 1 ,maxThreads: 2)

총 카운트가 2가 찍히는걸 볼 수 있다.

먼저 10개의 쓰레드 요청이 오면 maxConections에 1이 올라가고,

나머지 요청(9개)은 대기할 수 있는 공간인 acceptCount로 가게되는데

이 acceptCount도 값이 1이기 때문에 1개만 대기하고 나머지(8개)는 타임아웃이 뜬걸 볼 수 있다.

maxThread의 값이 100이여도 maxConections의값이 1로 설정되어있기 때문에 요청 1개의 요청을 처리하고 또 대기해야한다.

 

 

 

 

maxConnections 10은어떨까?

maxConnections를 10으로 설정하면 10개의 Thread를 모두 처리할 수 있지만 처리하는 시간이 5초나 들어간게 보인다.

 

모두 5로 설정하면 어떨까?

5개를 동시에처리하고 acceptCount에서 대기하고있던 5개의 요청도 동시에 처리한것을 볼 수 있다.

 

 

 

 

 

다시 정리를하면

maxThread는 Tomcat이 동시에 처리할 수 있는 Thread 수

maxConnections는 동시에 연결할 수 있는 커넥션  수

accpetCount는 연결된 Thread를 초과했을때 대기할 수 있는 대기방 크기

로정리할 수 있을거같다!