【问题标题】:Is it possible for a single webjob to have a timer triggered function as well as a manually triggered function单个网络作业是否可以同时具有计时器触发功能和手动触发功能
【发布时间】:2019-10-17 07:12:44
【问题描述】:

我在 .net 核心中有一个现有的 webjob(V3.0),它有一个由手动触发器调用的函数,主要是通过 webhook。我想向同一个 webjob 添加另一个函数,该函数应该每 20 分钟在 Timer 触发器上调用一次。是否可以将这两者都放在同一个网络作业中。如果可能的话,我需要做什么主机配置。我尝试浏览 Microsoft 的文档,但几乎没有任何关于具有多个触发器的主机配置部分的文档

【问题讨论】:

  • invoked by manual trigger, essentially by a webhook 是什么意思,你的意思是一个使用 REST api 的 webjob 调用和一个使用计时器触发器的 webjob?
  • @GeorgeChen 是的。我需要一个 Web 作业,其中一个功能由 REST Api 在 Web 挂钩上触发,另一个功能在计时器触发器上触发。我正在使用 WebJobs 3.0

标签: azure azure-webjobs azure-webjobssdk azure-webjobs-triggered


【解决方案1】:

是的,但您的函数需要由 Azure 存储中的某个对象(例如队列)触发。此代码可能比您可能需要的更多。我所有的服务都实现了一个自定义接口 IServiceInvoker。我的 CTOR 要求

IEnumerable<IServiceInvoker>

获得所有服务。然后我使用常量或传入的值来确定要运行的服务。因为我只希望一个函数一直运行,所以我使用传入 String.Empty 的 Singleton 属性。我的队列中也有以下设置

b.AddAzureStorage(a =>
{
    a.BatchSize = 1;
    a.NewBatchThreshold = 1;
    a.MaxDequeueCount = 1;
    a.MaxPollingInterval = TimeSpan.FromSeconds(60);
    a.VisibilityTimeout = TimeSpan.FromSeconds(60);
});

最后我发现在测试过程中我有时需要关闭或关闭更多功能,因此需要使用 ServiceConfigurationProvider 类。

代码示例如下我删除了相当多的代码,所以 YMMV

        public static async Task Main(string[] args)
        {
            await CreateHostBuilder(args).Build().RunAsync();
        }

更多代码

    public class Functions
    {

        /// <summary>
        /// This scopes the singleton attribute to each individual function rather than the entire host
        /// </summary>
        const String SCOPESINGLETONTOFUNCTION = "";
        readonly ILogger<Functions> _logger;
        readonly Dictionary<String, IServiceInvoker> _services;
        readonly IConfiguration _configuration;

        private Functions()
        {
            _services = new Dictionary<string, IServiceInvoker>();

        }

        public Functions(IEnumerable<IServiceInvoker> services, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IConfiguration configuration) : this()
        {

            _logger = loggerFactory.CreateLogger<Functions>();
            foreach (var service in services)
            {
                _services.Add(service.ServiceIdentifier, service);
            }
            _configuration = configuration;
        }

        [Disable(typeof(ServiceConfigurationProvider))]
        [Singleton(SCOPESINGLETONTOFUNCTION)]
        public async Task TimerTriggerFunction([TimerTrigger("%TimerTriggerFunctionExpression%")]TimerInfo myTimer, CancellationToken cancellationToken)
        {
            try
            {
                if (_services.TryGetValue("ServiceName", out IServiceInvoker serviceToInvoke))
                {
                    await serviceToInvoke.InvokeServiceAsync(null, cancellationToken, false);
                }
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, $"Unhandled exception occurred in method:'{nameof(TimerTriggerFunction)}'");
            }
        }

        [Disable(typeof(ServiceConfigurationProvider))]
        [Singleton(SCOPESINGLETONTOFUNCTION)]
        public async Task ServiceInvokerQueueFunction([QueueTrigger("%ServiceInvokerQueueName%", Connection = "AzureWebJobsStorage")] ServiceInvokerMessage serviceInvokerMessage, CancellationToken cancellationToken)
        {
            if (serviceInvokerMessage is null || String.IsNullOrEmpty(serviceInvokerMessage.ServiceIdentifier))
            {
                _logger?.LogError("The queue message received in the ServiceInvokerQueueFunction could not be serialized into a ServiceInvokerMessage instance.");
            }
            else
            {

                Boolean serviceExists = _services.TryGetValue(serviceInvokerMessage.ServiceIdentifier, out IServiceInvoker serviceToInvoke);
                if (serviceExists)
                {
                    try
                    {
                        await serviceToInvoke.InvokeServiceAsync(null, cancellationToken, true);
                    }
                    catch (Exception exception)
                    {
                        _logger?.LogError(exception, $"Unhandled exception occurred in method:'{nameof(ServiceInvokerQueueFunction)}' for service:'{serviceInvokerMessage.ServiceIdentifier}'");
                    }
                }
            }
        }

        [Disable(typeof(ServiceConfigurationProvider))]
        [Singleton(SCOPESINGLETONTOFUNCTION)]
        public async Task RecordQueueFunction([QueueTrigger("%RecordQueueName%", Connection = "RecordConnectString")] string message, CancellationToken cancellationToken)
        {
            {
                _logger?.LogInformation(message);
                try
                {
                    if (_services.TryGetValue("ServiceName", out IServiceInvoker serviceToInvoke))
                    {
                        await serviceToInvoke.InvokeServiceAsync(message, cancellationToken, false);
                    }
                }
                catch (Exception ex)
                {
                    _logger?.LogError(ex, $"Unhandled exception occurred in method:'{nameof(RecordQueueFunction)}'");
                    throw;
                }
            }
        }
    }
public class ServiceConfigurationProvider
{
    readonly IConfiguration _configuration;
    public ServiceConfigurationProvider(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    public bool IsDisabled(MethodInfo method)
    {
        Boolean returnValue = false;
        String resultConfiguration = _configuration[$"{method.Name}Disable"];
        if (!String.IsNullOrEmpty(resultConfiguration))
        {
            Boolean.TryParse(resultConfiguration, out returnValue);
        }
        return returnValue;
    }
}

【讨论】:

  • 感谢您的回复。但问题是我有一个 Web 作业,它已经有一个在 REST Api (通过 Web 挂钩)上手动调用的现有函数,并且同样被 NoAutomaticTrigger 属性装饰,我现在正在尝试添加另一个函数按计划触发。您的代码看起来不错,但如果有一种方法可以确定需要在 Web 挂钩触发器上调用特定服务\函数,那就太好了
  • 您的意思是我需要修改 REST api 以向队列添加一些虚拟值,以便可以在该特定队列触发器上触发相应的 Web 作业功能。我的理解正确还是我遗漏了什么
  • 我现在明白了。您正在关注SDK Link 的手动触发示例。要执行您想要的操作,您必须有一个连续的 Web 作业并使用 Azure 存储队列触发手动部分。更改您的 program.cs,以便您不会专门调用函数并删除对“等待 host.StopAsync();”的调用然后将您的手动功能更改为从存储队列触发。最后添加新的预定函数。
猜你喜欢
  • 2020-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-03
  • 2019-02-19
  • 2012-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多