[JAVA WAS] 비동기 처리

Zoonmy·2024년 6월 26일

JAVA WAS

목록 보기
2/5

JAVA WAS 서버를 구현하면서, 들었던 생각은 WAS 처럼 다중 접속 처리를 어떻게 해야하지? 에 대한 생각이었다.

기존 WAS 같읕 경우에는 따로 개발자가 처리해주지 않아도 DEFAULT로 설정된 값들로 멀티스레딩을 통해 동작하는데, 현재 순수 자바로 개발하고 있는 WAS 서버에서는 어떻게 멀티 스레딩을 지원해줄 수 있을 지 고민하였다.


Executor Service

  • 자바의 java.util.concurrent 패키지에 포함되어 있는 인터페이스.
  • 비동기 작업을 실행할 수 있는 방법을 제공
  • 해당 인터페이스에서 스레드를 직접 생성 & 관리 하는 번거로움을 줄일 수 있다.
  • 또한, 다양한 스레드 풀 전략 을 통해 작업을 효과적으로 관리할 수 있게 해준다.

Executor Service 공식 문서

사용한 메소드

  • execute(Runnable command) : 주어진 작업 실행, 작업은 Runnable 인터페이스를 구현한 객체로 표현
  • shutdown() : 더 이상 새로운 작업을 받지 않고, 이미 제출된 작업 완료
ExecutorService executor = new ThreadPoolExecutor(
                   ...
            );

Socket connection;
while ((connection = listenSocket.accept()) != null) {
	executor.execute(new ConnectionHandler(connection));
}

executor.shutdown();

Thread Pool Executor

  • ExecutorService의 구현체 중 하나
  • 스레드 풀을 사용하여 작업을 관리
  • 작업을 스레드 풀에 제출 -> 스레드 풀이 작업을 실행하는 방식으로 동작

사용한 속성

  • corePoolSize: 기본적으로 유지할 스레드 수. 이 수 이하의 스레드는 작업이 없어도 유지됨
  • maximumPoolSize: 최대 스레드 수. 이 수를 초과하는 작업이 제출되면, 추가 작업은 큐에 대기
  • keepAliveTime: 기본 스레드 수를 초과하는 스레드가 유휴 상태로 있을 수 있는 최대 시간. 이 시간이 지나면 스레드는 종료됨
  • workQueue: 작업 큐입니다. 제출된 작업이 대기하는 공간. 일반적으로 LinkedBlockingQueue, SynchronousQueue 등이 사용됨
ExecutorService executor = new ThreadPoolExecutor(
    CORE_THREAD_SIZE,          // 코어 스레드 개수
    MAX_THREAD_SIZE,        // 최대 스레드 개수
    REST_TIME,       // 놀고 있는 시간
    TimeUnit.SECONDS,   // 놀고 있는 시간 단위
	new SynchronousQueue<Runnable>()    // 작업 큐
);

사용한 이유

  • ScheduledThreadPoolExecutor, ForkJoinPool, Executors 팩토리 메소드 등과 같은 방법들이 있지만, ThreadPoolExecutor을 사용한 이유는 아래와 같다.

1) 유연성

  • corePoolSize, maximumPoolSize, keepAliveTime 등의 매개변수를 통해 스레드 풀의 동작을 세밀하게 조정할 수 있는 특징이 있어, 설정들을 커스텀 해보고 싶었다!

2) 작업 큐 선택

  • 여기서는 SynchronousQueue 를 사용했지만, LinkedBlockingQueue와 같은 것들도 사용해볼 수 있기 때문에 선택

    이 부분에 대해서는 리팩토링을 해보아야겠다고 생각한 게, 만약 다중 입력 대기 큐 와 같은 것들을 구현하게 된다면, LinkedBlockingQueue를 통해 구현하는 게 조금 더 편하지 않았을까? 하는 고민이 되었다..!

    다음 포스트에서 상세히 다뤄보겠다!


사용하지 않은 것들 알아보기!

ScheduledThreadPoolExecutor

  • 주기적으로 또는 지연된 작업을 실행할 수 있는 스레드 풀

장점

  • 주기적 작업: 주기적 작업을 간단히 설정 가능
  • 지연 작업: 지연된 작업 실행을 지원
  • 타이머 대체: java.util.Timer와 달리 예외 발생 시에도 계속 실행

단점

  • 고정 크기: 기본적으로 고정된 스레드 풀 크기를 사용

ForkJoinPool

  • ForkJoinPool은 병렬 작업을 처리하기 위해 작업을 분할하고 병합하는 데 최적화된 스레드 풀
  • 자바 7부터 도입된 ForkJoin 프레임워크의 일부

장점

  • 병렬 처리 최적화: 큰 작업을 작은 작업으로 분할하여 병렬로 처리 가능
  • 워크 스틸링: 비활성 스레드가 다른 스레드의 작업을 훔쳐 처리할 수 있음

    워크 스틸링 : 작업자 스레드가 일할 작업이 없으면 다른 스레드의 데크에서 작업을 가져온다. 이를 통해 스레드 간의 작업 부하를 균형잡히게 유지하고, 모든 스레드가 계속 작업을 수행할 수 있도록 한다. 일종의 로드 밸런서 역할과 비슷한? 느낌!

단점

  • 설계 복잡성: 작업을 분할하고 병합하는 알고리즘을 설계하는 것이 복잡

결론적으로, java의 concurrent 패키지를 이용해서 멀티 스레딩에 대한 자원들을 효율적으로 관리하고 customize해서 사용할 수 있다는 것을 알게 되었다.


만약에, 나중에 기회가 된다면 이러한 수치들을 조절해서 다중 대기 큐 와 같은 것들을 구현해봐도 좋을 것 같다고 생각하였다.


활용될 수 있는 스레드 개수는 정해져 있을 때, 그보다 더 큰 요청들이 들어오게 된다면 그것들을 관리해주어야 하는 것도 필요하다고 생각했다. 추후에, 학습을 더 하고 난 후에 다중 대기 큐 같은 것들도 구현해보아야겠다!


profile
열시미 해야쥐

0개의 댓글