使用.NET Core 2.1的Azure WebJobs

WebJobs不是Azure和.NET中的新事物。 Visual Studio 2017中甚至还有一个默认的Azure WebJob模板,用于完整的.NET Framework。 但是,Visual Studio中以某种方式遗漏了.NET Core中WebJobs的类似模板。 在这篇文章中,我使用的是.NET Core 2.1来创建WebJobs。

在.NET Core中创建WebJob并不难,但你必须知道一些技巧,特别是如果你想使用一些.NET Core好特性,比如日志和DI。

在这篇文章中,我们将构建一个WebJob并使用Visual Studio,Azure门户和VSTS将其发布到Azure。

什么是WebJobs

WebJob是在App Service后台运行的程序。 它与您的Web应用程序在相同的环境中运行,无需额外费用。 也许你需要做一些每小时任务或每天凌晨1点做一些清理工作。 Azure Application Insights使用WebJob报告应用程序的统计信息。

WebJobs可以按小时或每天安排,但也可以触发。 触发器可以是文件上载或队列上的新消息。

WebJobs 对比 Functions

我经常在WebJobs和Azure Functions之间比较。 在某种程度上,Functions是WebJobs的后继者。 Functions(通常)是在Azure中运行的一小段代码,就像WebJobs一样,在某个事件中触发,包括HTTP触发器。

Functions通常是WebJobs的一个很好的替代品,但如果你已经有了一个Web应用程序,那么使用WebJob就可以了。 特别是当您想在WebJob和Web应用程序之间共享代码或设置,这也使部署非常容易,因为它们在相同的上下文中运行。

创建一个Storage账户

在我们继续之前,先让我们先处理一下。 WebJob需要Azure Storage账户。 我将快速引导您完成创建过程。

在Azure中,找到“Storage Azure( 存储帐户)”并添加一个。 您必须选择一个在Azure中唯一的名称。 除此之外,您可以保留默认值。

一旦你的 Storage Account就绪,选择它并找到您的“Access keys(访问密钥)”。 我们稍后需要的两个连接字符串之一。

创建WebJob

前面已经说过,完整的.NET Framework有一个WebJob模板。 我建议你看看。 首先创建一个ASP.NET Web应用程序,然后添加一个新的WebJob。 如果您尝试立即创建WebJob,则会收到错误消息,指出项目需要先保存(尽管它确实创建了WebJob)。

这边文章写的是.NET Core的WebJob。 首先,创建一个ASP.NET Core Web应用程序,然后将新的.NET Core Console应用程序项目添加到您的解决方案中。

我们需要做的第一件事是从NuGet安装Microsoft.Azure.WebJobs包。 我们还应该安装Microsoft.Azure.WebJobs.Extensions。 这些库的最新稳定版本依赖于完整的.NET Framework,因此我们将需要3.0.0-beta5版本(在撰写本文时),它与.NET Core完全兼容。

我们需要的其他NuGet包是Microsoft.Extensions.Options.ConfigurationExtensions(它还提供了我们还需要的Microsoft.Extensions.Options包),Microsoft.Extensions.DependencyInjection和Microsoft.Extensions.Logging.Console。 请确保安装这些软件包的2.1.0版本,因为.NET Core 2.1中似乎存在一个错误,导致您无法使用包含修补程序版本的软件包,例如2.1.1。

添加到Program.cs文件

我们需要做的下一件事是更改我们的Program.cs文件。 如果您使用.NET Framework模板创建了WebJob,则只需复制并粘贴在那里生成的Program.cs文件(您可能需要更改命名空间)。

 

复制代码
using Microsoft.Azure.WebJobs;
 
namespace NetCoreWebJob.WebJob
{
    // To learn more about Microsoft Azure WebJobs SDK, please see https://go.microsoft.com/fwlink/?LinkID=320976
    internal class Program
    {
        // Please set the following connection strings in app.config for this WebJob to run:
        // AzureWebJobsDashboard and AzureWebJobsStorage
        private static void Main()
        {
            var config = new JobHostConfiguration();
            if (config.IsDevelopment)
            {
                config.UseDevelopmentSettings();
            }
            var host = new JobHost(config);
            // The following code ensures that the WebJob will be running continuously
            host.RunAndBlock();
        }
    }
}
复制代码

 

添加配置和依赖

您会用到.NET Core里面好的特性,比如日志和DI。 默认情况下,Console App不具备任何功能,但您可以自行添加。

复制代码
private static void Main()
{
    IServiceCollection services = new ServiceCollection();
    ConfigureServices(services);
 
    // ...
}
 
private static IConfiguration Configuration { get; set; }
 
private static void ConfigureServices(IServiceCollection services)
{
    var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
 
    Configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();
 
    services.AddSingleton(Configuration);
    services.AddTransient<Functions, Functions>();
    services.AddLogging(builder => builder.AddConsole());
}
复制代码

接下来,创建一个appsettings.json文件并将“复制到输出目录”属性设置为“始终复制”。 appsettings.json文件应该有两个连接字符串,如Program.cs模板文件中所述。 这些是我们之前创建的存储帐户连接字符串。

 

复制代码
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "ConnectionStrings": {
    "AzureWebJobsDashboard": "[your Storage Account connection string]",
    "AzureWebJobsStorage": "[your Storage Account connection string]"
  }
}
复制代码

 

接下来我们需要的是一个自定义的IJobActivator,可用于将依赖项注入到我们的类中。 它需要在Program类的JobHostConfiguration上设置。

复制代码
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.DependencyInjection;
using System;
 
namespace NetCoreWebJob.WebJob
{
    public class JobActivator : IJobActivator
    {
        private readonly IServiceProvider services;
 
        public JobActivator(IServiceProvider services)
        {
            this.services = services;
        }
 
        public T CreateInstance<T>()
        {
            return services.GetService<T>();
        }
    }
}
复制代码
var config = new JobHostConfiguration();
config.JobActivator = new JobActivator(services.BuildServiceProvider());

添加Trigger(触发器)

之后,创建一个类并将其命名为Functions(就像在WebJob模板中一样)。 Functions类将包含WebJob的实际代码。

当然,我们需要添加一个触发器。 这与完整的.NET Framework不同。 毕竟,模板使用静态方法,这使得DI无法实现。 说到DI,请注意我们还将Functions类本身添加到DI容器中。

为简单起见,我们将使用TimerTrigger,它由所谓的CRON表达式触发。 这只是意味着它在某一分钟,小时,一天等时触发。在这个例子中,它会触发每一分钟。

我们还需要在JobHostConfiguration上配置计时器。

复制代码
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using System;
 
namespace NetCoreWebJob.WebJob
{
    public class Functions
    {
        private readonly ILogger<Functions> logger;
 
        public Functions(ILogger<Functions> logger)
        {
            this.logger = logger;
        }
 
        public void ProcessQueueMessage([TimerTrigger("* * * * *")]TimerInfo timerInfo)
        {
            logger.LogInformation(DateTime.Now.ToString());
        }
    }
}
复制代码
var config = new JobHostConfiguration();
config.JobActivator = new JobActivator(services.BuildServiceProvider());
config.UseTimers();

运行示例

如果您正确地执行了所有操作,或者如果您正在从GitHub运行我的代码,那么您现在应该能够运行控制台应用程序。 如果您在例外情况下中断或者您正在观看“输出”窗口,您可能会注意到很多StorageExceptions。 不要担心它们,忽略它们。 这是WebJobs库中的一个错误,不会影响您的程序。 您的触发器可能需要一分钟才会消失,所以要有一点耐心。

如果您转到Azure存储帐户,您应该看到两个Blob容器,“azure-jobs-host-output”和“azure-webjobs-hosts”。 这里有很多东西,但你可以忽略它。 我发现我的WebJob触发器由于某种原因不会消失,删除Blob容器通常会有所帮助。 显然,存储在那里的某些状态在重新添加和删除WebJobs时并不总是正确处理掉。

 

发布到Azure

我们要做的下一件事是将WebJob部署到Azure。为了运行WebJob,需要一些可以用来运行的可执行脚本。支持许多文件类型,但对于我们Windows用户来说,使用exe,cmd,bat或PowerShell文件。

控制台应用程序曾经是一个exe文件,但在.NET Core中,它会生成一个我们需要手动启动的常规DLL文件。因此,创建一个文件并将其命名为“run.cmd”并确保它以UTF-8编码没有BOM(您可以使用Notepad ++之类的东西来检查)。它只需要一行代码,即“dotnet NetCoreWebJob.WebJob.dll”。这会运行您的控制台应用。确保将文件的“复制到输出目录”设置为“始终复制”。

最后一件事,由于某种原因,Azure WebJobs需要WebJob的所有依赖项,这意味着我们用来构建它的所有.NET Core包。您可以通过编辑csproj文件并将“<CopyLocalLockFileAssemblies> true </ CopyLocalLockFileAssemblies>”添加到第一个<PropertyGroup>(在“<TargetFramework>”下面)来完成此操作。

在我们部署WebJob之前,我们需要部署我们的Web应用程序。右键单击ASP.NET项目,然后单击“发布...”。只需按照向导操作,Visual Studio就会为您部署您的应用程序。您可以创建新的Web应用程序或选择现有的应用程序。此步骤并非严格必要,因为您可以托管独立的WebJobs,但这应该是熟悉的,它为您提供了我们可以用于WebJob的App Service。

使用Visual Studio发布

使用Visual Studio部署WebJobs应该很容易。事实上,你可能已经知道如何做到这一点(尽管不这样做)。右键单击WebJob项目,然后单击“发布...”。以下向导看起来很像我们刚刚做的Web应用程序的发布。您可以选择“选择现有”并选择我们刚创建的Azure Web应用程序。

不幸的是,微软以最糟糕的方式搞砸了这个功能。 Visual Studio将使用与项目相同的名称部署WebJob,即“NetCoreWebJob.WebJob”,除了dot是WebJob名称中的非法字符!这搞砸了我的项目非常糟糕我不得不手动编辑它以使我的解决方案再次运行。

所以这就是你做的。在向导开始时,您可以选择新的或现有的App Service,单击“立即发布”旁边的箭头,然后选择“创建配置文件”。现在,您可以先在设置中更改WebJob的名称,然后再进行部署。确保您没有选择“删除目的地的其他文件”,否则您将删除您的网络应用程序。

现在,浏览到Azure门户并查找您的Web应用程序。你会在菜单中找到“WebJobs”。你会看到你的WebJob,但它没有做任何事情。您需要通过选择它并单击“运行”来手动运行它。状态应更新为“正在运行”。您现在可以查看日志以确定它确实有效。您可能会看到有关连接字符串的错误,但您可以忽略它们。如果您切换输出,您仍然会看到一个日志写入控制台,让您知道它的工作原理!如果您没有立即看到日志,请尝试等待一两分钟,不要忘记手动刷新输出。

WebJobs in Azure

Azure WebJobs图

使用Azure Portal发布

添加新的WebJob时,您需要填写一些选项。 您可以组成一些名称,将类型设置为“已触发”,将触发器设置为“手动”。 你的选择是一个“连续”WebJob,它只是运行和关闭(除非你在你的应用程序中实现了无限循环); 以及“预定”触发的工作,这基本上就是我们所拥有的,除非我们自己实施了计划。

“文件上传”需要一些解释。 在这里,您可以上传包含WebJob的zip文件。 因此,请转到Visual Studio并构建您的解决方案。 然后转到WebJob项目的输出文件夹,例如“MyProject \ bin \ [Debug | Release] \ netcoreapp2.1”,并将该文件夹中的所有内容放入zip文件中。 然后在新WebJob的“文件上传”中选择它。

Add WebJob

添加 WebJob

Azure需要几秒钟才能创建您的WebJob,因此请在刷新之前保持刷新。 之后,您必须再次手动启动它,您可以查看日志。

使用VSTS发布

最终,我们希望将我们的WebJob添加到VSTS中的CI / CD管道中。 不幸的是,这个功能并不是即拿即用的。 幸运的是,这也不是很困难。

当您在Azure门户中时,找到App Service的“App Service Editor(预览)”。 这使您可以浏览App Service中的所有文件。 我们注意到的一件事是您的WebJob位于“App_Data \ jobs \ triggered \ [WebJob名称]”中。 由于您的WebJob实际上只是WebJob项目构建的输出,因此只需将WebJob文件复制到App_Data即可。

WebJob file location

WebJob文件位置
 

 

构建

所以去VSTS并创建一个新版本。选择存储库,分支,然后选择“ASP.NET Core”作为模板。我们只需要在这里改变两件事。我们需要更改现有的“发布”任务并添加新的“.NET Core”任务来发布我们的WebJob。

将现有发布任务的名称更改为“发布Web应用程序”,取消选中“发布Web项目”复选框,然后输入“项目路径”,即“** / NetCoreWebJob.csproj”。此外,取消选中“Zip Published Projects”和“Add project name to publish path”复选框,因为它们最终会破坏我们的版本。

之后,创建一个新的.NET Core任务,将“Command”设置为“publish”,并将任务名称更改为“Publish web job”。再次,取消“发布Web项目”并设置“项目路径”,即“** / NetCoreWebJob.WebJob.csproj”。再次,不要压缩已发布的项目或将项目名称添加到发布路径。这里的最后一步是“Arguments”字段,可以从其他发布步骤复制/粘贴,除了我们要添加一点:“ - configuration (BuildConfiguration)−output(BuildConfiguration)−output(build。 artifactstagingdirectory)\ App_Data文件\ \作业引发\ WebJobVSTS”。

VSTS WebJob build

VSTS WebJob构建

发布

最后,但并非最不重要的是,发布。 在VSTS中创建新版本,选择“Azure App Service部署”模板并填写空白,这是工件和环境中的Azure设置。 因为我们没有压缩我们的构建,所以我们只需要更改一个设置。 在“部署Azure应用程序服务”任务是“包或文件夹”设置,其默认值为“[...] / * .zip”,显然不起作用。 而是使用浏览器(带有“...”的按钮)并选择您的drop文件夹。

保存,点击新版本并选择最新版本。 如果一切顺利,您应该在Azure门户中看到新的WebJob!

结束语

喜欢这篇文章就转载吧!不要直接copy过去哦!关注微信公众号获取源代码。

关注公众号获得技术支持

给作者留言

提交留言