我的团队一直在尝试使我们的某些WebJobs custom queue processors具有Singleton行为,但是我们实际上并没有通过[Singleton(Mode = SingletonMode.Listener)]
属性或设置QueueProcessorFactoryContext.BatchSize = 1
来获得这种行为。 。这导致每晚执行的过程立即一次猛击数据库-其中许多过程超时-变得有些头疼。
这差不多就是我们的CustomQueueProcessorFactory的样子:
public class CustomQueueProcessorFactory : IQueueProcessorFactory
{
public QueueProcessor Create(QueueProcessorFactoryContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (context.Queue.Name == Constants.UploadQueueName
|| context.Queue.Name == Constants.BuildQueueName)
{
context.BatchSize = 1;
}
return new QueueProcessor(context);
}
}
在配置我们的JobHost时引用了它:
var config = new JobHostConfiguration();
config.Queues.QueueProcessorFactory = new CustomQueueProcessorFactory();
我们还使用QueueTrigger
设置了一些功能,例如:
public static async Task ExecuteBackgroundRequest([QueueTrigger(Constants.BackgroundQueueName)] BackgroundRequest background, TextWriter logger)
{
await ExecuteRequest(background, logger);
}
[Singleton(Mode = SingletonMode.Listener)]
public static async Task ExecuteUploadRequest([QueueTrigger(Constants.UploadQueueName)] BackgroundRequest background, TextWriter logger)
{
await ExecuteRequest(background, logger);
}
[Singleton(Mode = SingletonMode.Listener)]
public static async Task ExecuteBuildRequest([QueueTrigger(Constants.BuildQueueName)] BackgroundRequest background, TextWriter logger)
{
await ExecuteRequest(background, logger);
}
我们使用的软件包Microsoft.Azure.WebJobs
(+ .Core
,.Extensions
)v2.0.0和WindowsAzure.Storage
v8.0.0已经过时,因此一个潜在的解决方案是一直在探索的是对WebJobs(v3.0.4)最新稳定版本的更新。由于配置已完全重做,并且所有类都已移至新位置,因此打开了一个全新的蠕虫罐。该文档似乎稀疏/分散,因此我还没有确定我可以在每个QueueProcessor基础上自定义属性的位置(或什至),例如将某些队列的BatchSize设置为1,而将其他队列的BatchSize设置为1。
是否存在某些版本的WebJobs,可以在其中使用上述CustomQueueProcessorFactory逻辑来限制BatchSize?或者,Singleton属性实际上将确保一次只有一个后台进程正在访问特定队列?可以在最新版本的WebJobs中配置QueueProcessorFactories吗?
在任何这些问题上的帮助将不胜感激!
答案 0 :(得分:0)
WebJobs SDK通过其SingletonAttribute简化了常见的分布式锁定方案。您可以简单地将SingletonAttribute应用于作业函数,以确保该函数的所有调用都将被序列化,即使在扩展实例中也是如此。如果您的函数需要访问其他分布式资源或执行不应/不能同时执行的其他操作,这将很有用。
[Singleton]
public static async Task ProcessImage([BlobTrigger("images")] Stream image)
{
// Process the image
}
在此示例中,在任何给定时间仅将运行ProcessImage函数的单个实例。当通过将新图像添加到图像容器触发该功能时,运行时将首先尝试获取锁(blob租约)。一旦获取,将在函数执行期间保持锁定(并更新Blob租约),以确保没有其他实例将运行。如果在运行此函数时触发了另一个函数实例,它将等待该锁,并定期轮询该锁。
Singleton使用Azure Blob Leases来实现分布式锁定。 SDK负责管理Blob租约,租约续约等所有复杂性。
单锁详细信息也显示在WebJobs仪表板上,包括正在进行的函数执行的当前锁状态,以及函数在获取锁之前等待了多长时间。您可以使用这些详细信息来查看和管理锁争用。
非并发方案将需要使用Singleton。一些触发器通过其配置设置具有对并发管理的内在支持。在这种情况下,使用内置支持可能会更有效。您可以使用这些设置来确保函数在单个实例上运行单例。为确保仅单个函数实例在扩展实例中运行,此外,您可以在函数上应用侦听器级别的Singleton锁(例如[Singleton(Mode = SingletonMode.Listener)])。一些触发器的配置旋钮是:
QueueTrigger -您可以将JobHostConfiguration.Queues.BatchSize设置为1 ServiceBusTrigger -您可以将ServiceBusConfiguration.MessageOptions.MaxConcurrentCalls设置为1 FileTrigger -您可以将FileProcessor.MaxDegreeOfParallelism设置为1 但是,对于固有不支持并发控制的触发器,或者如果您想通过Singleton范围(请参见下文)进行更高级的锁定,Singleton是正确的选择。
我也想说,使用双锁实现如下所示的CustomQueueProcessorFactory:-
if (_instance == null)
{
lock (SyncObject)
{
if (_instance == null)
{
_instance = new CustomQueueProcessor();
}
}
}
return _instance;
答案 1 :(得分:0)
因此,如果我理解正确,即使在横向扩展之后,您仍希望多个功能按顺序处理其触发消息。我说的对吗?
我认为您可以使用带有scoping参数的SingletonAttribute来强制执行此操作:
public static async Task ExecuteBackgroundRequest([QueueTrigger(Constants.BackgroundQueueName)] BackgroundRequest background, TextWriter logger)
{
await ExecuteRequest(background, logger);
}
[Singleton("DatabaseSync", SingletonScope.Host)]
public static async Task ExecuteUploadRequest([QueueTrigger(Constants.UploadQueueName)] BackgroundRequest background, TextWriter logger)
{
await ExecuteRequest(background, logger);
}
[Singleton("DatabaseSync", SingletonScope.Host)]
public static async Task ExecuteBuildRequest(
[QueueTrigger(Constants.BuildQueueName)] BackgroundRequest background, TextWriter logger)
{
await ExecuteRequest(background, logger);
}