Como usar o SDK do Azure WebJobs para o processamento em segundo plano controlado por evento

Este artigo fornece orientações sobre como trabalhar com o SDK do Azure WebJobs. Para começar a usar o WebJobs imediatamente, confira Introdução ao SDK do Azure WebJobs.

Versões do SDK do WebJobs

Essas são as principais diferenças entre a versão 3.x e a versão 2.x do SDK do WebJobs:

Várias descrições deste artigo fornecem exemplos para as versões 3.x e 2.x do WebJobs.

O Azure Functions é baseado no SDK de WebJobs.

  • A versão 2.x do Azure Functions é baseada na versão 3.x do SDK do WebJobs.
  • A versão 1.x do Azure Functions é baseada na versão 2.x do SDK do WebJobs.

Os repositórios de código-fonte do Azure Functions e do SDK do WebJobs usam a numeração do SDK do WebJobs. Várias seções deste artigo de instruções são vinculadas à documentação do Azure Functions.

Para obter mais informações, confira Comparar o SDK do WebJobs e o Azure Functions

Host do WebJobs

O host é um contêiner de runtime para funções. O host escuta gatilhos e chamadas de funções. Na versão 3.x, o host é uma implementação de IHost. Na versão 2.x, use o objeto JobHost. Você cria uma instância do host em seu código e escreve um código para personalizar seu comportamento.

Essa é uma diferença importante entre usar diretamente o SDK do WebJobs e usá-lo indiretamente pelo Azure Functions. No Azure Functions, o serviço controla o host e não é possível personalizá-lo gravando o código. O Azure Functions permite que você personalize o comportamento de host por meio de configurações no arquivo host.json. Essas configurações são cadeias de caracteres, e não códigos, e usar essas cadeia de caracteres limita os tipos de personalizações possíveis.

Conexões de host

O SDK do WebJobs procura conexões do Armazenamento do Microsoft Azure e do Barramento de Serviço do Azure no arquivo local.settings.json quando você executa localmente ou no ambiente do WebJob ao executar no Azure. Por padrão, o SDK do WebJobs requer uma conexão de armazenamento com o nome AzureWebJobsStorage.

Quando o nome da conexão é resolvido para um único valor exato, o runtime identifica o valor como uma cadeia de conexão, que normalmente inclui um segredo. Os detalhes de uma cadeia de conexão dependem do serviço ao qual você se conecta. No entanto, um nome de conexão também pode se referir a uma coleção de vários itens de configuração, útil para configurar conexões baseadas em identidade. As variáveis de ambiente podem ser tratadas como uma coleção ao usar um prefixo compartilhado que termina em sublinhados duplos __. Em seguida, o grupo pode ser referenciado ao definir o nome da conexão como esse prefixo.

Por exemplo, a propriedade connection de uma definição de gatilho de Blob do Azure pode ser Storage1. Desde que não haja um único valor de cadeia de caracteres configurado por uma variável de ambiente chamada Storage1, uma variável de ambiente chamada Storage1__blobServiceUri pode ser usada para informar a propriedade blobServiceUri da conexão. As propriedades de conexão são diferentes para cada serviço. Confira a documentação para ver o componente que usa a conexão.

Conexões baseadas em identidade

Para usar conexões baseadas em identidade no SDK do WebJobs, verifique se você está usando as versões mais recentes de pacotes WebJobs no seu projeto. Você também deve garantir que tenha uma referência a Microsoft.Azure.WebJobs.Host.Storage. Veja a seguir um exemplo de como o arquivo de projeto pode ficar depois de fazer essas atualizações:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net48</TargetFramework>
    <IsWebJobProject>true</IsWebJobProject>
    <WebJobName>$(AssemblyName)</WebJobName>
    <WebJobType>Continuous</WebJobType>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.41" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage.Queues" Version="5.3.1" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Host.Storage" Version="5.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
  </ItemGroup>

  <ItemGroup>
    <None Update="appsettings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>

Ao configurar WebJobs no seu HostBuilder, inclua uma chamada para AddAzureStorageCoreServices, pois é isso que permite que AzureWebJobsStorage e outros gatilhos de armazenamento e associações usem a identidade:

    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
        // other configurations...
    });

Em seguida, você pode configurar a conexão AzureWebJobsStorage definindo variáveis de ambiente (ou Configurações de Aplicativo quando hospedadas no Serviço de Aplicativo):

Variável de ambiente Descrição Valor de exemplo
AzureWebJobsStorage__blobServiceUri O URI do plano de dados do serviço de blobs da conta de armazenamento, usando o esquema HTTPS. https://<storage_account_name>.blob.core.windows.net
AzureWebJobsStorage__queueServiceUri O URI do plano de dados do serviço de filas da conta de armazenamento, usando o esquema HTTPS. https://<storage_account_name>.queue.core.windows.net

Se você fornecer sua configuração por meio de qualquer meio diferente das variáveis de ambiente, como com um appsettings.json, você precisará fornecer uma configuração estruturada para a conexão e suas propriedades:

{
    "AzureWebJobsStorage": {
        "blobServiceUri": "https://<storage_account_name>.blob.core.windows.net",
        "queueServiceUri": "https://<storage_account_name>.queue.core.windows.net"
    }
}

Você pode omitir a propriedade queueServiceUri se não planeja usar gatilhos de blob.

Quando o código for executado localmente, o padrão será usar sua identidade de desenvolvedor de acordo com o comportamento descrito para DefaultAzureCredential.

Quando o código estiver hospedado no Serviço de Aplicativo do Azure, a configuração mostrada acima usará como padrão a identidade gerenciada atribuída pelo sistema para o recurso. Em vez disso, para usar uma identidade atribuída pelo usuário, que foi atribuída ao aplicativo, você precisa adicionar propriedades adicionais para sua conexão que especifique qual identidade deve ser usada. A propriedade credential (AzureWebJobsStorage__credential como uma variável de ambiente) deve ser definida como a cadeia de caracteres "managedidentity". A propriedade clientId (AzureWebJobsStorage__clientId como uma variável de ambiente) deve ser definida como a ID do cliente da identidade gerenciada atribuída pelo usuário a ser usada. Como configuração estruturada, o objeto completo seria:

{
    "AzureWebJobsStorage": {
        "blobServiceUri": "https://<storage_account_name>.blob.core.windows.net",
        "queueServiceUri": "https://<storage_account_name>.queue.core.windows.net",
        "credential": "managedidentity",
        "clientId": "<user-assigned-identity-client-id>"
    }
}

A identidade usada para AzureWebJobsStorage deve ter atribuições de função que concedam as funções Proprietário de Dados do Blob de Armazenamento, Colaborador de Dados da Fila de Armazenamento e Colaborador da Conta de Armazenamento. Você pode omitir o Colaborador de Dados da Fila de Armazenamento e o Colaborador da Conta de Armazenamento, se não planeja usar gatilhos de blob.

A tabela a seguir mostra as funções internas recomendadas ao usar gatilhos nas associações em operação normal. Seu aplicativo pode exigir mais permissões com base no código que você escrever.

Associação Exemplo de funções internas
Gatilho de blob Proprietário de Dados do Blob de Armazenamento e Colaborador de Dados da Fila de Armazenamento
Confira acima para obter também os requisitos em AzureWebJobsStorage.
Blob (entrada) Leitor de Dados do Blob de Armazenamento
Blob (saída) Proprietário de Dados do Blob de Armazenamento
Gatilho de Fila Leitor de dados da fila de armazenamento, Processador de mensagens de dados de fila de armazenamento
Fila (saída) Colaborador de dados da fila de armazenamento, Remetente de mensagens de dados de fila de armazenamento
Gatilho de Barramento de Serviço1 Receptor de Dados do Barramento de Serviço do Azure, Proprietário de Dados do Barramento de Serviço do Azure
Barramento de Serviço (saída) Remetente de dados do Barramento de Serviço do Azure

1 Para disparar os tópicos do Barramento de Serviço, a atribuição de função precisa ter escopo efetivo sobre o recurso de assinatura do Barramento de Serviço. Se apenas o tópico for incluído, ocorrerá um erro. Alguns clientes, como o portal do Azure, não expõem o recurso de assinatura do Barramento de Serviço como um escopo para atribuição de função. Nesses casos, a CLI do Azure pode ser usada. Para saber mais, confira Funções integradas do Azure para o Azure Barramento de Serviço.

Cadeias de conexão na versão 2.x

A versão 2.x do SDK não requer um nome específico. A versão 2.x permite que você use os próprios nomes para essas cadeias de conexão e armazene-as em outro lugar. Você pode definir nomes no código usando o JobHostConfiguration, desta forma:

static void Main(string[] args)
{
    var _storageConn = ConfigurationManager
        .ConnectionStrings["MyStorageConnection"].ConnectionString;

    //// Dashboard logging is deprecated; use Application Insights.
    //var _dashboardConn = ConfigurationManager
    //    .ConnectionStrings["MyDashboardConnection"].ConnectionString;

    JobHostConfiguration config = new JobHostConfiguration();
    config.StorageConnectionString = _storageConn;
    //config.DashboardConnectionString = _dashboardConn;
    JobHost host = new JobHost(config);
    host.RunAndBlock();
}

Observação

Porque a versão 3.x usa as APIs de configuração padrão do .NET Core, não há API para alterar os nomes da cadeia de conexão. Confira Desenvolver e implantar os Trabalhos Web usando o Visual Studio

Configurações de desenvolvimento do host

Você pode executar o host no modo de desenvolvimento para tornar o desenvolvimento local mais eficiente. Veja algumas das configurações alteradas automaticamente quando você executa no modo de desenvolvimento:

Propriedade Configuração de desenvolvimento
Tracing.ConsoleLevel TraceLevel.Verbose para maximizar a saída de log.
Queues.MaxPollingInterval Um valor baixo para garantir que os métodos de fila sejam disparados imediatamente.
Singleton.ListenerLockPeriod Quinze segundos para ajudar no desenvolvimento iterativo rápido.

O processo para habilitar o modo de desenvolvimento depende da versão do SDK.

Versão 3.x

Versão 3.x usa as APIs padrão do ASP.NET Core. Chame o método UseEnvironment na instância HostBuilder. Passe uma cadeia de caracteres denominada development, conforme neste exemplo:

static async Task Main()
{
    var builder = new HostBuilder();
    builder.UseEnvironment("development");
    builder.ConfigureWebJobs(b =>
            {
                b.AddAzureStorageCoreServices();
            });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Versão 2.x

A classe JobHostConfiguration tem um método UseDevelopmentSettings que habilita o modo de desenvolvimento. O exemplo a seguir mostra como usar as configurações de desempenho. Para fazer config.IsDevelopment retornar true quando ele é executado localmente, defina uma variável de ambiente local chamada AzureWebJobsEnv com o valor Development.

static void Main()
{
    config = new JobHostConfiguration();

    if (config.IsDevelopment)
    {
        config.UseDevelopmentSettings();
    }

    var host = new JobHost(config);
    host.RunAndBlock();
}

Gerenciando conexões simultâneas (versão 2.x)

Na versão 3.x, o limite de conexão assume como padrão conexões infinitas. Se, por alguma razão, você precisar alterar esse limite, poderá usar a propriedade MaxConnectionsPerServer da classe WinHttpHandler.

Na versão 2.x, você controla o número de conexões simultâneas com um host usando a API ServicePointManager.DefaultConnectionLimit. Na 2.x, é recomendável aumentar esse valor a partir do padrão 2 antes de iniciar o host do WebJobs.

Todas as solicitações HTTP de saída feitas por meio de uma função usando o fluxo de HttpClient por meio do ServicePointManager. Depois de alcançar o valor definido em DefaultConnectionLimit, ServicePointManager iniciará as solicitações de enfileiramento antes de enviá-las. Suponha que seu DefaultConnectionLimit seja definido como 2 e seu código faça 1.000 solicitações HTTP. Inicialmente, apenas duas solicitações são permitidas por meio do sistema operacional. As outras 998 são colocadas na fila até que haja espaço para elas. Isso significa que seu HttpClient poderá atingir o tempo limite, porque ele parece ter feito a solicitação, mas esta nunca foi enviada pelo sistema operacional para o servidor de destino. Para que você possa ver o comportamento que não faz sentido: o HttpClient local está demorando 10 segundos para concluir uma solicitação, mas o serviço está retornando cada solicitação em 200 ms.

O valor padrão para aplicativos ASP.NET é Int32.MaxValue, e é provável que funcione bem para WebJobs em execução em um Plano do Serviço de Aplicativo Básico ou superior. Normalmente, os WebJobs precisam da configuração Always On, que é suportada apenas pelos planos Básico e superiores do Serviço de Aplicativo.

Caso o WebJob esteja em execução em um Plano do Serviço de Aplicativo Gratuito ou Compartilhado, seu aplicativo ficará restrito à área restrita do Serviço de Aplicativo, que atualmente tem um limite de conexão de 300. Com um limite de conexão não associado no ServicePointManager, é mais provável que o limite de conexão de área restrita seja atingido e o site seja desativado. Nesse caso, a configuração de DefaultConnectionLimit para algo inferior, como 50 ou 100, pode evitar que isso aconteça e ainda permitir uma taxa de transferência suficiente.

A configuração deve ser definida antes de todas as solicitações HTTP serem feitas. Por esse motivo, o host do WebJobs não deve ajustar a configuração automaticamente. Poderá haver solicitações HTTP que ocorrem antes do início do host, o que pode levar a um comportamento inesperado. A melhor abordagem é definir o valor imediatamente no método Main antes de inicializar o JobHost, conforme mostrado aqui:

static void Main(string[] args)
{
    // Set this immediately so that it's used by all requests.
    ServicePointManager.DefaultConnectionLimit = Int32.MaxValue;

    var host = new JobHost();
    host.RunAndBlock();
}

Gatilhos

O SDK dos WebJobs dá suporte ao mesmo conjunto de gatilhos e associação usados ​​pelo Azure Functions. Observe que, no SDK dos WebJobs, os gatilhos são específicos da função e não estão relacionados ao tipo de implantação do WebJob. WebJobs com funções disparadas por evento criadas usando o SDK devem sempre ser publicados como um WebJob contínuo, com Always On ativado.

As funções devem ser métodos públicos e devem ter um atributo de gatilho ou o atributo NoAutomaticTrigger.

Gatilhos automáticos

Os disparadores automáticos chamam uma função em resposta a um evento. Considere este exemplo de uma função que é disparada por uma mensagem adicionada ao armazenamento de Filas do Azure. A função responde lendo um blob do Armazenamento de Blobs do Azure:

public static void Run(
    [QueueTrigger("myqueue-items")] string myQueueItem,
    [Blob("samples-workitems/{queueTrigger}", FileAccess.Read)] Stream myBlob,
    ILogger log)
{
    log.LogInformation($"BlobInput processed blob\n Name:{myQueueItem} \n Size: {myBlob.Length} bytes");
}

O atributo QueueTrigger informa o runtime para chamar a função sempre que uma mensagem de fila aparecer em myqueue-items. O atributo Blob informa o tempo de execução para usar a mensagem da fila para ler um blob no contêiner sample-workitems. O nome do item de blob no contêiner samples-workitems é obtido diretamente no gatilho de fila como uma expressão de associação ({queueTrigger}).

Observação

Um aplicativo Web pode atingir o tempo limite após 20 minutos de inatividade, e somente as solicitações ao aplicativo Web real podem redefinir o temporizador. As ações de exibir a configuração do aplicativo no portal do Azure ou fazer solicitações para o site de ferramentas avançadas (https://<app_name>.scm.azurewebsites.net) não reiniciam o temporizador. Se você definir o aplicativo Web que hospeda seu trabalho para ser executado continuamente, executado por agenda ou usar gatilhos orientados a eventos, habilite a configuração Always On na página Configuração do Azure do aplicativo Web. A configuração Always On ajuda a garantir que esses tipos de WebJobs sejam executados de maneira confiável. Este recurso está disponível apenas nos tipos de preço Básico, Standard e Premium.

Gatilhos manuais

Para disparar uma função manualmente, use o atributo NoAutomaticTrigger, conforme mostrado aqui:

[NoAutomaticTrigger]
public static void CreateQueueMessage(
ILogger logger,
string value,
[Queue("outputqueue")] out string message)
{
    message = value;
    logger.LogInformation("Creating queue message: ", message);
}

O processo para disparar a função manualmente depende da versão do SDK.

Versão 3.x

static async Task Main(string[] args)
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
        b.AddAzureStorage();
    });
    var host = builder.Build();
    using (host)
    {
        var jobHost = host.Services.GetService(typeof(IJobHost)) as JobHost;
        var inputs = new Dictionary<string, object>
        {
            { "value", "Hello world!" }
        };

        await host.StartAsync();
        await jobHost.CallAsync("CreateQueueMessage", inputs);
        await host.StopAsync();
    }
}

Versão 2.x

static void Main(string[] args)
{
    JobHost host = new JobHost();
    host.Call(typeof(Program).GetMethod("CreateQueueMessage"), new { value = "Hello world!" });
}

Associações de entrada e saída

As associações de entrada e saída fornecem uma maneira declarativa para criar dados a partir do Azure ou de serviços externos disponíveis para seu código. As associações de saída fornecem uma maneira de atualizar os dados. O artigo Introdução mostra um exemplo de cada um.

Você pode usar um valor de retorno do método para uma associação de saída, aplicando o atributo ao valor de retorno do método. Consulte o exemplo em Usando o valor de retorno da Função do Azure.

Tipos de associação

O processo de instalação e gerenciamento de tipos de associação depende se você está usando a versão 3.x ou a versão 2.x do SDK. Você pode encontrar o pacote a ser instalado para um tipo de associação específico na seção “Pacotes” desse tipo de associação no artigo de referência do Azure Functions. Uma exceção é o gatilho e a associação de Arquivos (para o sistema de arquivos local), o que não é suportado pelo Azure Functions.

Versão 3.x

Na versão 3.x, as associações de armazenamento são incluídas no pacote Microsoft.Azure.WebJobs.Extensions.Storage. Chame o método de extensão AddAzureStorage no método ConfigureWebJobs, como mostrado aqui:

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
            {
                b.AddAzureStorageCoreServices();
                b.AddAzureStorage();
            });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Para usar outros tipos de gatilho e associação, instale o pacote do NuGet que os contém e chame o método de extensão Add<binding> implementado na extensão. Por exemplo, se você quiser usar uma associação do Azure Cosmos DB, instale Microsoft.Azure.WebJobs.Extensions.CosmosDB e chame AddCosmosDB, desta forma:

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
            {
                b.AddAzureStorageCoreServices();
                b.AddCosmosDB();
            });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Para usar o gatilho de Temporizador ou a associação de Arquivos, que fazem parte dos serviços principais, chame os métodos de extensão AddTimers ou AddFiles.

Versão 2.x

Esses tipos de gatilho e de associação estão incluídos na versão 2.x do pacote Microsoft.Azure.WebJobs:

  • Armazenamento de blob
  • Armazenamento de filas
  • Armazenamento de tabela

Para usar outros tipos de associação e gatilho, instale o pacote do NuGet que os contém e chame um método Use<binding> no objeto JobHostConfiguration. Por exemplo, se você quiser usar um gatilho de Temporizador, instale Microsoft.Azure.WebJobs.Extensions e chame UseTimers no método Main, como mostrado aqui:

static void Main()
{
    config = new JobHostConfiguration();
    config.UseTimers();
    var host = new JobHost(config);
    host.RunAndBlock();
}

Para usar a associação de Arquivos, instale Microsoft.Azure.WebJobs.Extensions e chame UseFiles.

ExecutionContext

O WebJobs permite que você se associe a um ExecutionContext. Com essa associação, você pode acessar o ExecutionContext como um parâmetro em sua assinatura de função. Por exemplo, o código a seguir usa o objeto de contexto para acessar a ID de invocação, que você pode usar para correlacionar todos os logs produzidos por uma determinada chamada de função.

public class Functions
{
    public static void ProcessQueueMessage([QueueTrigger("queue")] string message,
        ExecutionContext executionContext,
        ILogger logger)
    {
        logger.LogInformation($"{message}\n{executionContext.InvocationId}");
    }
}

O processo de associação ao ExecutionContext depende da versão do SDK.

Versão 3.x

Chame o método de extensão AddExecutionContextBinding no método ConfigureWebJobs, como mostrado aqui:

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
            {
                b.AddAzureStorageCoreServices();
                b.AddExecutionContextBinding();
            });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Versão 2.x

O pacote Microsoft.Azure.WebJobs.Extensions mencionado anteriormente também fornece um tipo especial de associação que você pode registrar chamando o método UseCore. Essa associação permite definir um parâmetro ExecutionContext em sua assinatura de função, que é habilitada da seguinte maneira:

class Program
{
    static void Main()
    {
        config = new JobHostConfiguration();
        config.UseCore();
        var host = new JobHost(config);
        host.RunAndBlock();
    }
}

Configuração de associação

Você pode configurar o comportamento de alguns gatilhos e associações. O processo para configurá-los depende da versão do SDK.

  • Versão 3.x: Defina a configuração quando o método Add<Binding> for chamado em ConfigureWebJobs.
  • Versão 2.x: Defina a configuração definindo as propriedades em um objeto de configuração que você passa para JobHost.

Essas configurações específicas de associação são equivalentes às configurações no arquivo de projeto host.json no Azure Functions.

É possível configurar as seguintes associações:

Configuração do gatilho do Azure Cosmos DB (versão 3.x)

Este exemplo mostra como configurar o gatilho do Azure Cosmos DB:

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
        b.AddCosmosDB(a =>
        {
            a.ConnectionMode = ConnectionMode.Gateway;
            a.Protocol = Protocol.Https;
            a.LeaseOptions.LeasePrefix = "prefix1";

        });
    });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Para obter mais detalhes, consulte o artigo Associação do Azure Cosmos DB.

Configuração do gatilho dos Hubs de Eventos (versão 3.x)

Este exemplo mostra como configurar o gatilho dos Hubs de Eventos:

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
        b.AddEventHubs(a =>
        {
            a.BatchCheckpointFrequency = 5;
            a.EventProcessorOptions.MaxBatchSize = 256;
            a.EventProcessorOptions.PrefetchCount = 512;
        });
    });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Para obter mais informações, confira o artigo Associação de Hubs de Eventos.

Configuração do gatilho de armazenamento de Filas

Os exemplos a seguir mostram como configurar o gatilho de armazenamento de Filas.

Versão 3.x

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
        b.AddAzureStorage(a => {
            a.BatchSize = 8;
            a.NewBatchThreshold = 4;
            a.MaxDequeueCount = 4;
            a.MaxPollingInterval = TimeSpan.FromSeconds(15);
        });
    });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Para obter mais informações, confira o artigo Associação do armazenamento de filas.

Versão 2.x

static void Main(string[] args)
{
    JobHostConfiguration config = new JobHostConfiguration();
    config.Queues.BatchSize = 8;
    config.Queues.NewBatchThreshold = 4;
    config.Queues.MaxDequeueCount = 4;
    config.Queues.MaxPollingInterval = TimeSpan.FromSeconds(15);
    JobHost host = new JobHost(config);
    host.RunAndBlock();
}

Para obter mais informações, confira a referência para host.json v1.x.

Configuração de associação SendGrid (versão 3.x)

Este exemplo mostra como configurar a associação de saída SendGrid:

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
        b.AddSendGrid(a =>
        {
            a.FromAddress.Email = "samples@functions.com";
            a.FromAddress.Name = "Azure Functions";
        });
    });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Para obter mais informações, confira o artigo Associação de SendGrid.

Configuração do gatilho do Barramento de Serviço (versão 3.x)

Este exemplo mostra como configurar o gatilho do Barramento de Serviço:

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
        b.AddServiceBus(sbOptions =>
        {
            sbOptions.MessageHandlerOptions.AutoComplete = true;
            sbOptions.MessageHandlerOptions.MaxConcurrentCalls = 16;
        });
    });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Para obter mais detalhes, consulte o artigo Associação do Barramento de Serviço.

Configuração de outras associações

Alguns tipos de gatilho e associação definem seus próprios tipos de configuração personalizados. Por exemplo, o gatilho de Arquivo permite especificar o caminho raiz de monitoramento, conforme mostrado nos exemplos a seguir.

Versão 3.x

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
        b.AddFiles(a => a.RootPath = @"c:\data\import");
    });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Versão 2.x

static void Main()
{
    config = new JobHostConfiguration();
    var filesConfig = new FilesConfiguration
    {
        RootPath = @"c:\data\import"
    };
    config.UseFiles(filesConfig);
    var host = new JobHost(config);
    host.RunAndBlock();
}

Expressões de associação

Nos parâmetros do construtor de atributo, você pode usar expressões que são resolvidas para valores de várias fontes. Por exemplo, no código a seguir, o caminho para o atributo BlobTrigger cria uma expressão nomeada filename. Quando usado para a associação de saída, filename é resolvido para o nome do blob de disparo.

public static void CreateThumbnail(
    [BlobTrigger("sample-images/{filename}")] Stream image,
    [Blob("sample-images-sm/{filename}", FileAccess.Write)] Stream imageSmall,
    string filename,
    ILogger logger)
{
    logger.Info($"Blob trigger processing: {filename}");
    // ...
}

Para obter mais informações sobre expressões de associação, consulte Padrões e expressões de associação na documentação do Azure Functions.

Expressões de associação personalizadas

Às vezes, você deseja especificar um nome de fila, um nome de blob ou contêiner ou um nome de tabela no código em vez de embuti-lo no código. Por exemplo, você talvez queira especificar o nome da fila para o atributo QueueTrigger em um arquivo de configuração ou uma variável de ambiente.

Você pode fazer isso passando um resolvedor de nome personalizado durante a configuração. Inclua espaços reservados no gatilho ou parâmetros do construtor de atributo de associação, e seu código resolvedor fornecerá os valores reais a serem usados no lugar desses espaços reservados. Você identifica espaços reservados ao redor deles com sinais de percentual (%), como mostrado aqui:

public static void WriteLog([QueueTrigger("%logqueue%")] string logMessage)
{
    Console.WriteLine(logMessage);
}

Este código lhe permite usar uma fila denominada logqueuetest no ambiente de teste e uma denominada logqueueprod na produção. Em vez de um nome de fila embutido em código, você especifica o nome de uma entrada na coleção appSettings.

Há um resolvedor padrão que entra em vigor se você não fornecer um resolvedor personalizado. O padrão obtém valores de configurações de aplicativo ou variáveis de ambiente.

A partir do .NET Core 3.1, o ConfigurationManager que você usar requer o pacote NuGet System.Configuration.ConfigurationManager. A amostra requer a seguinte declaração de uso using:

using System.Configuration;

Sua classe NameResolver obtém o nome da fila das configurações de aplicativo, conforme mostrado aqui:

public class CustomNameResolver : INameResolver
{
    public string Resolve(string name)
    {
        return ConfigurationManager.AppSettings[name].ToString();
    }
}

Versão 3.x

Você configura o resolvedor usando injeção de dependência. Esses exemplos exigem a seguinte instrução using:

using Microsoft.Extensions.DependencyInjection;

Você adiciona o resolvedor chamando o método de extensão ConfigureServices no HostBuilder, como neste exemplo:

static async Task Main(string[] args)
{
    var builder = new HostBuilder();
    var resolver = new CustomNameResolver();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
    });
    builder.ConfigureServices(s => s.AddSingleton<INameResolver>(resolver));
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Versão 2.x

Transfira a classe NameResolver para o objeto JobHost, conforme mostrado aqui:

 static void Main(string[] args)
{
    JobHostConfiguration config = new JobHostConfiguration();
    config.NameResolver = new CustomNameResolver();
    JobHost host = new JobHost(config);
    host.RunAndBlock();
}

O Azure Functions implementa INameResolver para obter valores de configurações do aplicativo, conforme mostrado no exemplo. Quando usa o WebJobs SDK diretamente, você pode escrever uma implementação personalizada que obtém valores de substituição de espaço reservado de qualquer fonte preferida.

Associando no runtime

Se você precisar fazer algum trabalho em sua função antes de usar um atributo de associação como Queue, Blob ou Table, poderá usar a interface IBinder.

O exemplo a seguir usa uma mensagem da fila de entrada e cria uma nova mensagem com o mesmo conteúdo em uma fila de saída. O nome da fila de saída é definido pelo código no corpo da função.

public static void CreateQueueMessage(
    [QueueTrigger("inputqueue")] string queueMessage,
    IBinder binder)
{
    string outputQueueName = "outputqueue" + DateTime.Now.Month.ToString();
    QueueAttribute queueAttribute = new QueueAttribute(outputQueueName);
    CloudQueue outputQueue = binder.Bind<CloudQueue>(queueAttribute);
    outputQueue.AddMessageAsync(new CloudQueueMessage(queueMessage));
}

Para obter mais informações, consulte Associação no runtime na documentação do Azure Functions.

Informações de referência de associação

A documentação do Azure Functions fornece informações de referência sobre cada tipo de associação. Você encontrará as informações a seguir em cada artigo de referência de associação. (Este exemplo é baseado na Fila de armazenamento.)

  • Pacotes. O pacote que você precisa instalar para incluir suporte para a associação em um projeto do SDK do WebJobs.
  • Exemplos. Exemplos de código. O exemplo da biblioteca de classes C# se aplica ao SDK do WebJobs. Basta omitir o atributo FunctionName.
  • Atributos. Os atributos a serem usados para o tipo de associação.
  • Configuração. Explicações sobre as propriedades de atributo e os parâmetros do construtor.
  • Uso. A quais tipos você pode associar e informações sobre como a associação funciona. Por exemplo: algoritmo de sondagem, processamento de fila de mensagens suspeita.

Observação

As associações de HTTP, Webhooks e Grade de Eventos são suportadas somente pelo Azure Functions, não pelo SDK dos WebJobs.

Para obter uma lista completa de associações com suporte no runtime do Azure Functions, confira Associações com suporte.

Atributos de Disable, Timeout e Singleton

Com esses atributos, você pode controlar o gatilho de função, cancelar funções e garantir que apenas uma instância de uma função seja executada.

Atributo Desabilitar

O atributo Disable permite que você controle se uma função pode ser disparada.

No exemplo a seguir, se a configuração do aplicativo Disable_TestJob tiver um valor 1 ou True (sem distinção entre maiúsculas e minúsculas), a função não será executada. Nesse caso, o runtime cria uma mensagem de log A função 'Functions.TestJob' está desabilitada.

[Disable("Disable_TestJob")]
public static void TestJob([QueueTrigger("testqueue2")] string message)
{
    Console.WriteLine("Function with Disable attribute executed!");
}

Quando você altera os valores de configuração do aplicativo no portal do Azure, o WebJob é reiniciado para selecionar a nova configuração.

O atributo pode ser declarado no nível de classe, método ou parâmetro. O nome da configuração pode também conter expressões de associação.

Atributo de tempo limite

O atributo Timeout faz com que uma função seja cancelada se ela não for concluída dentro de um período especificado. No exemplo a seguir, a função seria executada por um dia sem o atributo Timeout. O Timeout faz com que a função seja cancelada após 15 segundos. Quando o parâmetro "throwOnError" do atributo Timeout é definido como "true", a invocação de função é encerrada por ter uma exceção gerada pelo SDK do webjobs quando o intervalo de tempo limite é excedido. O valor padrão de "throwOnError" é "false". Quando o atributo Timeout é usado, o comportamento padrão é cancelar a invocação de função definindo o token de cancelamento, permitindo que a invocação seja executada indefinidamente até que o código de função retorne ou gere uma exceção.

[Timeout("00:00:15")]
public static async Task TimeoutJob(
    [QueueTrigger("testqueue2")] string message,
    CancellationToken token,
    TextWriter log)
{
    await log.WriteLineAsync("Job starting");
    await Task.Delay(TimeSpan.FromDays(1), token);
    await log.WriteLineAsync("Job completed");
}

Você pode aplicar o atributo Timeout no nível de classe ou método e especificar um tempo limite global usando JobHostConfiguration.FunctionTimeout. Os tempos limites no nível de classe ou de método substituem os tempos limites globais.

Atributo Singleton

Use o atributo Singleton assegura que apenas uma instância de uma função seja executada, mesmo quando houver várias instâncias do aplicativo Web host. O atributo Singleton usa bloqueio distribuído para garantir a execução de uma instância.

Neste exemplo, apenas uma única instância da função ProcessImage é executada em um determinado momento:

[Singleton]
public static async Task ProcessImage([BlobTrigger("images")] Stream image)
{
     // Process the image.
}

SingletonMode.Listener

Alguns gatilhos têm suporte interno para gerenciamento de simultaneidade:

  • QueueTrigger. Defina JobHostConfiguration.Queues.BatchSize como 1.
  • ServiceBusTrigger. Defina ServiceBusConfiguration.MessageOptions.MaxConcurrentCalls como 1.
  • FileTrigger. Defina FileProcessor.MaxDegreeOfParallelism como 1.

Você pode usar essas configurações para garantir que sua função seja executada como um singleton em uma única instância. Para garantir que apenas uma única instância da função esteja em execução quando o aplicativo Web for expandido para várias instâncias, aplique um bloqueio de Singleton em nível de ouvinte na função ([Singleton(Mode = SingletonMode.Listener)]). Os bloqueios de ouvinte são adquiridos quando o JobHost é iniciado. Se três instâncias expandidas forem iniciadas ao mesmo tempo, somente uma das instâncias adquirirá o bloqueio e somente um ouvinte será iniciado.

Observação

Consulte este Repositório do GitHub para saber mais sobre como o SingletonMode.Function funciona.

Valores de escopo

Você pode especificar uma expressão/valor de escopo em um singleton. A expressão/valor garante que todas as execuções da função em um escopo específico serão serializadas. Implementar um bloqueio mais granular dessa maneira pode permitir algum nível de paralelismo para sua função e, ao mesmo tempo, serializar outras invocações, conforme definido pelos seus requisitos. Por exemplo, no código a seguir, a expressão de escopo é associada ao valor Region da mensagem de entrada. Quando a fila contém três mensagens nas regiões leste, leste e oeste, as mensagens que têm a região leste são executadas em série. A mensagem da região oeste é executada paralelamente às mensagens da região leste.

[Singleton("{Region}")]
public static async Task ProcessWorkItem([QueueTrigger("workitems")] WorkItem workItem)
{
     // Process the work item.
}

public class WorkItem
{
     public int ID { get; set; }
     public string Region { get; set; }
     public int Category { get; set; }
     public string Description { get; set; }
}

SingletonScope.Host

O escopo padrão para um bloqueio é SingletonScope.Function, o que significa que o escopo de bloqueio (o caminho de concessão do blob) está vinculado ao nome da função totalmente qualificada. Para bloquear funções, especifique SingletonScope.Host e use um nome de ID de escopo que é o mesmo em todas as funções que você não deseja executar simultaneamente. No exemplo a seguir, apenas uma instância de AddItem ou RemoveItem é executada por vez:

[Singleton("ItemsLock", SingletonScope.Host)]
public static void AddItem([QueueTrigger("add-item")] string message)
{
     // Perform the add operation.
}

[Singleton("ItemsLock", SingletonScope.Host)]
public static void RemoveItem([QueueTrigger("remove-item")] string message)
{
     // Perform the remove operation.
}

Exibição de blobs de concessão

O WebJobs SDK usa concessões de blob do Azure nos bastidores para implementar o bloqueio distribuído. Os blobs de concessão usados pelo Singleton podem ser encontrados no contêiner azure-webjobs-host na conta de armazenamento AzureWebJobsStorage no caminho “bloqueios”. Por exemplo, o caminho de blob de concessão para o primeiro exemplo ProcessImage mostrado anteriormente pode ser locks/061851c758f04938a4426aa9ab3869c0/WebJobs.Functions.ProcessImage. Todos os caminhos incluem a ID de JobHost, 061851c758f04938a4426aa9ab3869c0 neste caso.

Funções assíncronas

Para obter informações sobre como codificar funções assíncronas, consulte a documentação do Azure Functions.

Tokens de cancelamento

Para obter informações sobre como lidar com tokens de cancelamento, consulte a documentação do Azure Functions nos tokens de cancelamento e desligamento normal.

Várias instâncias

Se o seu aplicativo Web for executado em várias instâncias, um WebJob contínuo será executado em cada instância, ouvindo os gatilhos e chamando funções. As várias associações de gatilho são projetadas para compartilhar com eficiência o trabalho de forma colaborativa entre instâncias, para que a expansão para mais instâncias permita que você manipule mais carga.

Embora alguns gatilhos possam resultar em processamento duplo, os gatilhos de armazenamento de fila e de blob impedem automaticamente que uma função processe uma mensagem de fila ou de blob mais de uma vez. Para obter mais informações, consulte Design para entrada idêntica na documentação do Azure Functions.

O gatilho de timer garante que apenas uma instância do timer seja executada, portanto você não terá mais de uma instância de função em execução em um determinado horário agendado.

No entanto, se desejar garantir que apenas uma instância de uma função seja executada, mesmo quando houver várias instâncias do aplicativo Web host, é possível usar o atributo Singleton.

Filtros

Filtros de função (visualização) fornecem uma maneira de personalizar o pipeline de execução de WebJobs com sua própria lógica. Os filtros são semelhantes aos filtros do ASP.NET Core. Você pode implementá-los como atributos declarativos que são aplicados às suas funções ou classes. Para obter mais informações, consulte Filtros de Função.

Log e monitoramento

Recomendamos a estrutura de registro em log que foi desenvolvida para ASP.NET. O artigo Introdução mostra como usá-lo.

Filtragem de linha

Cada log criado por uma instância de ILogger possui um Category e Level associados. LogLevel é uma enumeração e o código inteiro indica importância relativa:

LogLevel Código
Trace 0
Depurar 1
Informações 2
Aviso 3
Erro 4
Crítico 5
Nenhum 6

Você pode filtrar de forma independente cada categoria em um determinado LogLevel. Por exemplo, você talvez queira ver todos os logs para o processamento de gatilho de blob, mas apenas Error e superiores para todo o resto.

Versão 3.x

A versão 3.x do SDK depende da filtragem integrada ao .NET Core. A classe LogCategories permite que você defina categorias para funções, gatilhos e usuários específicos. Ela também define filtros para estados de host específicos, como Startup e Results. Isso permite que você ajuste a saída de log. Se nenhuma correspondência for encontrada nas categorias definidas, o filtro reverterá para o valor Default ao decidir se deseja filtrar a mensagem.

LogCategories requer a seguinte declaração de uso:

using Microsoft.Azure.WebJobs.Logging; 

O exemplo a seguir constrói um filtro que, por padrão, filtra todos os logs no nível de Warning. As categorias Function e results (equivalente a Host.Results na versão 2.x) são filtradas no nível de Error. O filtro compara a categoria atual com todos os níveis registrados na instância LogCategories e escolhe a maior correspondência. Isso significa que o nível de Debug registrado para Host.Triggers corresponde a Host.Triggers.Queue ou Host.Triggers.Blob. Isso permite que você controle categorias mais amplas sem a necessidade de adicionar cada uma delas.

static async Task Main(string[] args)
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
    });
    builder.ConfigureLogging(logging =>
            {
                logging.SetMinimumLevel(LogLevel.Warning);
                logging.AddFilter("Function", LogLevel.Error);
                logging.AddFilter(LogCategories.CreateFunctionCategory("MySpecificFunctionName"),
                    LogLevel.Debug);
                logging.AddFilter(LogCategories.Results, LogLevel.Error);
                logging.AddFilter("Host.Triggers", LogLevel.Debug);
            });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Versão 2.x

Na versão 2.x do SDK, use a classe LogCategoryFilter para controlar a filtragem. O LogCategoryFilter tem uma propriedade Default com um valor inicial de Information, o que significa que todas as mensagens nos níveis Information, Warning, Error ou Critical são registradas, mas todas as mensagens nos níveis Debug ou Trace são filtradas.

Tal como acontece com LogCategories na versão 3.x, a propriedade CategoryLevels permite que você especifique níveis de log para categorias específicas para que você possa ajustar a saída de log. Se nenhuma correspondência for encontrada no dicionário CategoryLevels, o filtro reverterá para o valor Default ao decidir se deseja filtrar a mensagem.

O exemplo a seguir constrói um filtro que, por padrão, filtra todos os logs no nível de Warning. As categorias Function ou Host.Results são filtradas no nível de Error. O LogCategoryFilter compara a categoria atual com todos os CategoryLevels registrados e escolhe a maior correspondência. Portanto, o nível de Debug registrado para Host.Triggers corresponderá a Host.Triggers.Queue ou Host.Triggers.Blob. Isso permite que você controle categorias mais amplas sem a necessidade de adicionar cada uma delas.

var filter = new LogCategoryFilter();
filter.DefaultLevel = LogLevel.Warning;
filter.CategoryLevels[LogCategories.Function] = LogLevel.Error;
filter.CategoryLevels[LogCategories.Results] = LogLevel.Error;
filter.CategoryLevels["Host.Triggers"] = LogLevel.Debug;

config.LoggerFactory = new LoggerFactory()
    .AddApplicationInsights(instrumentationKey, filter.Filter)
    .AddConsole(filter.Filter);

Telemetria personalizada do Application Insights​

O processo de implementação de telemetria personalizada para Application Insights depende da versão do SDK. Para saber como configurar o Application Insights, confira Adicionar registro em log do Application Insights.

Versão 3.x

Como a versão 3.x do SDK do WebJobs depende do host genérico do .NET Core, um alocador de telemetria personalizada já não é mais fornecido. No entanto, você pode adicionar telemetria personalizada ao pipeline usando a injeção de dependência. Os exemplos nesta seção exigem as seguintes declarações using:

using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Channel;

A seguinte implementação personalizada de ITelemetryInitializer permite que você adicione seu próprio ITelemetry ao padrão TelemetryConfiguration.

internal class CustomTelemetryInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        // Do something with telemetry.
    }
}

Chame ConfigureServices no construtor para adicionar seu item personalizado ITelemetryInitializer ao pipeline.

static async Task Main()
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
    });
    builder.ConfigureLogging((context, b) =>
    {
        // Add logging providers.
        b.AddConsole();

        // If this key exists in any config, use it to enable Application Insights.
        string appInsightsKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
        if (!string.IsNullOrEmpty(appInsightsKey))
        {
            // This uses the options callback to explicitly set the instrumentation key.
            b.AddApplicationInsights(o => o.InstrumentationKey = appInsightsKey);
        }
    });
    builder.ConfigureServices(services =>
        {
            services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();
        });
    var host = builder.Build();
    using (host)
    {
        await host.RunAsync();
    }
}

Quando o TelemetryConfiguration é construído, todos os tipos registrados de ITelemetryInitializer são incluídos. Para saber mais, consulte API do Application Insights para métricas e eventos personalizados.

Na versão 3.x, você já não precisa liberar o TelemetryClient quando o host é interrompido. O sistema de injeção de dependência do .NET Core automaticamente descarta o ApplicationInsightsLoggerProvider registrado que libera o TelemetryClient.

Versão 2.x

Na versão 2.x, o TelemetryClient criado internamente pelo provedor do Application Insights para o SDK do WebJobs usa ServerTelemetryChannel. Quando o ponto de extremidade do Application Insights está indisponível ou limitando solicitações de entrada, este canal salva as solicitações no sistema de arquivos do aplicativo Web e reenvia-as depois.

O TelemetryClient é criado por uma classe que implementa ITelemetryClientFactory. Por padrão, é DefaultTelemetryClientFactory.

Se quiser modificar qualquer parte do pipeline do Application Insights, você poderá fornecer seu próprio ITelemetryClientFactory, e o host usará sua classe para construir um TelemetryClient. Por exemplo, esse código substitui DefaultTelemetryClientFactory para modificar uma propriedade de ServerTelemetryChannel:

private class CustomTelemetryClientFactory : DefaultTelemetryClientFactory
{
    public CustomTelemetryClientFactory(string instrumentationKey, Func<string, LogLevel, bool> filter)
        : base(instrumentationKey, new SamplingPercentageEstimatorSettings(), filter)
    {
    }

    protected override ITelemetryChannel CreateTelemetryChannel()
    {
        ServerTelemetryChannel channel = new ServerTelemetryChannel();

        // Change the default from 30 seconds to 15 seconds.
        channel.MaxTelemetryBufferDelay = TimeSpan.FromSeconds(15);

        return channel;
    }
}

O objeto SamplingPercentageEstimatorSettings configura a amostragem adaptável. Isso significa que em determinados cenários de alto volume, o Application Insights envia um subconjunto selecionado de dados de telemetria para o servidor.

Depois de criar o alocador de telemetria, transfira-o para o provedor de logs do Application Insights:

var clientFactory = new CustomTelemetryClientFactory(instrumentationKey, filter.Filter);

config.LoggerFactory = new LoggerFactory()
    .AddApplicationInsights(clientFactory);

Próximas etapas

Este artigo forneceu snippets de código que mostram como lidar com cenários comuns para trabalhar com o SDK do WebJobs. Para obter exemplos completos, consulte azure-webjobs-sdk-samples.