Tomcat PollerEvent Queue

SeungHoon·2025년 5월 19일

Spring

목록 보기
14/15

개요

  • Tomcat NIO Connector에 대해서 공부하다가 Acceptor -> poller -> Worker 구조에서 사용되는 PollerEvent Queue을 발견하게 되었다.
  • 클래스가 길지 않아 한번 분석해보았다.

클래스 미리보기

This is intended as a (mostly) GC-free alternative to java.util.concurrent.ConcurrentLinkedQueue when the requirement is to create an unbounded queue with no requirement to shrink the queue. The aim is to provide the bare minimum of required functionality as quickly as possible with minimum garbage.

  • 여기서 포인트는 불필요한 메모리 쓰레기(Garbage)를 최소화하면서, 필요한 최소한의 기능을 가능한 한 빠르게 제공하는 것이다.
  • ConcurrentLinkedQueue 을 사용할 경우 Node 를 사용하기 때문에 poll() 하게 되면 해당 Node 는 GC의 대상이 된다. 수많은 요청을 처리해야 하는 Tomcat의 특성 상 GC가 자주 일어나는 것은 치명적이다. (GC가 일어나면 STW(Stop-the-World) 가 일어나 GC thread를 제외한 모든 스레드가 멈추기 때문이다)
  • 따라서 배열을 사용해 간단한 Queue를 구현한다.
  • 아래 코드를 보면 synchronized 을 사용해서 동기화를 구현한 것을 확인할 수 있다.

생성자 부분 보기

public class SynchronizedQueue<T> {

    public static final int DEFAULT_SIZE = 128;

    private Object[] queue;
    private int size; // Queue의 크기
    private int insert = 0; // 새로 삽입할 인덱스
    private int remove = 0; // 가장 오래된 인덱스

    public SynchronizedQueue() {
        this(DEFAULT_SIZE);
    }

    public SynchronizedQueue(int initialSize) {
        queue = new Object[initialSize];
        size = initialSize;
    }
    ...
  • 기본 크기는 128이고, 원한다면 크기를 지정해줄 수 있다. 배열로 Queue를 구현한다.

offer(T t)

public synchronized boolean offer(T t) {
        queue[insert++] = t;

        if (insert == size) {
            insert = 0; // 원형 큐임을 짐작할 수 있음.
        }

        if (insert == remove) {
            expand(); // 2배로 늘림
        }
        return true;
    }
  
  • 코드를 보면 원형 큐로 구현했다는 사실을 알 수 있다.
  • insert == remove큐가 꽉 찼다는 것을 의미하므로 expand() 메서드를 통해 Queue 의 크기를 2배 확장한다.

poll()

public synchronized T poll() {
        if (insert == remove) {
            // empty
            return null;
        }

        @SuppressWarnings("unchecked")
        T result = (T) queue[remove];
        queue[remove] = null;
        remove++;

        // Wrap
        if (remove == size) {
            remove = 0;
        }

        return result;
    }
  • 마찬가지로 원형 큐로 구현되었다는 것을 알 수 있다.
  • 여기서 insert == remove 은 위에서와는 의미가 살짝 다른데, 배열이 비어있다는 것을 의미한다.

size(), clear()

public synchronized int size() {
        int result = insert - remove;
        if (result < 0) {
            result += size;
        }
        return result;
    }

    public synchronized void clear() {
        queue = new Object[size];
        insert = 0;
        remove = 0;
    }
  • 마찬가지로 synchronized 로 동기화가 되어있다.

정말 간단한 구조군요

  • 예전에 원형 큐 구현할 때가 생각나면서 복습도 할 수 있었다. Tomcat의 목적인 "필요한 최소한의 기능을 가능한 한 빠르게 제공하는 것" 을 제대로 확인할 수 있었다.
profile
공유하며 성장하는 Spring 백엔드 취준생입니다

0개의 댓글