잘못된 Kafka 이벤트 처리와 재전달 관찰

빅 데이터Beginner
지금 연습하기

소개

정상적으로 동작한 컨슈머는 파일을 쓴 뒤 읽기 진행 상태를 커밋했습니다. 이 둘은 별도 작업입니다. 이 실습에서는 두 가지 실패 사례를 확인합니다. 형식이 잘못된 JSON 레코드에서는 진행 상태가 이를 지나치기 전에 처리를 중지해야 하며, 쓰기 후 커밋 전에 종료된 프로세스는 같은 레코드를 다시 받을 수 있습니다.

먼저 정상 전달을 확인하고 잘못된 레코드의 정확한 위치를 살펴본 뒤, 제공된 학습용 옵션으로 플러시와 커밋 사이에서 중지합니다. 같은 그룹을 재시작하면 파일의 동일한 이벤트 ID와 Kafka 위치를 통해 재전달을 확인할 수 있습니다. 중복 제거 기능이나 재시도 구조를 구현하지 않고 이 동작을 관찰합니다.

이 독립된 환경에는 파티션이 하나씩인 토픽 세 개와 이전 실습에서 익힌 전체 컨슈머가 준비됩니다. 추가된 의도적 종료 옵션도 이미 구현되어 있습니다. 앞서 배운 Python JSON, 파일, 수동 커밋 개념이 필요하며, 브로커 재설정이나 새 복구 아키텍처는 필요하지 않습니다. 터미널을 사용하세요.

정상 처리 경로 확인하기

이 단계에서는 문제를 진단하기 전에 유효한 입력에서 애플리케이션이 동작하는지 확인합니다. 각 토픽의 파티션이 하나이므로 레코드 위치를 바로 파악할 수 있습니다. orders-normal에는 유효한 레코드 두 개가 있습니다. 컨슈머는 앞서와 동일하게 명시적인 쓰기, 플러시, 동기 메시지 커밋, 종료 순서를 사용합니다.

의도적 종료 옵션을 포함한 제공 애플리케이션을 수정하지 말고 살펴보세요.

cd /home/labex/project
cat /home/labex/project/consumer.py

기본적으로 이 옵션은 꺼져 있습니다. 별도 그룹과 파일로 정상 레코드 두 개의 처리 경로를 실행하세요.

bigdata-python /home/labex/project/consumer.py --topic orders-normal --group normal-reader --output /home/labex/project/output/normal-delivery.jsonl --max-messages 2 --timeout-seconds 20 > /home/labex/project/output/normal.log 2>&1
cat /home/labex/project/output/normal.log
cat /home/labex/project/output/normal-delivery.jsonl

오프셋 0과 1에 normal1과 normal2가 있고, 두 쓰기 후 커밋된 다음 오프셋은 2여야 합니다. max-messages는 이번 실행에서 요청한 개수입니다. 추가 전용 파일에는 이후 단계의 여러 실행에서 관찰한 결과가 쌓일 수 있습니다.

이 정상 기준 결과를 통해 애플리케이션 전체의 연결 문제와 특정 레코드의 실패를 구분할 수 있습니다. 의도적인 실패에는 서로 다른 토픽, 그룹, 파일을 사용하여 증거를 명확하게 유지합니다.

잘못된 JSON을 지나쳐 커밋하지 않고 중지하기

이 단계에서는 앞쪽의 유효한 레코드, 잘못된 JSON 레코드, 뒤쪽의 유효한 레코드를 확인합니다. 로컬 증거를 읽어보세요.

cat /home/labex/project/data/bad-events.txt

orders-bad의 파티션 0에서 앞쪽의 유효한 레코드는 오프셋 0, 형식이 잘못된 텍스트는 오프셋 1, 뒤쪽의 유효한 이벤트는 오프셋 2에 있습니다. 정책은 즉시 실패입니다. 잘못된 위치를 보고하고 종료합니다. 오류를 잡은 뒤 계속 진행하여 같은 파티션의 뒤쪽 레코드까지 커밋하지 마세요.

레코드 세 개의 전달을 요청하세요. 다음 명령은 실패하는 것이 정상입니다.

bigdata-python /home/labex/project/consumer.py --topic orders-bad --group bad-reader --output /home/labex/project/output/bad-prefix.jsonl --max-messages 3 --timeout-seconds 20 > /home/labex/project/output/bad-first.log 2>&1
printf '%s\n' "$?" > /home/labex/project/output/bad-first-exit.txt
cat /home/labex/project/output/bad-first-exit.txt
cat /home/labex/project/output/bad-first.log
cat /home/labex/project/output/bad-prefix.jsonl

0이 아닌 상태와 topic=orders-bad partition=0 offset=1을 가리키는 Decode failure가 예상됩니다. 파일에는 오프셋 0의 bad-prefix만 있어야 합니다. 성공한 쓰기가 커밋되었으므로 그룹의 다음 오프셋은 1입니다. 잘못된 레코드와 뒤쪽 레코드는 처리되지 않은 상태로 남습니다.

같은 그룹을 재시작하고 같은 파일에 추가하세요. 다음 명령은 실패하는 것이 정상입니다.

bigdata-python /home/labex/project/consumer.py --topic orders-bad --group bad-reader --output /home/labex/project/output/bad-prefix.jsonl --max-messages 3 --timeout-seconds 20 > /home/labex/project/output/bad-retry.log 2>&1
printf '%s\n' "$?" > /home/labex/project/output/bad-retry-exit.txt
cat /home/labex/project/output/bad-retry-exit.txt
cat /home/labex/project/output/bad-retry.log
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group bad-reader --describe

같은 오프셋 1에서 다시 실패해야 합니다. 다음 위치 1이 이미 커밋되었으므로 앞쪽의 유효한 레코드를 두 번 쓰면 안 됩니다. 뒤쪽의 유효한 이벤트도 파일에 나타나면 안 됩니다. 이 결과로 정책을 확인할 수 있습니다. 운영자는 잘못된 데이터를 어떻게 처리할지 결정해야 하며, 이 최소 애플리케이션은 이를 조용히 건너뛰거나 지나쳐 커밋하지 않습니다.

쓰기 성공 후 커밋 전에 종료하기

이 단계에서는 준비된 orders-replay 토픽으로 다른 장애 가능 구간을 만듭니다. 이 토픽에는 오프셋 0의 repeat1만 있습니다. 학습용 --exit-before-commit 옵션은 파일 플러시 직후, 명시적인 커밋 전에 상태 73으로 종료합니다. 실제 위치를 기록하고 자동 커밋이 비활성화된 상태에서 컨슈머도 닫습니다.

새 그룹과 별도 파일을 사용하세요. 다음 명령은 실패하는 것이 정상입니다.

bigdata-python /home/labex/project/consumer.py --topic orders-replay --group replay-reader --output /home/labex/project/output/redelivery.jsonl --max-messages 1 --timeout-seconds 20 --exit-before-commit > /home/labex/project/output/before-commit-exit.log 2>&1
printf '%s\n' "$?" > /home/labex/project/output/before-commit-exit-status.txt
cat /home/labex/project/output/before-commit-exit-status.txt
cat /home/labex/project/output/before-commit-exit.log
cat /home/labex/project/output/redelivery.jsonl

상태 73과 파티션 0, 오프셋 0의 repeat1 봉투 객체 하나가 저장되어 있어야 합니다. 로그에는 CONTROLLED_EXIT_AFTER_WRITE_BEFORE_COMMIT가 표시됩니다. 제공된 장애 시연 기능이므로 직접 구현할 필요는 없습니다.

이제 파일에는 레코드가 있지만 그룹에는 해당 레코드의 커밋 위치가 없습니다. 따라서 같은 그룹을 다시 시작하면 앞서 배운 시작 정책 규칙이 적용됩니다. 다음 단계에서는 바로 이 분리된 구간을 관찰합니다.

재시작하고 반복 전달된 Kafka 레코드 식별하기

이 단계에서는 학습용 옵션 없이 replay-reader를 재시작합니다. 커밋 위치가 없고 earliest 정책을 사용하므로 보관된 레코드를 다시 읽을 수 있습니다. 두 전달 결과를 모두 볼 수 있도록 추가 모드와 같은 출력 파일을 유지하세요.

bigdata-python /home/labex/project/consumer.py --topic orders-replay --group replay-reader --output /home/labex/project/output/redelivery.jsonl --max-messages 1 --timeout-seconds 20 > /home/labex/project/output/after-restart.log 2>&1
cat /home/labex/project/output/after-restart.log
cat /home/labex/project/output/redelivery.jsonl
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group replay-reader --describe

event_id, key, value, topic, partition, offset이 모두 같은 봉투 객체 두 개가 있어야 합니다. 원래 브로커 레코드는 여전히 하나뿐입니다. 두 번째 실행은 다음 오프셋 1을 성공적으로 커밋하고, 파일에는 두 전달의 증거가 모두 남습니다.

다음 증거로 흔한 상황을 구분하세요.

관찰 내용 먼저 확인할 증거
새 이벤트 없음 유효한 토픽과 끝에 도달한 그룹 진행 상태를 확인합니다. 제한 시간 동안 폴링해도 레코드가 없을 수 있습니다
잘못된 토픽 이름 입력한 이름을 토픽 목록/상세 정보와 비교합니다. 이 클라이언트는 우발적인 토픽 생성을 비활성화합니다
유효한 커밋 위치 없음 그룹 진행 상태와 설정된 earliest 시작 정책을 확인합니다. 보관된 기록을 읽을 수 있습니다
디코딩 실패 보고된 토픽/파티션/오프셋과 값의 증거를 읽습니다. 그룹은 해당 잘못된 위치를 넘기기 전에 중지합니다

파일 쓰기와 Kafka 위치 커밋은 하나의 트랜잭션이 아닙니다. 실제 레코드 위치로 확인했듯 그 사이에 중단되면 재전달이 발생할 수 있습니다. 이 실습은 두 전달 결과를 모두 유지하며 중복 제거, 데드 레터 큐, 트랜잭션 설계를 구현하지 않습니다. 마지막 도전 과제에서는 명시적인 초기 상태 재시도 절차와 함께 정상 경로를 적용합니다.

요약

이 실습에서는 정상 전달을 확인하고 잘못된 JSON을 지나쳐 커밋하지 않고 중지했으며, 플러시되었지만 커밋되지 않은 레코드가 재시작 후 다시 전달되는 모습을 관찰했습니다. 일치하는 이벤트 내용과 Kafka 위치를 통해 두 번째 브로커 이벤트가 생긴 것이 아니라 파일에 같은 레코드의 관찰 결과가 중복되었음을 설명했습니다. 마지막 도전 과제에서는 새 데이터에 정상 발행, 제한된 소비, 파일 출력, 쓰기 후 커밋 동작을 함께 적용합니다.