Files

9.1 KiB

설정 레퍼런스

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

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.destinationdlq.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.codecapplication/json, schema.compatibilityBACKWARD_TRANSITIVE, guarantees.deliveryAT_LEAST_ONCE, retry.modeNONE이 기본값이다. 자동 retry가 기본으로 꺼져 있는 이유는 순서를 흐트러뜨리거나 비멱등 side effect를 두 번 실행하는 retry가 눈에 보이는 실패보다 나쁘기 때문이다.

Broker profile

브로커는 app.messaging.brokers 아래에 한 번만 기술한다. type이 어느 계열의 설정이 적용되는지 결정하며, 다른 계열의 키(Kafka 항목의 prefetch 같은)는 무시되지 않고 부팅 실패로 거절된다 — 무시하면 그 줄을 쓴 사람은 무언가가 적용됐다고 믿게 된다.

Kafka

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

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-enabledauthentication-enabled가 모두 참이어야 하고, 그렇지 않으면 KafkaProfileValidator / RabbitProfileValidator가 부팅을 거절한다. 위 예시가 production: false인 것은 이 페이지가 그대로 실행되는 fixture이기 때문이며, 실 배포는 셋 다 참이다.

보안

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-enabledproduction은 브로커 쪽에만 있고 여기에 중복되지 않는다 — 하나의 브로커가 두 곳에서 기술되면 두 값이 어긋나는 날이 오고, 어느 쪽이 이기는지는 아무도 모른다.

credential-id는 이름일 뿐이고 자격 증명 자체가 아니다. 실제 재료는 CredentialProvider가 연결 시점에 해석하므로, 설정 덤프나 힙 덤프에서 나오는 것은 이름뿐이다. producer와 consumer는 서로 다른 credential-id를 써야 하며, 같으면 부팅에 실패한다.

Experimental / Optional

기본값은 전부 false다.

app:
  messaging:
    experimental:
      kafka-share: false
      pulsar: false
      nats: false
    bridge:
      spring-cloud-stream: false

Backpressure

app:
  messaging:
    backpressure:
      global-limit: 512
      per-destination-limit: 64    # global-limit 이하여야 한다

per-destination-limit > global-limit이면 global limit이 limit이 아니게 되므로 부팅에 실패한다.

바인딩되지 않는 키

섹션은 바인딩되는데 그 안의 키 하나가 오타인 경우는 접두사 오타와 달리 조용하다 — 섹션은 붙고, 플랫폼은 뜨고, 바꾸러 온 그 설정만 적용되지 않는다. MessagingConfigurationKeyValidatorapp.messaging.destinations|brokers|security 아래의 모든 키를 settings 레코드에서 파생한 목록과 대조하고, 없는 키는 그 키 이름을 담아 부팅을 거절한다.

허용 키 목록은 이 문서가 아니라 레코드에서 나온다. 문서에 목록을 적으면 필드가 추가된 날 그 목록이 틀리고, 오타를 잡으라고 만든 검사가 정상 필드를 거절하게 된다.

환경변수(APP_MESSAGING_...)는 이 검사의 대상이 아니다. APP_MESSAGING_DESTINATIONS_ORDER_EVENTS_ CONSUMER_PREFETCH에서 entry 이름과 leaf를 가르는 밑줄은 둘 안에 있는 밑줄과 구별되지 않으므로, 되돌려 쪼개려면 추측해야 한다. 여기서의 추측은 정상 배포를 거절하는 쪽으로 틀리며, 그것은 배포 매니페스트에 손으로 적어야 하는 변수에서 오타 하나를 놓치는 것보다 나쁘다.