Kafka 컨슈머 오프셋 추적과 이벤트 재생

빅 데이터Beginner
지금 연습하기

소개

Kafka는 누가 읽었는지와 관계없이 레코드를 보관합니다. 컨슈머 그룹은 이후 프로세스가 처음부터 시작하지 않고 이어서 읽도록 진행 상태도 기억해야 합니다. 커밋된 오프셋은 마지막으로 읽은 레코드의 번호가 아니라, 해당 그룹이 파티션에서 다음에 읽어야 할 위치입니다.

준비된 레코드 네 개 중 두 개를 읽고 커밋된 진행 상태와 지연을 확인한 다음, 같은 그룹으로 읽기를 재개하고 새 그룹과 비교합니다. 이후 원래 그룹이 비활성 상태일 때 초기화 결과를 미리 보고 실행하여 보관된 레코드를 다시 읽습니다. 위치를 쉽게 이해할 수 있도록 토픽의 파티션은 하나입니다.

이 독립된 실습에는 브로커, 레코드 네 개, 예상 오프셋이 나타날 때까지 실제 코디네이터 상태를 반복 확인하는 도우미가 제공됩니다. 기본적인 터미널 사용법이면 충분하며 Python 코딩이나 서비스 설정은 필요하지 않습니다. 도우미는 오프셋을 바꾸거나 보고서를 만들어 내지 않습니다. 터미널을 사용하세요.

레코드 두 개를 읽고 커밋된 다음 위치 확인하기

이 단계에서는 개수를 제한한 읽기와 그룹의 실제 저장 위치를 연결해 봅니다. 준비된 값 네 개를 확인하세요.

cd /home/labex/project
cat /home/labex/project/data/progress-events.txt

파티션이 하나인 이 새 토픽에서 각 값의 오프셋은 0부터 3까지입니다. orders-review라는 새 그룹에는 저장된 위치가 없으므로, --from-beginning이 첫 읽기의 시작 위치를 보관된 맨 앞으로 정합니다.

--command-property는 이벤트 표시 형식을 바꾸는 대신 컨슈머 클라이언트에 설정값을 전달합니다. 이 실습에서는 콘솔 클라이언트가 진행 상태를 자동으로 커밋합니다. max.poll.records=1은 한 번의 폴링에서 반환되는 레코드를 하나로 제한합니다. 따라서 두 레코드를 표시한 뒤 중지할 때 더 큰 반환 묶음까지 위치가 앞서 나가지 않습니다. 커밋 간격은 주기적인 커밋 동작을 설정하며, 클라이언트가 종료될 때도 정상적인 진행 상태 처리를 마칩니다. 특정 지연 시간을 가정하지 않고 실제 결과를 확인합니다.

kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic orders-progress --group orders-review --from-beginning --max-messages 2 --timeout-ms 30000 --command-property enable.auto.commit=true --command-property auto.commit.interval.ms=1000 --command-property max.poll.records=1 > /home/labex/project/output/first-two.txt
cat /home/labex/project/output/first-two.txt

created 이벤트 두 개가 출력된 뒤 클라이언트가 종료됩니다. 그룹 이름과 예상되는 다음 오프셋을 인수로 전달해 제공된 도우미를 실행하세요. 도우미는 제한된 시간 동안 반복 확인합니다.

/home/labex/project/tools/wait-group-offset orders-review 2 > /home/labex/project/output/after-first.txt
cat /home/labex/project/output/after-first.txt

도우미는 커밋 오프셋이 2가 될 때까지 실제 그룹 정보를 반복 조회하며, 제한 시간을 넘기면 실패합니다. 직접 커밋하거나 초기화하지는 않습니다. 파티션 0에서 CURRENT-OFFSET 2, LOG-END-OFFSET 4, LAG 2를 찾으세요. 제한된 읽기를 마친 클라이언트가 종료된 뒤 그룹에 활성 멤버가 없다는 메시지가 나오는 것은 정상입니다. 저장된 진행 상태는 여전히 존재합니다.

오프셋 2는 다음 읽기가 저장된 세 번째 레코드에서 시작한다는 뜻입니다. 끝 오프셋 4는 기존 레코드 네 개 바로 다음의 위치입니다. 데이터 변동이 없는 이 간단한 예에서 지연은 4 − 2 = 2로, 남은 레코드 수를 나타냅니다. 처음 두 레코드는 삭제되지 않았습니다.

같은 그룹으로 재개하고 새 그룹과 비교하기

이 단계에서는 그룹이 저장된 위치에서 어떤 레코드를 받게 될지 예상합니다. 처음부터 읽는 옵션을 의도적으로 유지한 채 같은 그룹을 다시 실행하세요.

kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic orders-progress --group orders-review --from-beginning --max-messages 2 --timeout-ms 30000 --command-property enable.auto.commit=true --command-property auto.commit.interval.ms=1000 --command-property max.poll.records=1 > /home/labex/project/output/resumed-two.txt
cat /home/labex/project/output/resumed-two.txt

앞서 읽은 created 이벤트 대신 paid 이벤트 두 개가 출력됩니다. --from-beginning은 유효한 커밋 오프셋이 없을 때 시작 정책을 제공합니다. 이 그룹에 유효하게 저장된 오프셋 2를 덮어쓰지는 않습니다. 실제 코디네이터 상태로 완료 여부를 확인하세요.

/home/labex/project/tools/wait-group-offset orders-review 4 > /home/labex/project/output/after-resume.txt
cat /home/labex/project/output/after-resume.txt

커밋 오프셋 4, 끝 오프셋 4, 지연 0이 예상됩니다. 이제 orders-independent라는 다른 그룹의 결과를 예상해 보세요. 저장된 위치가 없고 orders-review와 독립적이므로, 처음부터 읽으면 보관된 레코드 네 개가 모두 반환되어야 합니다.

kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic orders-progress --group orders-independent --from-beginning --max-messages 4 --timeout-ms 30000 --command-property enable.auto.commit=true --command-property auto.commit.interval.ms=1000 --command-property max.poll.records=1 > /home/labex/project/output/new-group-all.txt
cat /home/labex/project/output/new-group-all.txt
/home/labex/project/tools/wait-group-offset orders-independent 4 > /home/labex/project/output/new-group-position.txt
cat /home/labex/project/output/new-group-position.txt

값 네 개가 모두 출력되고 새 그룹 자체의 커밋 오프셋이 4가 됩니다. 두 그룹은 서로 다른 읽기 이력을 거쳐 모두 끝에 도달할 수 있습니다. 한 그룹의 진행 상태가 다른 그룹의 시작 위치를 옮기지 않았으며, 어느 그룹의 소비도 토픽 레코드를 삭제하지 않았습니다.

다음으로 시작 옵션을 되감기 명령처럼 사용하지 않고, 원래 그룹의 저장 위치를 의도적으로 변경합니다.

비활성 그룹 초기화를 미리 보고 기록 재생하기

이 단계에서는 orders-review만 초기화합니다. 제한된 읽기를 수행한 컨슈머가 종료되어 그룹에 활성 멤버가 없습니다. 실수로 같은 그룹의 멤버를 실행해 둔 경우 초기화 전에 중지하세요. 활성 컨슈머가 계속 진행 상태를 바꿀 수 있기 때문입니다.

--reset-offsets는 진행 상태 관리 작업을 선택하고, --topic은 범위를 제한하며, --to-earliest는 보관된 맨 앞을 선택합니다. 먼저 --dry-run으로 적용하지 않고 제안된 새 오프셋을 미리 확인하세요.

kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group orders-review --reset-offsets --to-earliest --topic orders-progress --dry-run > /home/labex/project/output/reset-preview.txt
cat /home/labex/project/output/reset-preview.txt

제안된 NEW-OFFSET은 0이어야 합니다. 미리 보기로 현재 저장된 위치가 바뀌지 않았는지 확인하세요.

/home/labex/project/tools/wait-group-offset orders-review 4 > /home/labex/project/output/after-preview.txt
cat /home/labex/project/output/after-preview.txt

이제 --execute로 지정된 범위의 초기화를 적용하세요.

kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group orders-review --reset-offsets --to-earliest --topic orders-progress --execute
/home/labex/project/tools/wait-group-offset orders-review 0 > /home/labex/project/output/after-reset.txt
cat /home/labex/project/output/after-reset.txt

이제 실제로 커밋된 다음 오프셋은 0, 끝 오프셋은 4, 지연은 4여야 합니다. 레코드는 그대로 있고 이 그룹의 저장 위치만 바뀌었습니다. 시작 기본값 옵션에 의존하지 않고, 이제 유효하게 커밋된 맨 앞 위치를 사용해 네 개를 모두 다시 읽으세요.

kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic orders-progress --group orders-review --max-messages 4 --timeout-ms 30000 --command-property enable.auto.commit=true --command-property auto.commit.interval.ms=1000 --command-property max.poll.records=1 > /home/labex/project/output/replayed-all.txt
cat /home/labex/project/output/replayed-all.txt
/home/labex/project/tools/wait-group-offset orders-review 4 > /home/labex/project/output/after-replay.txt
cat /home/labex/project/output/after-replay.txt

원래 값 네 개가 출력되고 커밋 오프셋이 다시 4가 됩니다. orders-independent는 자체 완료 위치를 그대로 유지해야 합니다. 초기화가 해당 그룹을 되감지는 않았습니다.

재생은 기존 레코드를 의도적으로 다시 읽는 작업입니다. 레코드 처리와 진행 상태 커밋 사이에 장애가 발생해도 레코드가 다시 전달될 수 있습니다. 이 콘솔 실습은 읽기 위치를 보여 주며, 파일 쓰기와 커밋을 원자적으로 수행하는 트랜잭션이나 전체 처리 과정의 정확히 한 번 보장을 구현하지는 않습니다. 이후 Python 실습에서는 처리와 커밋의 순서를 명시적으로 다룹니다.

요약

이 실습에서는 커밋된 다음 오프셋과 지연을 확인하고, 처음부터 읽는 기본값이 있어도 저장된 진행 상태에서 재개하며 독립 그룹과 비교했습니다. 비활성 그룹의 초기화를 미리 보고 실행하여 실제 토픽 레코드를 바꾸지 않고 의도적으로 재생했습니다. 다음으로 한 그룹의 여러 멤버가 파티션을 나누고, 멤버가 나간 뒤 할당을 복구하는 방식을 관찰합니다.