본문 바로가기
1. 개발/1.8. ActiveMQ

Redelivery Policy의 'collisionAvoidanceFactor'는 무엇을 조절하나?

by 엉짱 2026. 3. 27.
반응형

Redelivery Policy의 'collisionAvoidanceFactor'는 무엇을 조절하나?

분산 시스템에서 장애는 필연적으로 발생하며, 이를 극복하기 위해 우리는 메시지 브로커에 '재전송(Redelivery)'과 '지수 백오프(Exponential Backoff)' 정책을 도입합니다. 하지만 완벽해 보이는 이 재시도 메커니즘조차, 특정 조건에서는 오히려 시스템을 완전히 붕괴시키는 파괴적인 무기로 돌변할 수 있습니다.

데이터베이스의 일시적 다운이나 외부 API 서버의 재부팅 상황을 가정해 보겠습니다. 수십 대의 워커(Worker) 노드들이 동시에 연결 실패 에러를 뿜어내며 메시지 처리에 실패합니다. 이때 모든 워커가 정확히 동일한 백오프 설정(예: 5초 대기 후 재시도)을 가지고 있다면 어떤 일이 벌어질까요?

이 가이드에서는 무결해 보이는 지수 백오프의 맹점인 'Thundering Herd(우르르 몰려드는 떼)' 현상과, 이를 우아하게 분산시켜 시스템의 숨통을 틔워주는 ActiveMQ의 핵심 설정인 collisionAvoidanceFactor의 아키텍처적 원리를 상세히 해부합니다.


1. 완벽한 동기화의 저주: Thundering Herd 현상

메시지 브로커에 50개의 컨슈머(Consumer) 스레드가 연결되어 있고, 이들이 공통의 타겟 데이터베이스에 데이터를 삽입하고 있다고 가정해 봅니다.

데이터베이스 서버에 일시적인 네트워크 튐 현상이 발생하여 1초간 연결이 단절되었습니다. 이 1초라는 찰나의 순간 동안, 50개의 컨슈머는 동시에 처리 실패 예외를 발생시키고 브로커의 재전송 정책에 따라 일제히 대기(Sleep) 상태에 빠집니다.

만약 재전송 대기 시간이 '정확히 10초'로 설정되어 있다면, 10초 뒤 데이터베이스가 갓 복구되어 간신히 정상 상태로 돌아온 바로 그 순간, 50개의 스레드가 '정확히 동일한 밀리초(ms)'에 동시에 깨어나 데이터베이스를 향해 무자비한 커넥션 폭격을 가하게 됩니다.

방금 전까지 아파서 쓰러져 있던 시스템에 50명의 사람이 동시에 달려들어 문을 두드리는 격입니다. 결국 데이터베이스는 이 동시다발적인 '충돌(Collision)' 트래픽을 견디지 못하고 다시 뻗어버리게 됩니다. 이를 인프라 공학에서는 무리가 한꺼번에 몰려드는 현상에 빗대어 Thundering Herd 문제라고 부릅니다.


2. Jitter(지터)의 도입과 'Collision Avoidance'

이 끔찍한 연쇄 장애를 막는 방법은 생각보다 단순합니다. 컨슈머들이 정확히 똑같은 시간에 깨어나지 않도록, 대기 시간에 약간의 '무작위성(Randomness)'을 섞어주는 것입니다. 통신 및 소프트웨어 공학에서는 이러한 의도적인 시간차 요동을 'Jitter(지터)'라고 부릅니다.

ActiveMQ는 이 Jitter 메커니즘을 useCollisionAvoidance (충돌 방지 사용) 플래그와 collisionAvoidanceFactor (충돌 방지 계수)라는 두 가지 설정으로 제공합니다.

이 기능을 활성화하면, 브로커는 컨슈머에게 다음 재전송을 예약할 때 원래 계산된 고정 대기 시간에 무작위 가중치를 더하거나 빼서 응답합니다. 결과적으로 50개의 스레드는 10초라는 고정된 시간에 동시에 깨어나는 대신, 8초부터 11초 사이의 넓은 시간대에 걸쳐 산발적이고 부드럽게 깨어나게 됩니다.


3. 'collisionAvoidanceFactor'의 구체적인 동작 원리

수식을 배제하고 이 계수가 작동하는 원리를 직관적으로 설명해 보겠습니다.

collisionAvoidanceFactor는 편차의 '범위(퍼센트 비율)'를 결정하는 척도입니다. ActiveMQ의 기본값은 0.15 (즉, 15퍼센트)로 설정되어 있습니다.

만약 브로커의 재전송 대기 시간이 10초(10,000 밀리초)로 도출되었다고 가정해 보겠습니다.
충돌 방지 기능이 켜져 있다면, 브로커는 10초라는 기준값을 그대로 사용하지 않습니다. 대신 기준값의 15퍼센트인 1.5초를 상하 변동 폭으로 잡습니다.

그리고 기준값에서 1.5초를 뺀 시간부터 1.5초를 더한 시간 사이의 구간에서 완벽하게 무작위(Random)로 하나의 시점을 찍어 새로운 대기 시간으로 확정합니다.

결과적으로 어떤 메시지는 8.5초 뒤에 재전송되고, 어떤 메시지는 9.2초 뒤에, 또 어떤 것은 11.5초 뒤에 재전송됩니다. 지수 백오프(Exponential Backoff)를 통해 대기 시간이 20초, 40초로 기하급수적으로 길어질수록, 이 15퍼센트의 변동 폭 역시 3초, 6초 단위로 함께 넓어지므로 트래픽 분산 효과는 갈수록 강력해집니다.


4. 아키텍처 복원력(Resilience)에 미치는 파급 효과

이 작은 난수(Random Number) 하나가 전체 엔터프라이즈 아키텍처에 미치는 효과는 경이롭습니다.

  • 타겟 시스템의 연착륙(Soft Landing) 유도: 장애가 발생했던 API 서버나 데이터베이스가 재부팅을 마치고 캐시를 덥히며(Warm-up) 서비스를 준비할 때, 밀려있던 트래픽이 쓰나미처럼 한 번에 몰아치지 않고 이슬비처럼 넓은 시간대에 분산되어 쏟아집니다. 타겟 시스템이 다시 과부하에 빠지지 않고 안정적으로 트래픽을 소화할 수 있는 여유를 제공합니다.
  • 경합 조건(Race Condition)의 해소: 동일한 레코드를 수정하려다 데이터베이스의 데드락(Deadlock)이나 낙관적 락(Optimistic Lock) 예외가 발생하여 롤백된 여러 개의 메시지가 있다고 가정해 봅니다. 이들이 똑같은 시간 뒤에 다시 시도한다면 100퍼센트 확률로 또다시 락 경합이 발생합니다. 하지만 Jitter가 개입되면 재시도 시점이 서로 미세하게 어긋나게 되어, 락 경합이 자연스럽게 해소되고 모든 메시지가 순차적으로 정상 처리될 수 있습니다.

5. Spring Boot 환경의 최적 설정 가이드

Spring Framework를 사용하여 ActiveMQ를 연동할 때, 이 충돌 방지 기능은 RedeliveryPolicy 객체를 통해 매우 간단하게 주입할 수 있습니다.

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.RedeliveryPolicy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ActiveMQConfig {

    @Bean
    public ActiveMQConnectionFactory connectionFactory() {
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
        factory.setBrokerURL("tcp://localhost:61616");

        RedeliveryPolicy policy = new RedeliveryPolicy();

        // 지수 백오프 활성화
        policy.setUseExponentialBackOff(true);
        policy.setInitialRedeliveryDelay(2000); 

        // ★ Jitter (충돌 방지) 메커니즘 활성화
        policy.setUseCollisionAvoidance(true);
        // ★ 변동 폭 설정 (기본값 0.15 유지 또는 인프라에 맞게 조절)
        policy.setCollisionAvoidanceFactor(0.15); 

        policy.setMaximumRedeliveries(5);

        factory.setRedeliveryPolicy(policy);
        return factory;
    }
}

기본값인 15퍼센트(0.15)는 대부분의 트래픽 패턴에서 훌륭한 분산 효과를 보여줍니다. 만약 컨슈머의 수가 수백 대 이상으로 극단적으로 많다면 이 값을 0.2 나 0.3 수준으로 소폭 올려 분산의 스펙트럼을 넓히는 아키텍처적 결단이 필요할 수 있습니다.


결론적으로 'collisionAvoidanceFactor'는 기계적으로 정확한 재시도 타이밍에 의도적인 '인간적인 흠결(무작위성)'을 부여하여 분산 시스템 전체의 숨통을 틔워주는 백신의 역할을 합니다. 지수 백오프를 아키텍처에 적용할 때는, 반드시 이 Jitter 기능을 함께 활성화하여 견고하고 유연한 장애 복원력을 확보하시기 바랍니다.

반응형