2017-12-30 4 views
1

Azure 서비스 버스 대기열에서 장기 실행 메시지를 처리하는 중 이상한 동작이 나타납니다. 상당히 큰 데이터 세트를 실행하면서 잠금을 갱신하고 있으며이 특정 메시지는 일반적으로 완료하는 데 약 1 시간이 걸립니다. 이 프로세스는 훌륭하게 작동하며 이 UTC 자정을 넘지 않는 한 완료됩니다. 자정 UTC에Azure 서비스 버스 대기열 메시지 잠금은 UTC 자정에 만료됩니다.

방은, 우리의 과정에서 예외가 발생합니다 :

Microsoft.ServiceBus.Messaging.MessageLockLostException : 공급 잠금이 유효하지 않습니다. 잠금이 만료되었거나 메시지가 이미 큐에서 제거되었습니다.

우리는이 밤에 밤을 재현 할 수 있으며, 자정이 지난 후에는 프로세스가 실행되지 않습니다.

메시지 잠금 "ExpiresAtUtc"타임 스탬프 계산이 언젠가 정상적으로 넘어가는 것을 처리 할 수 ​​있습니까?

---- UPDATE ---- 도움이 될 수 있습니다

일부 자세한 내용 :

장기 실행 프로세스가 RenewLock를 호출하여 큐에 다시 볼 수지고에서 메시지를 유지합니다. 이 처리가 자정에 계속되면 대기열에서 메시지가 표시되고 처리가 다시 시작됩니다. 메시지는 삭제되지 않고 배달되지 않은 편지 대기열로 이동하지 않습니다. 잠김이 만료되고 메시지가 대기열에 다시 표시되어 프로세서에서 처리를 시작하고 프로세스가 다시 시작됩니다. 프로세스가 UTC 자정을 넘지 않는 한 프로세스가 성공적으로 완료됩니다. 큐

연결 :

private QueueClient GetQueue<TMessage>() => QueueClient.CreateFromConnectionString(this.configSection.Value.ConnectionString, typeof(TMessage).Name, ReceiveMode.PeekLock); 

인큐 메시지 :

using (var brokeredMessage = new BrokeredMessage(message) {ContentType = "application/json"}) 
{ 
    await GetQueue<TMessage>().SendAsync(brokeredMessage).ConfigureAwait(false); 
} 

큐에서 메시지를

여기에 코드 조각은 우리가 큐에서/인큐/디큐를 연결하기 위해 사용하고 있습니다 :

GetQueue<TMessage>().OnMessageAsync(
    async msg => 
    { 
     TMessage body = null; 
     try 
     { 
      body = msg.GetBody<TMessage>(); 

      await handler.HandleMessageAsync(body, msg.RenewLockAsync).ConfigureAwait(false); 
      await msg.CompleteAsync().ConfigureAwait(false); 
     } 
     catch (Exception ex) 
     { 
      await msg.AbandonAsync().ConfigureAwait(false); 
     } 
    }, 
    new OnMessageOptions 
    { 
     AutoComplete = false 
    } 
); 

다음은 Azure Service Bus Metrics - Successful Requests의 스크린 샷입니다. 자정에 UTC + 1 시간대이므로 오전 1시에 자정에 표시합니다. 무언가 대기열에 영향을줍니다 :

enter image description here 이하

가 내부 기록의 화면이며, 처리는 중지하고 이후 다시 시작 잠깐 위에 - 로크가 만료 메시지가 촬상되어, 다시 큐 상에 표시 될 때 프로세서에 의한 업그레이드 :

enter image description here

+0

_ 메시지 잠금 "ExpiresAtUtc"타임 스탬프 계산이 하루를 넘어서 정상적으로 넘어가는 것을 처리하지 못합니까? _ 그렇게 생각하지 마십시오. 당신이 간단한 repro를 공유 할 수 있다면 좋을 것입니다. –

+0

안녕하세요 @SeanFeldman - 좀 더 많은 정보를 추가했습니다. 이게 도움이 되나요? – codefrenzy

+0

흥미 롭습니다. 차트를 보면 2 분 동안 연결이 끊어지는 것 같습니다. 잠금 갱신은 클라이언트 작업이므로 클라이언트가 서버에 연결하지 못하면 잠금이 손실되고 다른 인스턴스에서 메시지를 사용할 수있게됩니다. 잠금 기간이 최대 (5 분)로 설정된 경우 잠금 갱신은 그보다 일찍 수행됩니다. 대기열에 설정된 잠금 기간은 무엇입니까? 또한, ASB 클라이언트의 어떤 버전을 사용하고 있습니까? –

답변

0

저는 ASB (엄청난 규모)의 주제/구독에 익숙하지만 대기열과 주제가 동일한 내부 인프라를 공유하는 한 알고 있습니다.

기본적으로 우리 소프트웨어에서는 장기 실행 프로세스에서 메시지를 잠그는 방법으로 BrokeredMessage.RenewLock()을 사용합니다.

작업을 수행하고 프로세스가 끝날 때까지 메시지를 잠근 상태로두면 30 초마다 (대기열 "잠금 지속 기간"구성에 따라 다름) 별도의 작업이 시작되며 brokeredMessage.RenewLock() , 즉, 우리는 잠금 장치를 핑 (ping)하여 계속 잃어 버리지 않습니다.

보고하려는 오류가 로컬이 아니거나 메시지를 '완료'하거나 데드 레터링하거나 메시지 잠금을 유지해야하는 다른 작업을 시도하면 ASB에서 던져진 오류 일 가능성이 높습니다 타임 아웃에 의한 잠금.

우리가 잠금을 유지하는 데 사용하는 코드는 생존이 매우 비슷합니다

private void EngageAutoRenewLock(BrokeredMessage message, CancellationTokenSource renewLockCancellationTokenSource) 
     { 
      var renewalThread = Task.Factory.StartNew(() => 
      { 
       Trace(string.Format("Starting lock renewal thread for MsgID {0}", message.MessageId)); 

       while (!renewLockCancellationTokenSource.IsCancellationRequested) 
       { 
        // we use Sleep instead of Task.Delay to avoid get an exception when token is cancelled 
        System.Threading.Thread.Sleep(30000); 
        // check again after sleeping 
        if (!renewLockCancellationTokenSource.IsCancellationRequested) 
        { 
         try 
         { 
          // renewlock wraps a RenewLockAsync() call 
          message.RenewLock(); 
         } 
         catch (Exception ex) 
         { 
          /* Do Nothing. 

           This exception can happen due the async nature of the RenewLock Operation (async Begin/End), it is possible we 
           completed/abandoned/deadlettered the message in the main thread in the middle of the RenewLock operation. 

           Additionally, we should keep retrying to renew lock until renewLockCancellationTokenSource has ben signaled. 
           This will allow to handle other transient renewal issues. */ 

          Trace(string.Format("Lock renewal failed due an exception for MsgID {0} - {1}", message.MessageId, ex.Message)); 
         } 
         Trace(string.Format("Lock renewed MsgID {0}", message.MessageId)); 
        } 
        else 
        { 
         Trace(string.Format("Lock renewed MsgID {0} not happened due IsCancellationRequested == true", message.MessageId)); 
        } 
       } 
       Trace(string.Format("Lock renewal has been cancelled by cancellationToken (ok) for MsgID {0}", message.MessageId)); 
      }, renewLockCancellationTokenSource.Token); 
     } 
+0

1. 메시지를 훨씬 간단하게 만들고 메시지 잠금 확장을 지원할 수있는 OnMessageAPI를 살펴보십시오. 2. Thread.Sleep()과 비동기 코드를 함께 사용하지 마십시오. –

+0

우리의 경우, Task.Delay 대신 Thread sleep에 의존 할 기술적 인 이유가 있습니다. 다른 사람들은 믿음이 구현 컨텍스트에 대해 더 정확하고 정확하다는 방식으로 지연을 처리 할 수 ​​있습니다! 힌트 : 많은 표준 라이브러리/다른 라이브러리가 TPL 이전에 작성 되었기 때문에 System.Threading.Thread와 TPL을 효과적으로 섞어서 코드가 TPL 책임을 방해하지 않는 한 자유롭게 혼합 할 수 있습니다. –

1

이 나는 ​​푸른 서비스 버스 팀입니다. LockDuration은 단지 지속 시간이며 내부적으로 타임 스탬프로 표시되지 않습니다. 메시지는 수신 된 순간부터 LockDuration에 대해 기술적으로 잠기고 미래의 어느 순간까지는 잠기지 않습니다. 예를 들어 메시지는 수신 된 시간으로부터 30 초 동안 잠겨 있습니다. 그러니 자정 UTC는 절대로 특별한 사건이 아닙니다. 우리의 자동화 된 테스트는 매일 밤마다 실행되며 이전에는이 ​​사례가 없습니다. 하지만 매일 밤 재현하고 있기 때문에 재미있는 일이있을 것입니다. 나는 그것이 무엇인지 아직 모른다. 당신이 이것을 재현하고있는 지역을 알고 싶습니다. 우리 팀원 중 한 명이 곧 연락을 드릴 것입니다.

+0

감사합니다. 상상할 수있는 것처럼 실제 실행중인 시스템에 영향을 미치므로이 문제를 해결하고 싶습니다. 우리의 대기열은 'West US'에 있습니다. 큐 프로세서 (Windows 서비스)를 실행중인 VM도 'West US'에 있지만 UTC 시간대를 사용합니다. – codefrenzy

관련 문제