224 lines
9.1 KiB
Markdown
224 lines
9.1 KiB
Markdown
# 설정 레퍼런스
|
|
|
|
> **Prefix.** Every property below binds under `app.messaging`, which is the prefix the deployed
|
|
> runtime and the `APP_MESSAGING_*` environment variables already use. Earlier revisions of this
|
|
> page documented a bare `messaging` prefix and the starter bound `backend.messaging`; neither
|
|
> bound what this page describes, so a deployment configured from it changed nothing. A key under
|
|
> either of the old prefixes now fails startup with a message naming the key — see
|
|
> `MessagingPrefixMigrationValidator`.
|
|
|
|
> **이 페이지는 실행된다.** 아래 YAML 블록은 `MessagingConfigurationBindingTest`가 이 파일에서 직접
|
|
> 읽어 컨텍스트에 올린다. 문서가 설명하는 모양이 곧 바인딩되는 모양이라는 뜻이고, 문서를 고치면서
|
|
> 코드를 고치지 않으면 테스트가 깨진다. 이전 판은 destination·broker·security 세 섹션을 설명했지만
|
|
> 어떤 binder도 그것을 읽지 않았다 — 문서대로 설정한 배포는 아무것도 바뀌지 않았고 아무 말도 듣지
|
|
> 못했다 (MSG-008).
|
|
|
|
## Destination profile
|
|
|
|
```yaml
|
|
app:
|
|
messaging:
|
|
destinations:
|
|
order-events:
|
|
broker: kafka-primary
|
|
kind: EVENT_STREAM # ASYNC_COMMAND | DOMAIN_EVENT | INTEGRATION_EVENT
|
|
# | WORK_QUEUE | PUBLISH_SUBSCRIBE | EVENT_STREAM | REQUEST_REPLY
|
|
tier: M1 # M1 | M2 | M3
|
|
physical:
|
|
topic: order.events.v1
|
|
schema:
|
|
codec: application/json
|
|
compatibility: BACKWARD_TRANSITIVE
|
|
message-types: [order.created]
|
|
guarantees:
|
|
delivery: AT_LEAST_ONCE # AT_MOST_ONCE | AT_LEAST_ONCE
|
|
ordering: KEY # NONE | DESTINATION | PARTITION | KEY
|
|
external-side-effect: INBOX_TRANSACTIONAL
|
|
producer:
|
|
confirmation: REPLICATION_OR_PERSISTENCE_ACK
|
|
timeout: 5s
|
|
mandatory-routing: true
|
|
idempotent: true
|
|
consumer:
|
|
group: order-projection
|
|
concurrency: 1 # DESTINATION 순서를 요구하면 1이어야 한다
|
|
max-in-flight-per-ordering-unit: 1
|
|
prefetch: 16
|
|
handler-timeout: 30s
|
|
manual-settlement: false
|
|
retry:
|
|
mode: PAUSE_PARTITION # NONE | INLINE | BLOCKING | PAUSE_PARTITION
|
|
# | RETRY_DESTINATION | BROKER_DELAYED
|
|
max-attempts: 3
|
|
initial-delay: 200ms
|
|
max-delay: 2s
|
|
multiplier: 2.0
|
|
jitter: true
|
|
ordering-impact: PRESERVE # PRESERVE | ALLOW_REORDER
|
|
dlq:
|
|
destination: order-events-dlq
|
|
max-redrive-count: 1
|
|
payload:
|
|
max-bytes: 1048576
|
|
claim-check-threshold-bytes: 1048576
|
|
key-resolver-configured: true
|
|
production: false
|
|
topology-auto-create: false
|
|
order-events-dlq:
|
|
broker: kafka-primary
|
|
kind: WORK_QUEUE
|
|
physical:
|
|
topic: order.events.v1.dlt
|
|
schema:
|
|
message-types: [order.created]
|
|
```
|
|
|
|
`dlq.destination`이 가리키는 destination도 선언되어야 한다. 선언되지 않은 이름은 부팅 실패이며,
|
|
메시지가 갈 곳 없는 DLQ 설정이 조용히 통과하지 않는다. `retry.destination`과 `dlq.destination`이
|
|
섞여 만드는 순환(A의 retry가 B로, B의 dlq가 A로)도 하나의 그래프로 검사되어 경로와 함께 거절된다.
|
|
|
|
## 기본값
|
|
|
|
| 설정 | 기본값 | 근거 |
|
|
|---|---:|---|
|
|
| logical payload 최대 | 1,048,576 bytes | portability. 초과는 Claim Check |
|
|
| global hard 최대 | 8,388,608 bytes | 어떤 destination도 넘을 수 없는 상한 |
|
|
| header 총 크기 | 32,768 bytes | |
|
|
| header 개수 | 64 | |
|
|
| header key | 128 bytes | metric tag 안전 |
|
|
| header value | 4,096 bytes | |
|
|
| publish timeout | 5s | |
|
|
| handler timeout | 30s | |
|
|
| graceful shutdown drain | 30s | |
|
|
| 일반 destination retry | 0회 | 자동 retry는 opt-in |
|
|
| DLQ redrive batch | 100 | 한 번의 작업이 source를 덮치지 않게 |
|
|
| Outbox relay batch | 100 | |
|
|
| Outbox lease | 30s | |
|
|
| Outbox polling | 500ms | |
|
|
| metric dimension 상한 | 200 | cardinality 폭발 방지 |
|
|
|
|
`schema.codec`은 `application/json`, `schema.compatibility`는 `BACKWARD_TRANSITIVE`,
|
|
`guarantees.delivery`는 `AT_LEAST_ONCE`, `retry.mode`는 `NONE`이 기본값이다. 자동 retry가 기본으로
|
|
꺼져 있는 이유는 순서를 흐트러뜨리거나 비멱등 side effect를 두 번 실행하는 retry가 눈에 보이는
|
|
실패보다 나쁘기 때문이다.
|
|
|
|
## Broker profile
|
|
|
|
브로커는 `app.messaging.brokers` 아래에 한 번만 기술한다. `type`이 어느 계열의 설정이 적용되는지
|
|
결정하며, 다른 계열의 키(Kafka 항목의 `prefetch` 같은)는 무시되지 않고 부팅 실패로 거절된다 —
|
|
무시하면 그 줄을 쓴 사람은 무언가가 적용됐다고 믿게 된다.
|
|
|
|
### Kafka
|
|
|
|
```yaml
|
|
app:
|
|
messaging:
|
|
brokers:
|
|
kafka-primary:
|
|
type: kafka
|
|
stable: true
|
|
production: false
|
|
bootstrap-servers: [broker-1:9093, broker-2:9093]
|
|
enable-idempotence: true # stable에서 필수
|
|
acks: all # stable에서 필수
|
|
max-in-flight-requests-per-connection: 5 # 최대 5
|
|
delivery-timeout: 30s
|
|
enable-auto-commit: false # 항상 금지
|
|
consumer-group: order-projection
|
|
tls-enabled: false # production이면 필수
|
|
authentication-enabled: false # production이면 필수
|
|
```
|
|
|
|
### RabbitMQ
|
|
|
|
```yaml
|
|
app:
|
|
messaging:
|
|
brokers:
|
|
rabbit-primary:
|
|
type: rabbitmq
|
|
stable: true
|
|
production: false
|
|
addresses: [rabbit-1:5671]
|
|
publisher-confirms: true # stable에서 필수
|
|
publisher-returns: true # stable에서 필수
|
|
mandatory: true # stable에서 필수
|
|
confirm-timeout: 5s
|
|
auto-ack: false # 항상 금지
|
|
prefetch: 16
|
|
quorum-queues: true # durable work queue 필수
|
|
tls-enabled: false
|
|
authentication-enabled: false
|
|
```
|
|
|
|
`production: true`인 브로커는 `tls-enabled`와 `authentication-enabled`가 모두 참이어야 하고,
|
|
그렇지 않으면 `KafkaProfileValidator` / `RabbitProfileValidator`가 부팅을 거절한다. 위 예시가
|
|
`production: false`인 것은 이 페이지가 그대로 실행되는 fixture이기 때문이며, 실 배포는 셋 다 참이다.
|
|
|
|
## 보안
|
|
|
|
```yaml
|
|
app:
|
|
messaging:
|
|
security:
|
|
kafka-primary:
|
|
producer: { type: SASL_SCRAM, credential-id: kafka-producer }
|
|
consumer: { type: SASL_SCRAM, credential-id: kafka-consumer }
|
|
# admin은 application runtime에 설정하지 않는다
|
|
hostname-verification: true
|
|
access:
|
|
publishable: [order-events]
|
|
consumable: []
|
|
administrable: []
|
|
```
|
|
|
|
키는 `app.messaging.brokers`에 선언된 브로커 이름과 같아야 한다. `tls-enabled`와 `production`은
|
|
브로커 쪽에만 있고 여기에 중복되지 않는다 — 하나의 브로커가 두 곳에서 기술되면 두 값이 어긋나는
|
|
날이 오고, 어느 쪽이 이기는지는 아무도 모른다.
|
|
|
|
`credential-id`는 이름일 뿐이고 자격 증명 자체가 아니다. 실제 재료는 `CredentialProvider`가
|
|
연결 시점에 해석하므로, 설정 덤프나 힙 덤프에서 나오는 것은 이름뿐이다. producer와 consumer는
|
|
서로 다른 `credential-id`를 써야 하며, 같으면 부팅에 실패한다.
|
|
|
|
## Experimental / Optional
|
|
|
|
기본값은 전부 `false`다.
|
|
|
|
```yaml
|
|
app:
|
|
messaging:
|
|
experimental:
|
|
kafka-share: false
|
|
pulsar: false
|
|
nats: false
|
|
bridge:
|
|
spring-cloud-stream: false
|
|
```
|
|
|
|
## Backpressure
|
|
|
|
```yaml
|
|
app:
|
|
messaging:
|
|
backpressure:
|
|
global-limit: 512
|
|
per-destination-limit: 64 # global-limit 이하여야 한다
|
|
```
|
|
|
|
`per-destination-limit > global-limit`이면 global limit이 limit이 아니게 되므로 부팅에 실패한다.
|
|
|
|
## 바인딩되지 않는 키
|
|
|
|
섹션은 바인딩되는데 그 안의 키 하나가 오타인 경우는 접두사 오타와 달리 조용하다 — 섹션은 붙고,
|
|
플랫폼은 뜨고, 바꾸러 온 그 설정만 적용되지 않는다. `MessagingConfigurationKeyValidator`가
|
|
`app.messaging.destinations|brokers|security` 아래의 모든 키를 settings 레코드에서 파생한 목록과
|
|
대조하고, 없는 키는 그 키 이름을 담아 부팅을 거절한다.
|
|
|
|
허용 키 목록은 이 문서가 아니라 레코드에서 나온다. 문서에 목록을 적으면 필드가 추가된 날 그
|
|
목록이 틀리고, 오타를 잡으라고 만든 검사가 정상 필드를 거절하게 된다.
|
|
|
|
환경변수(`APP_MESSAGING_...`)는 이 검사의 대상이 아니다. `APP_MESSAGING_DESTINATIONS_ORDER_EVENTS_
|
|
CONSUMER_PREFETCH`에서 entry 이름과 leaf를 가르는 밑줄은 둘 안에 있는 밑줄과 구별되지 않으므로,
|
|
되돌려 쪼개려면 추측해야 한다. 여기서의 추측은 정상 배포를 거절하는 쪽으로 틀리며, 그것은 배포
|
|
매니페스트에 손으로 적어야 하는 변수에서 오타 하나를 놓치는 것보다 나쁘다.
|