Возможно ли, чтобы одно веб-задание имело функцию, запускаемую по таймеру, а также функцию, запускаемую вручную - PullRequest
0 голосов
/ 17 октября 2019

У меня есть существующее веб-задание (V3.0) в ядре .net, которое имеет функцию, которая вызывается ручным триггером, в основном веб-крючком. Я хочу добавить еще одну функцию к тому же веб-заданию, которая должна вызываться при срабатывании таймера каждые 20 минут. Возможно ли иметь оба этих в одном и том же веб-задании. Если это возможно, что должна конфигурация хоста, что мне нужно сделать. Я попытался просмотреть документацию Microsoft, но едва ли есть какая-либо документация по части конфигурации хоста с несколькими триггерами

1 Ответ

0 голосов
/ 22 октября 2019

Да, но ваша функция должна запускаться чем-то в хранилище Azure, например, очередью. Этот код, вероятно, больше, чем вам может понадобиться. Все мои сервисы реализуют собственный интерфейс IServiceInvoker. Мой CTOR запрашивает

IEnumerable<IServiceInvoker>

, который получает все услуги. Затем я использую константу или переданное значение, чтобы определить, какую службу запустить. Поскольку я ТОЛЬКО хочу, чтобы ОДНА функция выполнялась, я использую атрибут Singleton, передаваемый в String.Empty. У меня также есть следующие настройки в моих очередях

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;
    }
}
...