Azure Service Bus 문제 해결
이 문서에서는 오류 조사 기술, 동시성, Azure Service Bus Java 클라이언트 라이브러리의 자격 증명 유형에 대한 일반적인 오류 및 이러한 오류를 해결하기 위한 완화 단계를 설명합니다.
로깅 사용 및 구성
Java용 Azure SDK는 애플리케이션 오류 문제를 해결하고 해결을 신속하게 하는 데 도움이 되는 일관된 로깅 스토리를 제공합니다. 생성된 로그는 터미널 상태에 도달하기 전에 애플리케이션의 흐름을 캡처하여 근본 문제를 찾는 데 도움이 됩니다. 로깅에 대한 지침은 Java용 Azure SDK의 로깅 구성 및 문제 해결 개요를 참조하세요.
로깅을 사용하도록 설정하는 것 외에도 로그 수준을 VERBOSE
라이브러리의 상태로 설정하거나 DEBUG
라이브러리의 상태에 대한 인사이트를 제공합니다. 다음 섹션에서는 자세한 정보 로깅을 사용할 때 과도한 메시지를 줄이기 위한 샘플 log4j2 및 logback 구성을 보여 줍니다.
Log4J 2 구성
다음 단계를 사용하여 Log4J 2를 구성합니다.
- "Log4j2에 필요한 종속성" 섹션의 로깅 샘플 pom.xml 종속성을 사용하여 pom.xml 종속성을 추가합니다.
- src/main/resources 폴더에 log4j2.xml 추가합니다.
로그백 구성
다음 단계를 사용하여 로그백을 구성합니다.
- 로깅 샘플 pom.xml "로그백에 필요한 종속성" 섹션의 종속성을 사용하여 pom.xml 종속성을 추가합니다.
- src/main/resources 폴더에 logback.xml 추가합니다.
AMQP 전송 로깅 사용
클라이언트 로깅을 사용하도록 설정해도 문제를 진단하기에 충분하지 않은 경우 기본 AMQP 라이브러리 Qpid Proton-J의 파일에 대한 로깅을 사용하도록 설정할 수 있습니다. Qpid Proton-J는 .를 사용합니다 java.util.logging
. 다음 섹션에 표시된 내용이 포함된 구성 파일을 만들어 로깅을 사용하도록 설정할 수 있습니다. 또는 구현에 원하는 java.util.logging.Handler
구성 옵션을 설정합니다proton.trace.level=ALL
. 구현 클래스 및 해당 옵션은 Java 8 SDK 설명서의 패키지 java.util.logging을 참조하세요.
AMQP 전송 프레임을 추적하려면 환경 변수를 PN_TRACE_FRM=1
설정합니다.
샘플 logging.properties 파일
다음 구성 파일은 Proton-J에서 파일 proton-trace.log TRACE 수준 출력을 기록합니다.
handlers=java.util.logging.FileHandler
.level=OFF
proton.trace.level=ALL
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.pattern=proton-trace.log
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=[%1$tF %1$tr] %3$s %4$s: %5$s %n
로깅 줄이기
로깅을 줄이는 한 가지 방법은 자세한 정보를 변경하는 것입니다. 또 다른 방법은 로거 이름 패키지에서 로그를 제외하는 필터를 추가하는 것입니다 com.azure.messaging.servicebus
com.azure.core.amqp
. 예를 들어 Log4J 2 구성 및 로그백 구성 섹션의 XML 파일을 참조하세요.
버그를 제출할 때 다음 패키지의 클래스에서 보내는 로그 메시지는 흥미롭습니다.
com.azure.core.amqp.implementation
com.azure.core.amqp.implementation.handler
- 예외는 .에서 메시지를 무시할
onDelivery
수 있다는 것입니다ReceiveLinkHandler
.
- 예외는 .에서 메시지를 무시할
com.azure.messaging.servicebus.implementation
ServiceBusProcessorClient의 동시성
ServiceBusProcessorClient
를 사용하면 애플리케이션이 메시지 처리기에 대한 호출 수를 동시에 구성할 수 있습니다. 이 구성을 사용하면 여러 메시지를 병렬로 처리할 수 있습니다. ServiceBusProcessorClient
비 세션 엔터티에서 메시지를 사용하는 경우 애플리케이션은 API를 사용하여 maxConcurrentCalls
원하는 동시성을 구성할 수 있습니다. 세션 사용 엔터티의 경우 원하는 동시성은 시간maxConcurrentCalls
입니다maxConcurrentSessions
.
애플리케이션이 구성된 동시성보다 메시지 처리기에 대한 동시 호출이 적은 것을 관찰하는 경우 스레드 풀의 크기가 적절하게 조정되지 않았기 때문일 수 있습니다.
ServiceBusProcessorClient
에서는 Reactor global boundedElastic 스레드 풀의 디먼 스레드를 사용하여 메시지 처리기를 호출합니다. 이 풀의 최대 동시 스레드 수는 한도로 제한됩니다. 기본적으로 이 한도는 사용 가능한 CPU 코어 수의 10배입니다. ServiceBusProcessorClient
애플리케이션의 원하는 동시성(maxConcurrentCalls
또는 maxConcurrentSessions
시간maxConcurrentCalls
)을 효과적으로 지원하려면 원하는 동시성보다 높은 풀 캡 값이 있어야 합니다boundedElastic
. 시스템 속성을 reactor.schedulers.defaultBoundedElasticSize
설정하여 기본 상한을 재정의할 수 있습니다.
대/소문자별로 스레드 풀 및 CPU 할당을 조정해야 합니다. 그러나 풀 캡을 재정의하는 경우 시작점으로 동시 스레드를 CPU 코어당 약 20-30으로 제한합니다. 인스턴스당 ServiceBusProcessorClient
원하는 동시성을 약 20-30으로 제한하는 것이 좋습니다. 특정 사용 사례를 프로파일 및 측정하고 그에 따라 동시성 측면을 조정합니다. 부하가 높은 시나리오의 경우 각 인스턴스가 새 ServiceBusClientBuilder
인스턴스에서 빌드되는 여러 ServiceBusProcessorClient
인스턴스를 실행하는 것이 좋습니다. 또한 한 호스트의 가동 중지 시간이 전체 메시지 처리에 영향을 주지 않도록 컨테이너 또는 VM과 같은 전용 호스트에서 각각 ServiceBusProcessorClient
실행하는 것이 좋습니다.
CPU 코어가 적은 호스트에서 풀 캡에 대한 높은 값을 설정하면 부정적인 영향을 미칠 수 있습니다. CPU 리소스가 낮거나 CPU 수가 너무 많은 풀의 일부 징후는 빈번한 시간 제한, 잠금 손실, 교착 상태 또는 낮은 처리량입니다. 컨테이너에서 Java 애플리케이션을 실행하는 경우 두 개 이상의 vCPU 코어를 사용하는 것이 좋습니다. 컨테이너화된 환경에서 Java 애플리케이션을 실행할 때 1개 미만의 vCPU 코어를 선택하지 않는 것이 좋습니다. 자원 조달에 대한 자세한 권장 사항은 Java 애플리케이션 컨테이너화를 참조하세요.
연결 공유 병목 현상
공유 ServiceBusClientBuilder
인스턴스에서 만든 모든 클라이언트는 Service Bus 네임스페이스에 대한 동일한 연결을 공유합니다.
공유 연결을 사용하면 한 연결에서 클라이언트 간에 멀티플렉싱 작업을 수행할 수 있지만 클라이언트가 많거나 클라이언트가 함께 높은 부하를 생성하는 경우 공유가 병목 상태가 될 수도 있습니다. 각 연결에는 연결된 I/O 스레드가 있습니다. 연결을 공유할 때 클라이언트는 이 공유 I/O 스레드의 작업 큐에 작업을 배치하고 각 클라이언트의 진행률은 큐에서 해당 작업이 적시에 완료되는 경우에 따라 달라집니다. I/O 스레드는 큐에 찍힌 작업을 직렬로 처리합니다. 즉, 공유 연결의 I/O 스레드 작업 큐가 처리해야 할 보류 중인 작업이 많은 경우 증상은 CPU가 낮은 것과 유사합니다. 이 조건은 동시성에 대한 이전 섹션에서 설명합니다(예: 클라이언트가 중단, 시간 제한, 잠금 손실 또는 복구 경로의 속도 저하).
Service Bus SDK는 reactor-executor-*
연결 I/O 스레드에 대한 명명 패턴을 사용합니다. 애플리케이션에서 공유 연결 병목 현상이 발생하면 I/O 스레드의 CPU 사용량에 반영될 수 있습니다. 또한 힙 덤프 또는 라이브 메모리에서 개체 ReactorDispatcher$workQueue
는 I/O 스레드의 작업 큐입니다. 병목 상태 기간 동안 메모리 스냅샷의 긴 작업 큐는 공유 I/O 스레드가 보류 중인 작업으로 오버로드되었음을 나타낼 수 있습니다.
따라서 Service Bus 엔드포인트에 대한 애플리케이션 로드가 전체 수신 메시지 수 또는 페이로드 크기 측면에서 상당히 높은 경우 빌드하는 각 클라이언트에 대해 별도의 작성기 인스턴스를 사용해야 합니다. 예를 들어 큐 또는 토픽과 같은 각 엔터티에 대해 새 ServiceBusClientBuilder
엔터티를 만들고 해당 엔터티에서 클라이언트를 빌드할 수 있습니다. 특정 엔터티에 대한 부하가 매우 높은 경우 해당 엔터티에 대한 여러 클라이언트 인스턴스를 만들거나 여러 호스트(예: 컨테이너 또는 VM)에서 클라이언트를 실행하여 부하를 분산하는 것이 좋습니다.
Application Gateway 사용자 지정 엔드포인트를 사용할 때 클라이언트가 중지됩니다.
사용자 지정 엔드포인트 주소는 Service Bus로 확인 가능하거나 Service Bus로 트래픽을 라우팅하도록 구성된 애플리케이션 제공 HTTPS 엔드포인트 주소를 나타냅니다. Azure 애플리케이션 게이트웨이를 사용하면 트래픽을 Service Bus로 전달하는 HTTPS 프런트 엔드를 쉽게 만들 수 있습니다. Application Gateway 프런트 엔드 IP 주소를 Service Bus에 연결하는 사용자 지정 엔드포인트로 사용하도록 애플리케이션에 대해 Service Bus SDK를 구성할 수 있습니다.
Application Gateway는 다양한 TLS 프로토콜 버전을 지원하는 여러 보안 정책을 제공합니다. TLSv1.2를 최소 버전으로 적용하는 미리 정의된 정책이 있으며, TLSv1.0을 최소 버전으로 사용하는 이전 정책도 있습니다. HTTPS 프런트 엔드에는 TLS 정책이 적용됩니다.
현재 Service Bus SDK는 TLSv1.0을 최소 버전으로 사용하는 Application Gateway 프런트 엔드에서 특정 원격 TCP 종료를 인식하지 못합니다. 예를 들어 프런트 엔드가 TCP FIN, ACK 패킷을 전송하여 속성이 업데이트될 때 연결을 닫는 경우 SDK는 이를 검색할 수 없으므로 다시 연결되지 않으며 클라이언트는 더 이상 메시지를 보내거나 받을 수 없습니다. 이러한 중지는 TLSv1.0을 최소 버전으로 사용하는 경우에만 발생합니다. 완화하려면 TLSv1.2 이상의 보안 정책을 Application Gateway 프런트 엔드의 최소 버전으로 사용합니다.
모든 Azure 서비스에서 TLSv1.0 및 1.1에 대한 지원은 이미 2024년 10월 31일까지 종료될 것으로 발표 되었으므로 TLSv1.2로 전환하는 것이 좋습니다.
메시지 또는 세션 잠금이 손실됨
Service Bus 큐 또는 토픽 구독에는 리소스 수준에서 잠금 기간이 설정됩니다. 수신자 클라이언트가 리소스에서 메시지를 가져오면 Service Bus Broker는 메시지에 초기 잠금을 적용합니다. 초기 잠금은 리소스 수준에서 설정된 잠금 기간 동안 지속됩니다. 메시지 잠금이 만료되기 전에 갱신되지 않으면 Service Bus 브로커는 메시지를 해제하여 다른 수신기에서 사용할 수 있도록 합니다. 애플리케이션이 잠금 만료 후 메시지를 완료하거나 중단하려고 하면 오류 com.azure.messaging.servicebus.ServiceBusException: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue
와 함께 API 호출이 실패합니다.
Service Bus 클라이언트는 만료되기 전에 매번 메시지 잠금을 지속적으로 갱신하는 백그라운드 잠금 갱신 작업 실행을 지원합니다. 기본적으로 잠금 갱신 작업은 5분 동안 실행됩니다. 를 사용하여 ServiceBusReceiverClientBuilder.maxAutoLockRenewDuration(Duration)
잠금 갱신 기간을 조정할 수 있습니다. 값을 전달 Duration.ZERO
하면 잠금 갱신 작업이 비활성화됩니다.
다음 목록에서는 잠금 손실 오류로 이어질 수 있는 일부 사용 패턴 또는 호스트 환경에 대해 설명합니다.
잠금 갱신 작업이 비활성화되고 애플리케이션의 메시지 처리 시간이 리소스 수준에서 설정된 잠금 기간을 초과합니다.
애플리케이션의 메시지 처리 시간이 구성된 잠금 갱신 작업 기간을 초과합니다. 잠금 갱신 기간이 명시적으로 설정되지 않은 경우 기본값은 5분입니다.
애플리케이션에서 프리페치 값을 사용하여
ServiceBusReceiverClientBuilder.prefetchCount(prefetch)
양수 정수로 설정하여 프리페치 기능을 설정했습니다. 프리페치 기능을 사용하도록 설정하면 클라이언트는 Service Bus 엔터티(큐 또는 토픽)에서 프리페치와 동일한 메시지 수를 검색하고 메모리 내 프리페치 버퍼에 저장합니다. 메시지는 애플리케이션에 수신될 때까지 프리페치 버퍼에 유지됩니다. 클라이언트는 프리페치 버퍼에 있는 동안 메시지의 잠금을 확장하지 않습니다. 애플리케이션 처리가 너무 오래 걸리면 프리페치 버퍼에 머무르는 동안 메시지 잠금이 만료되면 애플리케이션이 만료된 잠금으로 메시지를 가져올 수 있습니다. 자세한 내용은 프리페치가 기본 옵션이 아닌 이유를 참조 하세요.호스트 환경에는 잠금 갱신 작업이 시간에 따라 잠금을 갱신하지 못하도록 하는 일시적인 네트워크 오류 또는 중단과 같은 네트워크 문제가 가끔 있습니다.
호스트 환경에 충분한 CPU가 없거나 일시적으로 CPU 주기가 부족하여 잠금 갱신 작업이 정시에 실행되는 것을 지연합니다.
호스트 시스템 시간이 정확하지 않습니다(예: 시계가 왜곡됨) 잠금 갱신 작업을 지연시키고 정시에 실행되지 않도록 합니다.
연결 I/O 스레드가 오버로드되어 시간에 잠금 갱신 네트워크 호출을 실행하는 기능에 영향을 미칩니다. 다음 두 가지 시나리오로 인해 이 문제가 발생할 수 있습니다.
클라이언트의 잠금 갱신 작업 수는 설정되거나 maxConcurrentCalls
ServiceBusReceiverClient.receiveMessages
설정된 ServiceBusProcessorClient
매개 변수 값과 같습니다maxMessages
. 여러 네트워크 호출을 수행하는 잠금 갱신 작업의 수가 많으면 Service Bus 네임스페이스 제한에 부정적인 영향을 미칠 수도 있습니다.
호스트에 충분한 리소스가 없는 경우 잠금 갱신 작업이 몇 개만 실행되더라도 잠금이 손실될 수 있습니다. 컨테이너에서 Java 애플리케이션을 실행하는 경우 두 개 이상의 vCPU 코어를 사용하는 것이 좋습니다. 컨테이너화된 환경에서 Java 애플리케이션을 실행하는 경우 1개 미만의 vCPU 코어를 선택하지 않는 것이 좋습니다. 자원 조달에 대한 자세한 권장 사항은 Java 애플리케이션 컨테이너화를 참조하세요.
잠금에 대한 동일한 설명은 Service Bus 큐 또는 세션을 사용하도록 설정된 토픽 구독과도 관련이 있습니다. 수신기 클라이언트가 리소스의 세션에 연결되면 broker는 세션에 초기 잠금을 적용합니다. 세션에 대한 잠금을 유지하려면 클라이언트의 잠금 갱신 작업이 만료되기 전에 세션 잠금을 계속 갱신해야 합니다. 세션 사용 리소스의 경우 기본 파티션은 때때로 Service Bus 노드 간에 부하 분산을 달성하기 위해 이동합니다(예: 부하를 공유하기 위해 새 노드가 추가될 때). 이 경우 세션 잠금이 손실될 수 있습니다. 세션 잠금이 손실된 후 애플리케이션이 메시지를 완료하거나 중단하려고 하면 오류 com.azure.messaging.servicebus.ServiceBusException: The session lock was lost. Request a new session receiver
와 함께 API 호출이 실패합니다.
7.15.x 또는 최신 버전으로 업그레이드
문제가 발생하는 경우 먼저 최신 버전의 Service Bus SDK로 업그레이드하여 문제를 해결해야 합니다. 버전 7.15.x는 오랜 성능 및 안정성 문제를 해결하는 주요 재설계입니다.
버전 7.15.x 이상에서는 스레드 호핑을 줄이고, 잠금을 제거하고, 핫 경로에서 코드를 최적화하고, 메모리 할당을 줄입니다. 이러한 변경으로 인해 처리량이 최대 45~50배 더 커지 ServiceBusProcessorClient
게됩니다.
버전 7.15.x 이상에는 다양한 안정성이 향상되었습니다. 프리페치 및 신용 계산과 같은 여러 경합 조건과 향상된 오류 처리를 해결합니다. 이러한 변경으로 인해 다양한 클라이언트 유형에서 일시적인 문제가 있는 경우 안정성이 향상됩니다.
최신 클라이언트 사용
이러한 향상된 새로운 기본 프레임워크(버전 7.15.x 이상)를 V2-Stack이라고 합니다. 이 릴리스 줄에는 이전 세대의 기본 스택(버전 7.14.x에서 사용하는 스택)과 새 V2-Stack이 모두 포함됩니다.
기본적으로 일부 클라이언트 유형은 V2-Stack을 사용하지만 다른 클라이언트 유형에는 V2-Stack 옵트인이 필요합니다. 클라이언트를 빌드할 때 값을 제공하여 com.azure.core.util.Configuration
클라이언트 유형에 대한 특정 스택(V2 또는 이전 세대)의 옵트인 또는 옵트아웃을 수행할 수 있습니다.
예를 들어 다음 예제와 ServiceBusSessionReceiverClient
같이 V2 스택 기반 세션 수신에는 옵트인(opt-in)이 필요합니다.
ServiceBusSessionReceiverClient sessionReceiver = new ServiceBusClientBuilder()
.connectionString(Config.CONNECTION_STRING)
.configuration(new com.azure.core.util.ConfigurationBuilder()
.putProperty("com.azure.messaging.servicebus.session.syncReceive.v2", "true") // 'false' by default, opt-in for V2-Stack.
.build())
.sessionReceiver()
.queueName(Config.QUEUE_NAME)
.buildClient();
다음 표에서는 클라이언트 유형 및 해당 구성 이름을 나열하고, 클라이언트가 현재 최신 버전 7.17.0에서 V2-Stack을 사용하도록 설정되어 있는지 여부를 나타냅니다. 기본적으로 V2-Stack에 없는 클라이언트의 경우 방금 표시된 예제를 사용하여 옵트인할 수 있습니다.
클라이언트 유형 | 구성 이름 | 기본적으로 V2-Stack에 있나요? |
---|---|---|
보낸 사람 및 관리 클라이언트 | com.azure.messaging.servicebus.sendAndManageRules.v2 |
예 |
비 세션 프로세서 및 반응기 수신기 클라이언트 | com.azure.messaging.servicebus.nonSession.asyncReceive.v2 |
예 |
세션 프로세서 수신기 클라이언트 | com.azure.messaging.servicebus.session.processor.asyncReceive.v2 |
예 |
세션 반응기 수신기 클라이언트 | com.azure.messaging.servicebus.session.reactor.asyncReceive.v2 |
예 |
비 세션 동기 수신기 클라이언트 | com.azure.messaging.servicebus.nonSession.syncReceive.v2 |
아니요 |
세션 동기 수신기 클라이언트 | com.azure.messaging.servicebus.session.syncReceive.v2 |
아니요 |
사용 com.azure.core.util.Configuration
대신 환경 변수 또는 시스템 속성을 사용하여 동일한 구성 이름을 설정하여 옵트인 또는 옵트아웃을 수행할 수 있습니다.
다음 단계
이 문서의 문제 해결 지침이 Java용 Azure SDK 클라이언트 라이브러리를 사용할 때 문제를 해결하는 데 도움이 되지 않는 경우 Java GitHub 리포지토리용 Azure SDK에 문제를 제출하는 것이 좋습니다.