问题描述

Service Bus接收端的日志中出现大量的MessageLockLostException异常。完整的错误消息为:

Microsoft.Azure.ServiceBus.MessageLockLostException: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue. Reference:b2b452db-bf32-41c1-8b76-e546fbdc3856, TrackingId:625929b5-7392-4bf2-9beb-63c132837fc8_B0, SystemTracker:nchn-pr-dep-iot-bus:Queue:dep-iot-input-st-output, Timestamp:2020-12-01T06:13:15
at Microsoft.Azure.ServiceBus.Core.MessageReceiver.OnRenewLockAsync(String lockToken)
at Microsoft.Azure.ServiceBus.Core.MessageReceiver.<>c__DisplayClass74_0.<<RenewLockAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.Azure.ServiceBus.RetryPolicy.RunOperation(Func`1 operation, TimeSpan operationTimeout)
at Microsoft.Azure.ServiceBus.RetryPolicy.RunOperation(Func`1 operation, TimeSpan operationTimeout)
at Microsoft.Azure.ServiceBus.Core.MessageReceiver.RenewLockAsync(String lockToken)
at Microsoft.Azure.ServiceBus.Core.MessageReceiver.RenewLockAsync(Message message)
at Microsoft.Azure.ServiceBus.MessageReceivePump.RenewMessageLockTask(Message message, CancellationToken renewLockCancellationToken) 791

问题原因

在接收方使用预提取后,Service Bus服务将锁定此次预提取的消息。通过锁定操作,其他接收方则无法接收到此预提取的消息(保持消息唯一消费)。如果接收方在锁定过期之前无法完成此消息,则该消息便对其他接收方可用。

预提取的消息的副本则保留在缓存中。 使用过期的缓存副本的接收方会在尝试完成该消息时接收到一个异常(MessageLockLostException)

默认情况下,消息锁定在 30 秒后过期。 这一值可延长到 5 分钟。 通常在创建队列时进行设置。 这是队列级别的属性,不能在消息基础上进行更改。如下图中的Message lock duration(可以点击Change Link进行修改).

解决问题

方法一:修改Message Lock Duration的时间长度,最大可以修改到5分钟。

方法二:在设定消息CompleteAsync前,判断时间 message.LockedUntilUtc中的时间是否已经超过了Message Lock Duration,如果消息未到期但即将到期,可通过调用RenewLock,延续和扩展又一默认锁定时间段

if(message.LockedUntilUtc.Minute <= 1)
message.RenewLock();

应用程序可能收到包含到期或即将到期的锁定的消息。 如果是这样,应用程序可能处理该消息,但随后发现,因锁定到期而无法完成处理。 应用程序可查看 LockedUntilUtc 属性(受代理时钟和本地计算机时钟之间的时钟偏差约束)。 如果消息锁定已到期,则应用程序必须忽略该消息,不应对该消息或通过该消息调用任何 API。 如果消息未到期但即将到期,可通过调用 message.RenewLock() 延续和扩展又一默认锁定时间段

如果锁定在预提取缓冲区静默地到期,则视为已放弃该消息,且可再次将消息用于从队列进行检索。 这可能导致将消息提取到预提取缓冲区,并置于末尾。 如果在消息过期期间往往无法使用预提取缓存区,这将导致重复预提取消息,但始终无法将其以可用(有效锁定)状态有效送达,并最终在超出最大传送数后移动到死信队列

扩展问题

1:既然预提取更快,为何不是默认选项?(https://docs.azure.cn/zh-cn/service-bus-messaging/service-bus-prefetch#if-it-is-faster-why-is-prefetch-not-the-default-option

预提取可加快消息流程,方法是在应用程序请求消息时及请求消息前,准备好消息用于本地检索。 这种吞吐量提升是应用程序作者不得不明确作出的某种权衡的结果:

通过 ReceiveAndDelete 接收模式,预提取缓存区获取的所有消息在队列中不再可用,仅驻留在内存中预提取缓存区,直到应用程序通过 Receive/ReceiveAsync 或 OnMessage/OnMessageAsync API 接收到它们 。 如果在应用程序接收到消息前终止应用程序,这些消息将丢失,且不可恢复。

在 PeekLock 接收模式下,提取到预提取缓存区的消息将以锁定状态进入缓存区,并且将超时时钟用于锁定计时。 如果预提取缓存区很大,且处理所需时间过长,以致消息锁定在驻留于预提取缓存区,甚至应用程序还在处理消息时就到期,可能出现一些令人困惑的事件要应用程序处理。 如MessageLockLostException

如果消息处理需要高度的可靠性,且处理需要大量精力和时间,则建议谨慎使用或者丝毫不用预提取功能。

如果需要较高吞吐量且消息处理通常比较便宜,则预提取会产生显著的吞吐量优势。

参考资料

Windows Azure MessageLockLostExceptionhttps://stackoverflow.com/questions/15303711/windows-azure-messagelocklostexception

使用服务总线消息传递改进性能的最佳实践https://docs.azure.cn/zh-cn/service-bus-messaging/service-bus-performance-improvements?tabs=net-standard-sdk#prefetching

【服务总线 Azure Service Bus】Service Bus在使用预提取(prefetching)后出现Microsoft.Azure.ServiceBus.MessageLockLostException异常问题的更多相关文章

  1. The thumbprint of same asymmetric key is not same in 'SQL Server Connector for Microsoft Azure Key Vault' 1.0.4.0 and 'SQL Server Connector for Microsoft Azure Key

    https://support.microsoft.com/en-us/help/4470999/db-backup-problems-to-sql-server-connector-for-azur ...

  2. 通过Microsoft Azure服务设计网络架构的经验分享(转)

    原文:http://www.infoq.com/cn/articles/azure-networking-tips 本文从产品设计和架构角度分享了 Microsoft Azure 网络服务方面的使用经 ...

  3. Microsoft Azure Web Sites应用与实践【1】—— 打造你的第一个Microsoft Azure Website

    Microsoft Azure Web Sites应用与实践 系列: [1]—— 打造你的第一个Microsoft Azure Website [2]—— 通过本地IIS 远程管理Microsoft ...

  4. 使用VNET-to-VNET连接Microsoft Azure国际版和中国版

    Microsoft Azure的VNET-to-VNET功能可以实现跨虚拟网络的VPN连接,通过VNET-to-VNET互联的两个虚拟网络可以在同一个订阅下或者隶属不同的订阅,而且可以跨数据中心.这实 ...

  5. Microsoft Azure Web Sites应用与实践【4】—— Microsoft Azure网站的“后门”

    Microsoft Azure Web Sites应用与实践 系列: [1]—— 打造你的第一个Microsoft Azure Website [2]—— 通过本地IIS 远程管理Microsoft ...

  6. Microsoft Azure Web Sites应用与实践【2】—— 通过本地IIS 远程管理Microsoft Azure Web Site

    Microsoft Azure Web Sites应用与实践 系列: [1]—— 打造你的第一个Microsoft Azure Website [2]—— 通过本地IIS 远程管理Microsoft ...

  7. Microsoft Azure Web Sites应用与实践【3】—— 通过Visual Studio Online在线编辑Microsoft Azure 网站

    Microsoft Azure Web Sites应用与实践 系列: [1]—— 打造你的第一个Microsoft Azure Website [2]—— 通过本地IIS 远程管理Microsoft ...

  8. C# 消息队列-Microsoft Azure service bus 服务总线

    先决条件 Visual Studio 2015或更高版本.本教程中的示例使用Visual Studio 2015. Azure订阅. 注意 要完成本教程,您需要一个Azure帐户.您可以激活MSDN订 ...

  9. 【服务总线 Azure Service Bus】ServiceBus 队列中死信(DLQ - Dead Letter Queue)问题

    Azure Service Bus 死信队列产生的原因 服务总线中有几个活动会导致从消息引擎本身将消息推送到 DLQ. 如 超过 MaxDeliveryCount 超过 TimeToLive 处理订阅 ...

随机推荐

  1. FastCGI协议分析

    不知道什么时候,就开始有了让HomeServer支持PHP的念头.于是分析起了FastCGI协议.FastCGI用于WebServer与WebApplication之间的通讯,例如Apache与PHP ...

  2. Windows10系统下Hadoop和Hive开发环境搭建填坑指南

    前提 笔者目前需要搭建数据平台,发现了Windows系统下,Hadoop和Hive等组件的安装和运行存在大量的坑,而本着有坑必填的目标,笔者还是花了几个晚上的下班时候在多个互联网参考资料的帮助下完成了 ...

  3. Django做验证码登录

    验证码 关注公众号"轻松学编程"了解更多. 1.作用 在用户登录,注册以及一些敏感操作的时候,我们为了防止服务器被暴力请求,或爬虫爬取,我们可以使用验证码进行过滤,减轻服务器的压力 ...

  4. Kafka_2.12-2.5.1集群搭建与参数调优

    Kafka是目前业界使用最广泛的消息队列.数据流转常见这样的业务场景,客户端把采集到的日志推送给Kafka,业务方可以消费Kafka的数据落地HDFS,用于离线分析,也可以使用Spark或Flink消 ...

  5. 直播平台源码搭建教程:微信小程序中的直播如何去掉水印

    直播平台源码搭建教程:微信小程序中的直播如何去掉水印 本文与大家分享一下直播平台源码搭建教程,如何去掉直播视频的水印 var services = require('../../lib/service ...

  6. Git项目管理出现 .gitignore文件不起作用的解决

    在git管理项目的代码过程中总会有需要忽略的目录或者文件,比如编译过程中产生的目录和文件,这时候就需要 .gitignore来进行目录或文件的忽略了. 如果没有 .gitignore文件,可以自己手工 ...

  7. tp3.2验证码

    切换验证码 document.getElementById('img_code_1').src="__URL__/verify/"+Math.random(1,9999); 生成验 ...

  8. mdp文件-Chapter4-MD.mdp

    终于到了mdp系列的第四篇,最终MD模拟的mdp文件 先上代码,md.mdp 1 title = OPLS Lysozyme MD simulation 2 ; Run parameters 3 in ...

  9. MySQL索引结构之Hash索引、full-text全文索引(面)

    Hash索引 主要就是通过Hash算法(常见的Hash算法有直接定址法.平方取中法.折叠法.除数取余法.随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位 ...

  10. python中 try、except、finally 的执行顺序(转)

    def test1(): try: print('to do stuff') raise Exception('hehe') print('to return in try') return 'try ...