RocketMQ "No topic route info in name server for the topic" 오류 해결 가이드

배경

비즈니스 팀이 사용하는 RocketMQ 클러스터는 미들웨어 그룹에서 구축했으며, 클라이언트는 마이크로서비스 그룹이 원시 RocketMQ 클라이언트를 기반으로 캡슐화한 xcloud-rocketmq입니다.

비즈니스 팀은 프로젝트 출시 이후 온라인 프로그램에서 지속적으로 다음과 같은 오류가 발생한다고 피드백했습니다:

No topic route info in name server for the topic: RMQ_SYS_TRACE_TOPIC

문제 조사

1. 비즈니스 팀의 설명을 듣고 온라인 클러스터에 문제가 있다고 생각했습니다. 콘솔에서 토픽이 성공적으로 생성되었지만 메시지 전송이 실패한 상황이었습니다. 온라인 클러스터가 정상인지 확인하기 위해 테스트 토픽을 생성하고 Java 클라이언트로 메시지를 보내보았으나 정상적으로 동작했습니다.

2. 마이크로서비스 그룹이 캡슐화한 xcloud 클라이언트일 가능성을 의심하여 비즈니스 팀과 동일한 클라이언트로 테스트했지만 결과는 정상이었고 경고 로그도 출력되지 않았습니다.

배제 및 귀납법을 통해 RocketMQ 서버와 클라이언트 모두 정상이라는 결론을 내렸습니다. 차이점은 비즈니스 팀의 사용 방식이나 설정에 있었습니다.

문제 발견

대화를 통해 비즈니스 팀이 RocketMQ 클라이언트의 로그를 비즈니스 로그와 함께 출력하도록 설정했다는 것을 알게 되었습니다. 이로 인해 ELK에서 수집한 비즈니스 로그에 RocketMQ 클라이언트의 예외 정보가 계속 출력되었습니다. 즉, 반복 출력되는 로그는 RocketMQ 클라이언트 원시 경고 로그였습니다.

<logger>
    <appender-ref ref="INFO_FILE" />
</logger>

소스 코드를 확인한 결과 RocketMQ 클라이언트가 자체적으로 로그를 특정 디렉토리에 출력하고 있었습니다:

로컬 머신의 ~/logs/rocketmqlogs 디렉토리를 확인해보니 실제로 로그 파일이 있었습니다.

전체 예외 로그 정보를 확인한 후 중요한 정보를 발견했습니다:

No topic route info in name server for the topic:RMQ_SYS_TRACE_TOPIC
  • RMQ_SYS_TRACE_TOPIC: 이 토픽은 브로커에서 메시지 추적이 활성화된 경우 시스템이 자동으로 생성합니다. 하지만 저는 메시지 추적을 활성화하지 않았으므로 이 토픽에 대한 예외가 발생해서는 안 됩니다.
  • TBW102: 이 토픽은 브로커 시작 시 autoCreateTopicEnable 설정이 true인 경우 자동으로 생성되는 기본 토픽입니다. 프로듀서가 전송하려는 토픽이 없으면 먼저 이 기본 토픽으로 라우팅된 후 새로운 토픽이 생성됩니다. 하지만 저는 자동 토픽 생성을 활성화하지 않았습니다.

로그 분석에 따른 문제 요약:

  • 클라이언트는 정기적으로 토픽 라우팅 정보를 업데이트하여 올바른 메시지 생산/소비를 보장합니다.
  • 서버가 클라이언트의 요청 중 RMQ_SYS_TRACE_TOPIC이라는 알 수 없는 토픽을 발견했습니다. 콘솔에서 확인한 결과 해당 토픽은 존재하지 않았습니다.
  • RocketMQ의 라우팅 로직에 따라 토픽을 찾을 수 없으면 TBW102가 등장합니다.
  • 자동 토픽 생성이 비활성화되어 TBW102도 존재하지 않아 무한 루프가 발생했습니다.

그렇다면 왜 서버에 RMQ_SYS_TRACE_TOPIC이 없는데 클라이언트가 이 예외를 발생시키는 걸까요?

문제 분석

의문점을 가지고 오류 정보의 근원을 추적해보았습니다. 클라이언트가 토픽 목록을 업데이트할 때 서버에서 직접 가져오는 것이 아니라, 먼저 모든 Consumer와 Producer 컬렉션을 조회한 후 이를 통해 관련된 토픽을 가져오는 것을 발견했습니다. 코드는 다음과 같습니다:

// 예시: ClientContext 클래스 내부
// consumerTable과 producerTable에서 topic 정보를 추출
Set<String> topicSet = new HashSet<>();
this.consumerTable.values().forEach(consumer -> topicSet.addAll(consumer.getTopics()));
this.producerTable.values().forEach(producer -> topicSet.addAll(producer.getTopics()));
return topicSet;

왜 Kafka처럼 직접 토픽 목록을 가져오지 않는 걸까요? 추측컨대 Kafka는 Zookeeper를 사용해 메타데이터를 저장하여 강력한 일관성을 보장하지만, RocketMQ는 그렇지 않습니다. 따라서 클라이언트는 자체 캐시된 Consumer와 Producer를 기준으로 라우팅할 토픽 정보를 조회합니다.

서버에 RMQ_SYS_TRACE_TOPIC이 없는데 클라이언트가 예외를 발생시키는 이유는 무엇일까요?

추측:

  1. 일부 시스템 또는 사용자의 Consumer/Producer가 RMQ_SYS_TRACE_TOPIC 토픽에 연결되어 있습니다.
  2. 사용자가 메시지 추적을 활성화하고 enableMsgTrace 파라미터를 true로 설정하여 클라이언트가 가져온 토픽 목록에 이 토픽이 포함되었지만 서버에는 없어서 지속적으로 오류가 발생합니다.

문제 해결

해결 방안

공식 GitHub 이슈에서도 이 문제가 제기되었습니다:

https://github.com/apache/rocketmq/issues/2938

공식 설명에 따르면 RocketMQ 4.4.0부터 메시지 추적을 지원합니다. 클라이언트에서 메시지 추적을 true로 설정한 경우 브로커 설정 파일에 tracetopicEnable=true를 설정해야 합니다.

이는 제 추측과 일치합니다. 일부 클라이언트가 Producer를 생성할 때 enableMsgTrace 파라미터를 true로 전달했기 때문입니다. 따라서 false로 전달하거나 파라미터를 생략하거나 서버 설정을 수정하여 메시지 추적을 지원해야 합니다.

public class Producer {
    public static void main(String[] args) throws MQClientException, InterruptedException {
        // enableMsgTrace를 false로 설정하여 메시지 추적 비활성화
        DefaultMQProducer producer = new DefaultMQProducer("zhurunhua", false);
        producer.setNamesrvAddr("172.20.10.42:9976;172.20.10.43:9976");
        producer.start();
        long l = System.currentTimeMillis();
        try {
            Message msg = new Message("test_topic", "TagA", "OrderID188",
                    "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = producer.send(msg);
            System.out.printf("cost:%s-->%s%n", (System.currentTimeMillis() - l), sendResult);
        } catch (Exception e) {
            log.error("", e);
        }
        producer.shutdown();
    }
}

하지만 비즈니스 팀의 코드를 확인한 결과 메시지 추적을 true로 설정하지 않았습니다:

@Component
@Slf4j
public class MqComp {
    @Resource
    private RocketMQTemplate rocketMqTemplate;

    public SendResult syncSendOrderly(String destination, Message<?> message, String hashKey) {
        return rocketMqTemplate.syncSendOrderly(destination, message, hashKey);
    }

    public void send(String destination, Message<?> message) {
        rocketMqTemplate.send(destination, message);
    }

    public void asyncSend(String destination, Message<?> message, SendCallback sendCallback, long timeout) {
        rocketMqTemplate.asyncSend(destination, message, sendCallback, timeout);
    }
}

따라서 마이크로서비스 그룹이 캡슐화한 클라이언트가 기본적으로 메시지 추적을 활성화했을 것이라고 추측했고, 예상대로였습니다. 마이크로서비스 그룹에 연락하여 이 파라미터를 설정 가능하도록 변경하거나 브로커 설정에서 traceTopicEnable을 true로 설정해야 합니다.

권장 사항

RocketMQ 클라이언트 자체 로그는 몇 초마다 라우팅 정보를 새로 고치므로 매우 방대합니다. RocketMQ 클라이언트의 로그 레벨을 낮추는 것이 좋습니다. 소스 코드에서 로그 레벨이 어떻게 설정되는지 확인할 수 있습니다:

// ClientLogger 클래스 내부에서 logLevel 설정 확인
// 기본적으로 INFO 레벨로 설정되어 있음

설정 파일에서 로그 레벨과 경로를 지정할 수 있습니다:

rocketmq:
  client:
    logRoot: ./logs/rmq
    logLevel: ERROR
    logFileName: rocketmq-client.log

요약

  • 이 문제의 근본 원인은 서버가 메시지 추적을 지원하지 않는데 클라이언트에서 enableMsgTrace=true로 설정했기 때문입니다.
  • 서버와 클라이언트가 서로 다른 팀에서 관리하고, 비즈니스 팀의 문제 설명이 명확하지 않아 문제 조사 과정이 어려웠습니다. 향후 문제를 더 빠르게 식별하려면 클라이언트 설정 및 사용법을 숙지하는 것이 중요합니다.

태그: RocketMQ 메시지추적 topic 라우팅 오류 enableMsgTrace No topic route info

6월 18일 19:39에 게시됨