16 KiB
title, source_type, url, archive_url, related_branches, related_projects, tags, created
| title | source_type | url | archive_url | related_branches | related_projects | tags | created | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| official-doc / Spring for Apache Kafka — Message Listener Containers (AckMode & Concurrency) | official-doc | https://docs.spring.io/spring-kafka/reference/kafka/receiving-messages/message-listener-container.html |
|
|
|
2026-07-28 |
official-doc / Spring for Apache Kafka — Message Listener Containers (AckMode & Concurrency)
Layer:
raw/— 외부 자료(공식 문서)의 원문 발췌·출처 기록. 검증된 요약은/ingest후wiki/concepts/에 별도 작성. 원본은 raw에 영구 보관.
Parent / 활용 branch
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| raw/branch-notes/feature-kafka-consumer-inbox-contract | manual acknowledgement 메커니즘(D3 — "application use case 성공 + DB 트랜잭션 커밋 이후에만 ack")을 ContainerProperties.AckMode 층에서 무엇으로 표현하는지의 근거. AckMode.MANUAL/MANUAL_IMMEDIATE 정의·Acknowledgment 호출 제약(nack() 스레드 제약, acknowledge(index) 리스너 스레드 제약)·ConcurrentMessageListenerContainer 의 concurrency 대 파티션 수 하향 조정 규칙을 확인 |
출처
- 원본 URL: https://docs.spring.io/spring-kafka/reference/kafka/receiving-messages/message-listener-container.html
- 아카이브 URL: (미제공)
- 저자 / 조직: Spring team (Broadcom / VMware Tanzu — Spring for Apache Kafka reference)
- 문서 버전: Spring for Apache Kafka reference 4.1.0 (페이지 상단 breadcrumb "Spring for Apache Kafka 4.1.0" 확인). 페이지 자체의 별도 발행일 표기는 없고 각 기능 옆에 도입 버전만 표기됨(예: AckMode 관련
enable.auto.commit강제 false 는 2.3부터,acknowledge(index)부분 배치 커밋은 3.0.10부터). - 발행일: 명시 없음 (버전 이력만 본문에 표기)
- 마지막 확인일: 2026-07-28
왜 저장했는지
ca-skeleton kafka consumer inbox 브랜치(feature-kafka-consumer-inbox-contract)의 D3(수동 ack, DB 커밋 이후에만 offset 커밋) 결정을 Spring Kafka 컨테이너 API 레벨의 AckMode 값과 Acknowledgment 인터페이스 계약으로 정당화하기 위함. 아울러 ConcurrentMessageListenerContainer 의 concurrency 가 파티션 수를 넘길 때 어떻게 처리되는지(D4/D6 의 "파티션당 컨슈머 1개" 전제와 인접)도 이 페이지에서 확인.
요청받은 5개 확인 대상 중 이 URL 페이지에 실제로 있는 것은 2개(AckMode 열거·MANUAL/MANUAL_IMMEDIATE 정의, concurrency 하향 조정)뿐이다. 나머지 3개(① Acknowledgment 를 어느 스레드에서 호출해야 하는지의 "calling consumer thread ... otherwise queued" 계열 문장, ② ack 순서 제약 "must be acknowledged in order ... does not maintain state for each record" 계열 문장, ③ asyncAcks/out-of-order ack 의 pause·중복 전달 trade-off 문장)은 self-grep 결과 이 페이지에 없음을 확인했다(아래 ## 메모 참조). 1 dispatch = 1 URL 원칙에 따라 다른 페이지 내용을 끌어와 대신 인용하지 않았다.
핵심 인용
원문 그대로. 이 페이지는 "Message Listener Containers" 단일 페이지이며 그 안에 "Committing Offsets" 소제목이 있다. 하위 번호 섹션이 없어 위치는 fetched text 의 line 번호로 표기한다 (self-grep 참조).
[Committing Offsets] "MANUAL: The message listener is responsible to acknowledge() the Acknowledgment." (line 447)
[Committing Offsets] "MANUAL_IMMEDIATE: Commit the offset immediately when the Acknowledgment.acknowledge() method is called by the listener." (line 450)
[Committing Offsets] "MANUAL and MANUAL_IMMEDIATE require the listener to be an AcknowledgingMessageListener or a BatchAcknowledgingMessageListener." (line 454)
[Committing Offsets] "The default AckMode is BATCH." (line 429)
[Committing Offsets] "nack() can only be called on the consumer thread that invokes your listener." (line 480)
[Committing Offsets — partial batch commit] "The method must be called on the listener thread" (line 507,
acknowledge(index)제약 목록 중 하나)
[Using ConcurrentMessageListenerContainer] "If the concurrency is greater than the number of TopicPartitions, the concurrency is adjusted down such that each container gets one partition." (line 412)
Claims Extracted
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| SPRK-ACKMODE-C1 | AckMode.MANUAL 은 리스너가 Acknowledgment.acknowledge() 를 호출할 책임을 지며, 그 이후엔 BATCH 와 동일한 커밋 시맨틱(poll() 이 반환한 레코드 전체 처리 후 커밋)이 적용된다 |
"MANUAL: The message listener is responsible to acknowledge() the Acknowledgment." + (같은 항목의 다음 문장, self-grep 대상은 아니나 문맥) "After that, the same semantics as BATCH are applied." | official-vendor-doc |
ca-skeleton D3 의 "use case 성공 이후에만 ack" 를 표현할 AckMode 값이 MANUAL 임을 확인 |
acknowledge() 를 언제 호출해야 하는지(DB 커밋 성공 이후)는 애플리케이션 책임이지 프레임워크가 강제하지 않음 — 순서를 지키는 코드는 리뷰/계약 테스트로 별도 보장해야 함(branch note 의 §검증해야 할 주장 이미 인지) |
| SPRK-ACKMODE-C2 | AckMode.MANUAL_IMMEDIATE 는 Acknowledgment.acknowledge() 호출 즉시 offset 을 커밋한다 — MANUAL 의 배치형 커밋과 달리 호출마다 즉시 커밋 |
"MANUAL_IMMEDIATE: Commit the offset immediately when the Acknowledgment.acknowledge() method is called by the listener." | official-vendor-doc |
MANUAL vs MANUAL_IMMEDIATE 선택 — ca-skeleton D3 는 "커밋 시점"만 결정했고 이 자료는 두 모드가 커밋 빈도(배치 대 즉시)에서 갈린다는 것을 보여줄 뿐, 어느 쪽이 D3 의 의도에 더 맞는지는 이 자료만으로 결정 불가 |
두 모드의 처리량/지연 trade-off (벤치마크 없음). acknowledge(index) 부분 배치 커밋(SPRK-ACKMODE-C6)은 MANUAL_IMMEDIATE 를 요구한다는 점에서 이 claim 과 연결되나 별도 claim으로 분리 |
| SPRK-ACKMODE-C3 | MANUAL/MANUAL_IMMEDIATE 를 쓰려면 리스너가 AcknowledgingMessageListener 또는 BatchAcknowledgingMessageListener 를 구현해야 한다 |
"MANUAL and MANUAL_IMMEDIATE require the listener to be an AcknowledgingMessageListener or a BatchAcknowledgingMessageListener." | official-vendor-doc |
ca-skeleton consumer 구현 시 리스너 인터페이스 선택 제약 — Acknowledgment 를 받으려면 이 인터페이스 중 하나를 구현해야 함(구체 클래스/메서드 명명 자체는 이 자료 범위 밖, §구현 가이드 1 의 UNSUPPORTED_IMPL_DECISION 대상) |
어느 인터페이스(단건 vs 배치)를 골라야 하는지는 D4 의 처리 단위 결정에 종속 — 이 자료는 존재 요건만 말함 |
| SPRK-ACKMODE-C4 | AckMode 를 명시하지 않으면 기본값은 BATCH 이지 MANUAL 이 아니다 |
"The default AckMode is BATCH." | official-vendor-doc |
ca-skeleton 이 D3 의 수동 ack 계약을 실제로 적용하려면 AckMode 를 명시적으로 MANUAL (또는 MANUAL_IMMEDIATE) 로 설정해야 함 — 설정을 빠뜨리면 조용히 BATCH(자동 그룹 커밋)로 동작 |
BATCH 자체가 언제 위험한지(자동 커밋과 유사한 문제)는 이 자료가 직접 논하지 않음 — 이미 수집한 kafka-consumer-offset-commit-semantics-apache-javadoc 의 자동 커밋 위험 논의가 별도 근거 |
| SPRK-ACKMODE-C5 | nack()(2.3 부터 추가된 Acknowledgment 메서드)은 리스너를 호출한 그 consumer 스레드에서만 호출할 수 있다 |
"nack() can only be called on the consumer thread that invokes your listener." | official-vendor-doc |
Acknowledgment 인터페이스의 적어도 한 메서드(nack)가 리스너/consumer 스레드에 강하게 묶여 있다는 것을 확인 — "별도 워커 스레드에서 ack 관련 호출을 해도 되는가"라는 branch 의 질문에 nack() 에 한해서는 "아니오" 라고 명시적으로 답함 |
acknowledge() 자체(단건 리스너, MANUAL 모드)에 동일한 스레드 제약이 있다고 이 문장만으로 단정할 수 없다 — 이 페이지가 acknowledge() 에 대해 그런 문장을 쓰는 곳은 SPRK-ACKMODE-C6(부분 배치 커밋 변형)뿐이며, 그건 다른 API(acknowledge(index), 배치 리스너, MANUAL_IMMEDIATE 한정)다. 사용자가 원한 "calling consumer thread ... otherwise queued" 문장은 이 페이지에 없음(미발견, 아래 Usage Boundaries) |
| SPRK-ACKMODE-C6 | 3.0.10 부터 추가된 배치 리스너의 부분 배치 커밋(acknowledge(index))은 AckMode.MANUAL_IMMEDIATE 를 요구하고, "리스너 스레드에서 호출되어야 한다" 는 제약을 명시적으로 건다(그 외에도 List 소비 요건, index 범위·단조 증가 요건이 있으며 위반 시 IllegalArgumentException/IllegalStateException) |
"The method must be called on the listener thread" (같은 목록의 다른 제약: "AckMode.MANUAL_IMMEDIATE is required") | official-vendor-doc |
acknowledge(index) 부분 배치 커밋을 쓸 경우의 스레드 제약 — 이 자료가 제공하는 가장 근접한 "Acknowledgment 관련 호출은 리스너/consumer 스레드에서" 근거 |
단건 레코드 리스너의 일반 acknowledge() 호출(MANUAL 모드, 배치 아님)에도 동일 제약이 명시돼 있는지는 이 페이지에서 확인 불가 — 그 문장은 별도 페이지("Manually Committing Offsets")에 있을 가능성이 있으나 본 dispatch 범위 밖 |
| SPRK-ACKMODE-C7 | ConcurrentMessageListenerContainer 의 concurrency 가 배정 가능한 TopicPartition 수보다 크면, 각 delegate 컨테이너가 파티션 하나씩만 갖도록 concurrency 가 하향 조정된다 |
"If the concurrency is greater than the number of TopicPartitions, the concurrency is adjusted down such that each container gets one partition." | official-vendor-doc |
ca-skeleton 의 concurrency 설정 상한 근거 — 파티션 수를 넘는 concurrency 값은 무의미(초과분 컨테이너가 유휴 상태로 남지 않고 애초에 만들어지지 않음). D4/D6 이 전제하는 "파티션 하나는 컨슈머 그룹 안에서 한 컨테이너만 소비"라는 가정과 정합되는 Spring 프레임워크 레벨 동작 | 이 문장은 Spring 컨테이너의 파티션 분배 동작을 말할 뿐, Kafka 프로토콜 레벨에서 "파티션 하나는 그룹 내 정확히 한 consumer 가 소비한다"는 것 자체의 공식 Kafka 문서 근거(branch SOURCE_GAP-1, needs-confirmation)를 대신하지 않는다 — Spring 이 이렇게 동작하는 것은 그 Kafka 레벨 보장을 전제로 구현했을 뿐, 이 문서가 Kafka 프로토콜 자체를 규정하는 문서는 아님 |
Strength 참고
모든 claim 은 official-vendor-doc (Spring 공식 reference — RFC/IETF 표준은 아니므로 official-standard 로 격상하지 않음).
Usage Boundaries
- 이 자료가 직접 증명하는 것:
SPRK-ACKMODE-C1~C4:AckMode값의 열거와 커밋 시점 정의, 기본값이BATCH라는 것,MANUAL/MANUAL_IMMEDIATE사용 시 리스너 인터페이스 요건SPRK-ACKMODE-C5:nack()은 consumer/리스너 스레드에서만 호출 가능SPRK-ACKMODE-C6:acknowledge(index)(부분 배치 커밋,MANUAL_IMMEDIATE한정)는 리스너 스레드에서 호출돼야 한다는 명시 제약SPRK-ACKMODE-C7:concurrency> 파티션 수일 때 하향 조정
- 이 자료가 증명하지 않는 것 (branch 가 요청했으나 이 URL 페이지에서 미발견 — self-grep 결과 0건):
Acknowledgment.acknowledge()(단건 레코드 리스너,MANUAL모드) 를 어느 스레드에서 호출해야 하는지에 대한 일반 규칙 — 사용자가 지정한 "commit will be performed immediately if the Acknowledgment is acknowledged on the calling consumer thread; otherwise, the acks will be queued" 계열 문장은grep -nF로 "calling consumer thread"/"queued" 검색 시 이 페이지에서 0건.nack()(C5)과acknowledge(index)(C6)에 대해서만 스레드 제약이 명시돼 있고, 일반acknowledge()에 대한 동일 문장은 이 페이지 범위 밖(다른 페이지 "Manually Committing Offsets" 가능성 — 본 dispatch 는 여기서 멈춤, 1 dispatch = 1 URL)- ack 순서 제약 — "acknowledgments must be acknowledged in order, because Kafka does not maintain state for each record, only a committed offset for each group/partition" 계열 문장 —
grep -nF로 "does not maintain state"/"in order" 검색 시 미발견(단, "Out of Order Commits" 라는 기능명 자체는 이 페이지에 1회 등장 — line 482, 483:nack()은 Out of Order Commits 사용 시 허용 안 됨. 이 기능의 정의 문장 자체는 이 페이지에 없음) asyncAcks/out-of-order ack 채택 시 consumer 가 pause 되고 중복 전달 가능성이 커진다는 trade-off 문장 —grep -ni "asyncAck"결과 0건- 파티션 단위 pause/resume API — 이미 별도 raw 문서(raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure
SPRK-PAUSE-C5)에서 동일하게 미발견 확인됨 commitSync/commitAsync중 어느 것을 쓸지의 권고 (이 페이지는syncCommits컨테이너 프로퍼티가 그 둘을 스위칭한다는 것만 언급하고 권고는 안 함 — 이미 branch D3 의 Open Risk 로 기록됨)
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
Acknowledgment.acknowledge()의 일반적 스레드 제약(있다면)을 확인하려면 "Manually Committing Offsets" 페이지(Message Listeners섹션의 sibling 페이지, 이 문서 최하단 nav 에서 확인됨)를 별도wiki-source-summarizerdispatch 로 조사해야 한다- ack 순서 제약과
asyncAckstrade-off 도 마찬가지로 별도 페이지 조사 필요 — 이 페이지에 없다고 해서 Spring Kafka 문서 전체에 없다는 뜻은 아님
메모
나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
- self-grep 실행 결과 (fetched text
/tmp/.../scratchpad/source-fetch-20260728-193000.txt, 658줄):calling consumer thread→ 0건queued→ 0건does not maintain state→ 0건asyncAck(대소문자 무관) → 0건Out of Order Commits→ 1건 (line 482, 기능명만 등장 — 정의 문장 없음)
- 이 페이지 자체가 "Committing Offsets" 절 안에서
AckMode7종(RECORD/BATCH/TIME/COUNT/COUNT_TIME/MANUAL/MANUAL_IMMEDIATE)을 모두 정의하지만, 이 raw 문서에는 branch 의 관심사(MANUAL/MANUAL_IMMEDIATE 대비, 스레드, concurrency)에 직접 관련된 것만 발췌했다. RECORD/BATCH/TIME/COUNT/COUNT_TIME 의 정의 자체도 필요해지면 이 문서에 claim 추가만으로 확장 가능(재-fetch 불필요, 같은 텍스트 파일에 이미 있음). - 추가로 봐야 할 동일 출처 페이지: 사이트 nav 에서 확인된 "Manually Committing Offsets" (이 페이지 바로 다음 sibling,
Message Listeners섹션 하위) —Acknowledgment.acknowledge()스레드 규칙과 ack 순서 제약이 있을 가능성이 높음. 별도 URL, 별도 dispatch 필요.
관련
- 같은 주제 다른 official-doc:
[[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]](같은 "Message Listener Containers" 상위 카테고리, pause/resume 근거),[[raw/official-docs/kafka-consumer-offset-commit-semantics-apache-javadoc]](Kafka 레벨 offset commit 시맨틱),[[raw/official-docs/kafka-incremental-cooperative-rebalance-kip429]](concurrency/rebalance 인접 주제) - 이 자료를 인용한 wiki 요약:
[[wiki/concepts/...]](생성 시)