本文介绍如何使用 Azure WebJobs SDK。 若要快速开始使用 WebJobs,请参阅 Azure WebJobs SDK 入门。
WebJobs SDK 版本 3.x 与版本 2.x 之间的主要差别如下:
- 版本 3.x 中添加了对 .NET Core 的支持。
- 在版本 3x 中,安装 WebJobs SDK 所需的存储绑定扩展。 在版本 2.x 中,存储绑定包含在 SDK 中。
- 适用于 .NET Core (3.x) 项目的 Visual Studio 2019 工具与适用于 .NET Framework (2.x) 项目的工具不同 。 有关详细信息,请参阅 使用 Visual Studio 开发和部署 WebJobs。
本文中的一些示例同时适用于 WebJobs 版本 3.x 和 WebJobs 版本 2.x。
Azure Functions 基于 WebJobs SDK 构建:
- Azure Functions 版本 2.x 以 WebJobs SDK 版本 3.x 为基础 。
- Azure Functions 版本 1.x 以 WebJobs SDK 版本 2.x 为基础 。
Azure Functions 和 WebJobs SDK 的源代码存储库使用 WebJobs SDK 编号。 本文的多个部分提供了 Azure Functions 文档的链接。
WebJobs 主机是函数的运行时容器。 主机侦听触发器并调用函数。 在版本 3.x 中,主机是 IHost
的实现。 在版本 2.x 中,使用的是 JobHost
对象。 在代码中创建主机实例,并编写代码来自定义其行为。
此体系结构更改反映了直接使用 WebJobs SDK 与通过 Azure Functions 间接使用 WebJobs 的主要差别。 在 Azure Functions 中,服务将控制主机。 你无法通过编写代码来自定义主机。 在 Azure Functions 中,可以通过 host.json
文件中的设置来自定义主机行为。 这些设置是字符串而不是代码,使用这些字符串会限制可执行的自定义类型。
在本地运行时,WebJobs SDK 会在 local.settings.json
文件中查找 Azure 存储和 Azure 服务总线连接;在 Azure 中运行时,它会在 WebJob 的环境中查找这些连接。 默认情况下,WebJobs SDK 需要一个名称为 AzureWebJobsStorage
的存储连接。
当连接名称解析为单个精确值时,运行时会将该值标识为连接字符串,该字符串通常包括机密。 连接字符串的详细信息取决于连接的服务。 但是,连接名称还可以引用多个配置项的集合。 此方法可用于配置基于标识的连接。 可以使用以双下划线__
结尾的共享前缀()将环境变量视为集合。 然后,可以通过将连接名称设置为此前缀来引用该组。
例如,Azure Blob 存储触发器定义的 connection
属性可能是 Storage1
。 只要名为 Storage1
的环境变量没有配置单个字符串值,就可以使用名为 Storage1__blobServiceUri
的环境变量来通知连接的 blobServiceUri
属性。 每个服务的连接属性各不相同。 请参阅相关文档,了解使用连接的组件。
若要在 WebJobs SDK 中使用基于标识的连接,请确保在项目中使用最新版本的 WebJobs 包。 另外,应确保引用了 Microsoft.Azure.WebJobs.Host.Storage。
以下示例显示了做出这些更新后项目文件的外观:
<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.42" />
<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>
在 HostBuilder 实例中设置 WebJobs 时,请确保调用 AddAzureStorageCoreServices
。 此调用允许 AzureWebJobsStorage
及其他存储触发器和绑定使用标识。
下面是一个示例:
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
// Other configurations...
});
然后,可以通过在 Azure 应用服务中托管代码时设置环境变量(或应用程序设置)来配置 AzureWebJobsStorage
连接:
环境变量 | 说明 | 示例值 |
---|---|---|
AzureWebJobsStorage__blobServiceUri |
存储帐户的 blob 服务的数据平面 URI。 它使用 HTTPS 方案。 | <https://storage_account_name.blob.core.chinacloudapi.cn> |
AzureWebJobsStorage__queueServiceUri |
存储帐户的队列服务的数据平面 URI。 它使用 HTTPS 方案。 | <https://storage_account_name.queue.core.chinacloudapi.cn> |
如果以环境变量以外的任何方式提供配置(如配置文件中 appsettings.json
),则必须为连接及其属性提供结构化配置:
{
"AzureWebJobsStorage": {
"blobServiceUri": "https://<storage_account_name>.blob.core.chinacloudapi.cn",
"queueServiceUri": "https://<storage_account_name>.queue.core.chinacloudapi.cn"
}
}
如果你不打算使用 Blob 触发器,则可以省略 queueServiceUri
属性。
当代码在本地运行时,默认会使用开发人员标识,如 DefaultAzureCredential
介绍内容中所述。
在应用服务中托管代码时,前面的示例中的配置默认为 资源系统分配的托管标识 。 若要改用分配给应用的 用户分配标识 ,必须添加连接的属性以指定要使用的标识。
credential
属性(AzureWebJobsStorage__credential
为环境变量)应设置为字符串 managedidentity
。 属性 clientId
(AzureWebJobsStorage__clientId
作为环境变量)应设置为要使用的用户分配的托管标识的客户端 ID。
作为结构化配置,完整的对象如以下示例所示:
{
"AzureWebJobsStorage": {
"blobServiceUri": "https://<storage_account_name>.blob.core.chinacloudapi.cn",
"queueServiceUri": "https://<storage_account_name>.queue.core.chinacloudapi.cn",
"credential": "managedidentity",
"clientId": "<user-assigned-identity-client-id>"
}
}
用于 AzureWebJobsStorage
的标识应具有授予其存储 Blob 数据所有者、存储队列数据参与者和存储帐户参与者角色的角色分配。 如果不打算使用 Blob 触发器,可以省略 存储队列数据参与者 和 存储帐户参与者 角色。
下表显示了在正常操作中使用触发器时建议使用的内置角色。 应用程序可能需要更多权限,具体取决于你编写的代码。
捆绑 | 内置角色示例 |
---|---|
Blob 触发器 |
存储 Blob 数据所有者和存储队列数据参与者 另请参阅上述 AzureWebJobsStorage 要求。 |
Blob(输入) | 存储 Blob 数据读者 |
Blob(输出) | 存储 Blob 数据所有者 |
队列触发器 | 存储队列数据读取者、存储队列数据消息处理者 |
队列(输出) | 存储队列数据参与者、存储队列数据消息发送方 |
服务总线触发器1 | Azure 服务总线数据接收方、Azure 服务总线数据所有者 |
服务总线(输出) | Azure 服务总线数据发送方 |
1 若要从服务总线主题触发,角色分配必须具有服务总线订阅资源的有效范围。 如果仅包含主题,将发生错误。 某些客户端(例如 Azure 门户)不会将服务总线订阅资源公开为角色分配的范围。 在这些场景中,可以改用 Azure CLI。 有关详细信息,请参阅 Azure 服务总线的 Azure 内置角色。
版本 2。x 的 SDK 不需要连接字符串的特定名称。 在版本 2.x 中,可为这些连接字符串使用你自己的名称,并将其存储在其他位置。 可以使用 JobHostConfiguration
在代码中设置名称,如以下示例所示:
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();
}
注意
由于版本 3.x 使用默认的 .NET Core 配置 API,因此不存在用于更改连接字符串名称的 API。 有关详细信息,请参阅 使用 Visual Studio 开发和部署 WebJobs。
可在开发模式下运行主机,提高本地开发效率。 下面是在开发模式下运行时会自动更改的一些设置:
属性 | 开发设置 |
---|---|
Tracing.ConsoleLevel |
TraceLevel.Verbose :最大化日志输出。 |
Queues.MaxPollingInterval |
一个低值,用于确保队列方法立即触发。 |
Singleton.ListenerLockPeriod |
设置为 15 秒有助于快速完成迭代开发。 |
启用开发模式的过程取决于 SDK 版本。
在版本 3 中。x,使用标准 ASP.NET 核心 API 更改主机环境。 对 UseEnvironment
实例调用 HostBuilder
方法。 传递名为 development
的字符串,如以下示例中所示:
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();
}
}
JobHostConfiguration
类具有 UseDevelopmentSettings
方法,该方法支持开发模式。 以下示例演示如何使用开发设置。 要使 config.IsDevelopment
在本地运行时返回 true
,请设置名为 AzureWebJobsEnv
、值为 Development
的本地环境变量。
static void Main()
{
config = new JobHostConfiguration();
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
var host = new JobHost(config);
host.RunAndBlock();
}
在版本 3.x 中,连接限制默认为无限次连接。 如果出于某种原因需要更改此限制,则可以使用 MaxConnectionsPerServer
类的 WinHttpHandler
属性。
在版本 2 中。x,可以使用该 ServicePointManager.DefaultConnectionLimit
属性控制与主机的并发连接数。 在 2.x 中,应在启动 WebJobs 主机之前,在默认值 的基础上增大此值2
。
使用 HttpClient
从某个函数发出的所有传出 HTTP 请求都会流经 ServicePointManager
。 达到 DefaultConnectionLimit
中设置的值后,ServicePointManager
会开始将请求排队,然后再发送请求。 假设 DefaultConnectionLimit
设置为 2
,并且代码发出了 1,000 个 HTTP 请求。 最初,只允许 2 个请求传入 OS。 其他 998 个请求将会排队,直到有可用的空间。
HttpClient
可能会超时,因为它似乎已发出请求,但是,OS 从未将此请求发送到目标服务器。 可能会出现看似不合理的行为:本地 HttpClient
花费了 10 秒来完成请求,但服务在 200 毫秒内就返回了每个请求。
ASP.NET 应用程序的默认值是 Int32.MaxValue
,这可能非常适合在“基本”或更高级别应用服务计划中运行的 WebJobs。 WebJobs 通常需要 Always On 设置,并且仅受基本和更高应用服务计划支持。
如果 WebJob 在免费或共享应用服务计划中运行,则应用程序受应用服务沙盒的限制,该沙盒当前 连接限制为 600。 如果在 ServicePointManager
中指定无限制的连接数,则很有可能会达到沙盒连接阈值,并且站点将会关闭。 在这种情况下,将 DefaultConnectionLimit
设置为较低的数值,例如 200,可以防止这种情况发生,同时仍然允许足够的吞吐量。
必须在发出任何 HTTP 请求之前配置该设置。 出于此原因,WebJobs 主机不应自动尝试调整该设置。 可能会在主机启动之前出现 HTTP 请求,这可能会导致意外的行为。 最佳的做法是在初始化 Main
之前,立即在 JobHost
方法中设置值,如以下示例所示:
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();
}
WebJobs SDK 支持 Azure Functions 使用的同一组触发器和绑定。 在 WebJobs SDK 中,触发器特定于函数,与 WebJob 部署类型无关。 具有通过 SDK 创建的事件触发函数的 WebJobs 始终应作为已启用 Always On 的连续 WebJob 进行发布。
函数必须是公共方法,并且必须包含一个触发器属性或 NoAutomaticTrigger
属性。
自动触发器调用函数来响应事件。 请考虑此示例,该函数是由添加到 Azure 队列存储的消息触发的。 该函数通过从 Blob 存储读取 Blob 来做出响应:
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");
}
QueueTrigger
特性告诉运行时在 myqueue-items
中出现队列消息时调用函数。
Blob
属性告诉运行时使用队列消息读取 sample-workitems
容器中的 Blob。
samples-workitems
容器中 Blob 项的名称将直接从队列触发器以绑定表达式 ({queueTrigger}
) 的形式获得。
注意
Web 应用可在进入非活动状态 20 分钟后超时,只有向实际 Web 应用发出请求才会重置计时器。 在 Azure 门户中查看应用的配置或向高级工具站点 (https://<app_name>.scm.chinacloudsites.cn
) 发出请求不会重置计时器。 如果将托管作业的 Web 应用设置为持续运行、按计划运行或使用事件驱动的触发器,请在 Web 应用的 Azure“配置”页上启用“始终可用”设置 。 “始终可用”设置有助于确保这些类型的 Web 作业可靠运行。 此功能仅在基本、标准和高级定价层中提供。
若要手动触发某个函数,请使用 NoAutomaticTrigger
属性:
[NoAutomaticTrigger]
public static void CreateQueueMessage(
ILogger logger,
string value,
[Queue("outputqueue")] out string message)
{
message = value;
logger.LogInformation("Creating queue message: ", message);
}
手动触发函数的过程取决于 SDK 版本。
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();
}
}
static void Main(string[] args)
{
JobHost host = new JobHost();
host.Call(typeof(Program).GetMethod("CreateQueueMessage"), new { value = "Hello world!" });
}
通过输入绑定能够以声明方式将 Azure 或第三方服务中的数据提供给代码使用。 输出绑定提供更新数据的方式。 入门文章中演示了输入和输出绑定的示例。
通过将属性应用于方法返回值,可以对输出绑定使用方法返回值。
安装和管理绑定类型的过程取决于使用的是 SDK 版本 3.x 还是版本 2.x。 可以在特定绑定类型的 Azure Functions 参考文章的“包”部分找到要为该绑定类型安装的包。 有一种例外情况:文件触发器和绑定(对于本地文件系统)不受 Azure Functions 的支持。
在版本 3.x 中,存储绑定包含在 Microsoft.Azure.WebJobs.Extensions.Storage
包中。 在 AddAzureStorage
方法中调用 ConfigureWebJobs
扩展方法:
static async Task Main()
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
若要使用其他触发器和绑定类型,请安装包含这些类型的 NuGet 包,并调用在扩展中实现的 Add<binding>
扩展方法。 例如,如果你要使用 Azure Cosmos DB 绑定,请安装 Microsoft.Azure.WebJobs.Extensions.CosmosDB
并调用 AddCosmosDB
:
static async Task Main()
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddCosmosDB();
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
若要使用属于核心服务的 Timer 触发器或 Files 绑定,请调用 AddTimers
或 AddFiles
扩展方法。
以下触发器和绑定类型包含在版本 2.x 的 Microsoft.Azure.WebJobs
包中:
- Blob 存储
- 队列存储
- 表存储
若要使用其他触发器和绑定类型,请安装包含这些类型的 NuGet 包,并对 Use<binding>
对象调用 JobHostConfiguration
方法。 例如,如果你要使用 Timer 触发器,请安装 Microsoft.Azure.WebJobs.Extensions
并在 UseTimers
方法中调用 Main
:
static void Main()
{
config = new JobHostConfiguration();
config.UseTimers();
var host = new JobHost(config);
host.RunAndBlock();
}
要使用 Files 绑定,请安装 Microsoft.Azure.WebJobs.Extensions
并调用 UseFiles
。
在 WebJobs 中,可以绑定到 ExecutionContext
实例。 使用此绑定,可以访问在函数签名中作为参数的 ExecutionContext
。 例如,以下代码使用上下文对象访问调用 ID,使用该 ID 可以关联给定函数调用生成的所有日志。
public class Functions
{
public static void ProcessQueueMessage([QueueTrigger("queue")] string message,
ExecutionContext executionContext,
ILogger logger)
{
logger.LogInformation($"{message}\n{executionContext.InvocationId}");
}
}
绑定到 ExecutionContext
的过程取决于你的 SDK 版本。
在 AddExecutionContextBinding
方法中调用 ConfigureWebJobs
扩展方法:
static async Task Main()
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddExecutionContextBinding();
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
前面所述的 Microsoft.Azure.WebJobs.Extensions
包还提供了一个可以通过调用 UseCore
方法注册的特殊绑定类型。 可以使用绑定在函数签名中定义 ExecutionContext
参数:
class Program
{
static void Main()
{
config = new JobHostConfiguration();
config.UseCore();
var host = new JobHost(config);
host.RunAndBlock();
}
}
可以配置某些触发器和绑定的行为。 配置过程取决于 SDK 版本。
-
版本 3.x: 在
Add<Binding>
中调用ConfigureWebJobs
方法时设置配置。 -
版本 2.x: 通过在传入
JobHost
的配置对象中设置属性来设置配置。
这些特定于绑定的设置等效于 Azure Functions 中的 host.json
项目文件中 的设置。
可配置以下绑定:
- Azure Cosmos DB 触发器
- 事件中心触发器
- 队列存储触发器
- SendGrid 绑定
- 服务总线触发器
此示例演示如何配置 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();
}
}
有关详细信息,请参阅 Azure Cosmos DB 绑定。
此示例演示如何配置事件中心触发器:
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();
}
}
有关详细信息,请参阅 事件中心绑定。
以下示例演示如何配置队列存储触发器。
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();
}
}
有关详细信息,请参阅 队列存储绑定。
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();
}
有关详细信息,请参阅 host.json
v1.x 参考。
此示例演示如何配置 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();
}
}
有关详细信息,请参阅 SendGrid
绑定。
此示例演示如何配置服务总线触发器:
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();
}
}
有关详细信息,请参阅 服务总线绑定。
某些触发器和绑定类型定义其自身的自定义配置类型。 例如,可以使用文件触发器来指定要监视的根路径,如以下示例所示。
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();
}
}
static void Main()
{
config = new JobHostConfiguration();
var filesConfig = new FilesConfiguration
{
RootPath = @"c:\data\import"
};
config.UseFiles(filesConfig);
var host = new JobHost(config);
host.RunAndBlock();
}
在特性构造函数参数中,可以使用解析为来自各种源的值的表达式。 例如,在以下代码中,BlobTrigger
特性的路径创建名为 filename
表达式。 用于输出绑定时,filename
解析为触发 Blob 的名称。
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}");
// ...
}
有关绑定表达式的详细信息,请参阅 Azure Functions 文档中的绑定表达式和模式。
有时,你想要在代码中指定队列名称、Blob 名称、容器或表名称,而不是进行硬编码。 例如,可能要在配置文件或环境变量中指定 QueueTrigger
特性的队列名称。
可以通过在配置期间传递自定义名称解析程序,来向属性分配队列名称。 在触发器或绑定特性构造函数参数中包含占位符,解析程序代码将提供用于取代这些占位符的实际值。 可以用百分号 (%
) 括住占位符,以此对其进行标识:
public static void WriteLog([QueueTrigger("%logqueue%")] string logMessage)
{
Console.WriteLine(logMessage);
}
此代码在测试环境中使用名为 logqueuetest
的队列,在生产环境中使用名为 logqueueprod
的队列。 在 appSettings
集合中指定条目名称,而不是硬编码的队列名称。
如果未提供自定义解析程序,则默认解析程序会生效。 默认设置从应用设置或环境变量中获取值。
从 .NET Core 3.1 开始, ConfigurationManager
使用的实例需要 System.Configuration.ConfigurationManager
NuGet 包。 示例需要以下 using
语句:
using System.Configuration;
NameResolver
类从应用设置获取队列名称:
public class CustomNameResolver : INameResolver
{
public string Resolve(string name)
{
return ConfigurationManager.AppSettings[name].ToString();
}
}
使用依赖关系注入配置解析程序。 这些示例需要下列 using
语句:
using Microsoft.Extensions.DependencyInjection;
可以通过调用 ConfigureServices
上的 HostBuilder
扩展方法来添加解析程序,如下例所示:
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();
}
}
将 NameResolver
类传入 JobHost
对象:
static void Main(string[] args)
{
JobHostConfiguration config = new JobHostConfiguration();
config.NameResolver = new CustomNameResolver();
JobHost host = new JobHost(config);
host.RunAndBlock();
}
Azure Functions 实现 INameResolver
以从应用设置中获取值,如以下示例中所示。 直接使用 WebJobs SDK 时,可以编写一个自定义实现,用于从偏好的任何源获取占位符替代值。
如果需要在使用 Queue
、Blob
或 Table
等绑定特性之前在函数中执行某项操作,可以使用 IBinder
接口。
以下示例使用输入队列消息,并在输出队列中创建具有相同内容的新消息。 输出队列名称由函数正文中的代码设置。
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));
}
有关详细信息,请参阅 Azure Functions 文档中的运行时绑定。
Azure Functions 文档中提供了有关每个绑定类型的参考信息。 每篇绑定参考文章中都提供了以下信息。 (此示例基于某个存储队列。)
- 包。 需要安装哪个包才能在 WebJobs SDK 项目中支持绑定。
-
示例。 代码示例。 C# 类库示例适用于 WebJobs SDK。 只需省略
FunctionName
特性。 - 特性。 用于绑定类型的特性。
- 配置。 特性属性和构造函数参数的解释。
- 使用情况。 可绑定到哪些类型,以及有关绑定工作原理的信息。 例如:轮询算法或有害队列处理。
注意
HTTP、Webhook 和事件网格绑定仅受 Azure Functions 的支持,而不受 WebJobs SDK 的支持。
有关 Azure Functions 运行时支持的绑定的完整列表,请参阅 支持的绑定。
可以使用这些属性来控制函数触发、取消函数,并确保只有一个函数实例运行。
使用 Disable
属性来控制是否可以触发某个函数。
在以下示例中,如果应用设置 Disable_TestJob
的值为 1
或 True
(不区分大小写),则该函数不会运行。 在这种情况下,运行时将创建日志消息“函数 'Functions.TestJob' 已禁用”。
[Disable("Disable_TestJob")]
public static void TestJob([QueueTrigger("testqueue2")] string message)
{
Console.WriteLine("Function with Disable attribute executed!");
}
在 Azure 门户中更改应用设置值时,WebJob 会重启并选取新的设置。
可以在参数、方法或类级别声明该特性。 设置名称还可以包含绑定表达式。
如果某个函数在指定的时间段内未完成,则 Timeout
特性会导致该函数被取消。 在以下示例中,如果没有 Timeout
属性,该函数将运行一天。 如果指定了 Timeout,该函数将在 15 秒后被取消。 当 Timeout
特性throwOnError
参数设置为true
时,如果超出超时间隔,WebJobs SDK 将引发异常并终止函数调用。
throwOnError
的默认值为 false
。 使用 Timeout
属性时,默认行为是通过设置取消标记来取消函数调用,同时允许该调用无限期地运行,直到函数代码返回异常或引发异常。
[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");
}
可以在类级别或方法级别应用Timeout
属性,并且可以使用JobHostConfiguration.FunctionTimeout
指定全局超时。 类级别或方法级别的超时替代全局超时。
Singleton
特性可确保即使有多个主机 Web 应用的实例,也只有一个函数实例运行。 该 Singleton
属性使用 分布式锁定 来确保仅运行一个实例。
在此示例中,在任意给定时间只会运行 ProcessImage
函数的单个实例:
[Singleton]
public static async Task ProcessImage([BlobTrigger("images")] Stream image)
{
// Process the image.
}
某些触发器为并发管理提供内置支持:
-
QueueTrigger。 将
JobHostConfiguration.Queues.BatchSize
设置为1
。 -
ServiceBusTrigger。 将
ServiceBusConfiguration.MessageOptions.MaxConcurrentCalls
设置为1
。 -
FileTrigger。 将
FileProcessor.MaxDegreeOfParallelism
设置为1
。
可以使用这些设置来确保函数在单个实例上作为单一实例运行。 若要确保在 Web 应用横向扩展到多个实例时只运行函数的单个实例,请对该函数应用侦听器级别的单一实例锁 ([Singleton(Mode = SingletonMode.Listener)]
)。 启动 JobHost 时获取侦听器锁。 如果三个横向扩展的实例全部同时启动,只有其中的一个实例获取该锁,并且只有一个侦听器启动。
注意
若要详细了解工作原理 SingletonMode.Function
,请参阅 SingletonMode GitHub 存储库。
可以在单一实例中指定一个范围表达式/值。 表达式/值可确保在特定范围内该函数的所有执行过程都将被序列化。 以这种方式实现更细化的锁定可以为函数提供一定程度的并行度,同时根据你的需求串行化其他调用。 例如,在以下代码中,范围表达式将绑定到传入消息的 Region
值。 当队列包含 East、East 和 West 区域中的三个消息时,将串行运行区域为 East 的消息。 区域 West 的消息与区域 East 的消息并行运行。
[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.Function
。 锁定范围(Blob 租约路径)与完全限定的函数名称相关联。 若要跨函数锁定,请指定 SingletonScope.Host
,并使用在不想要同时运行的所有函数中相同的范围 ID 名称。 在以下示例中,每次只会运行 AddItem
或 RemoveItem
的一个实例:
[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.
}
WebJobs SDK 使用 Azure Blob 租约 来实现分布式锁定。 可以在 Singleton
存储帐户的 azure-webjobs-host
容器中的路径“locks”下面找到 AzureWebJobsStorage
使用的租约 Blob。例如,前面演示的第一个 ProcessImage
示例的租约 Blob 路径可能是 locks/061851c758f04938a4426aa9ab3869c0/WebJobs.Functions.ProcessImage
。 所有路径包含 JobHost ID,在本例中为 061851c758f04938a4426aa9ab3869c0。
有关如何编写异步函数代码的信息,请参阅 Azure Functions 文档。
有关如何处理取消令牌的信息,请参阅有关取消令牌和正常关闭的 Azure Functions 文档。
如果 Web 应用在多个实例上运行,则会有一个连续的 WebJob 在每个实例上运行,并侦听触发器和调用函数。 各种触发器绑定旨在以协作方式有效分担各个实例上的工作,以便横向扩展到多个实例后可以处理更多的负载。
虽然某些触发器可能会导致重复处理,但队列和 Blob 存储触发器自动阻止函数多次处理队列消息或 Blob。 有关详细信息,请参阅 Azure Functions 文档中的针对完全相同的输入进行设计。
计时器触发器会自动确保只会运行计时器的一个实例,因此,在给定的计划时间,不会运行多个函数实例。
如果你要确保即使有多个主机 Web 应用的实例,也只有一个函数实例运行,可以使用 Singleton
属性。
通过函数筛选器(预览版)可以使用自己的逻辑自定义 WebJobs 执行管道。 这些筛选器类似于 ASP.NET 核心筛选器。 可将其实现为应用到函数或类的声明性特性。 有关详细信息,请参阅函数筛选器。
建议使用为 ASP.NET 开发的日志记录框架。 入门文章中介绍了其用法。
ILogger
实例创建的每个日志都有关联的 Category
和 Level
值。
LogLevel
是一个枚举,整数代码指示相对重要性:
LogLevel | 代码 |
---|---|
跟踪 | 0 |
调试 | 1 |
信息 | 2 |
警告 | 3 |
错误 | 4 |
严重 | 5 |
无 | 6 |
可以根据特定的 LogLevel
值单独筛选每个类别。 例如,你可能想要查看有关 Blob 触发器处理的所有日志,但对于其他任何操作,只想查看 Error
和更高级别的日志。
版本 3。x 的 SDK 依赖于内置于 .NET Core 中的筛选。 使用 LogCategories
类可为特定的函数、触发器或用户定义类别。
LogCategories
类还定义了针对特定主机状态的筛选器,例如 Startup
和 Results
,以便你可以微调日志输出。 如果在定义的类别中找不到匹配项,则筛选器在确定是否筛选消息时回退到 Default
值。
LogCategories
需要以下 using
语句:
using Microsoft.Azure.WebJobs.Logging;
以下示例构造的筛选器默认会筛选 Warning
级别的所有日志。
Function
和 results
类别(等效于版本 2.Host.Results
中的 )在 Error
级别进行筛选。 筛选器将当前类别与 LogCategories
实例中所有已注册的级别进行比较,并选择最长匹配项。 因此为 Debug
注册的 Host.Triggers
级别将与 Host.Triggers.Queue
或 Host.Triggers.Blob
匹配。 你可以控制更广泛的类别,而无需添加每个类别。
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();
}
}
在版本 2.x 的 SDK 中,LogCategoryFilter
类用于控制筛选。
LogCategoryFilter
包含初始值为 Default
的 Information
属性,这意味着,将会记录级别为 Information
、Warning
、Error
或 Critical
的所有消息,但会筛选掉级别为 Debug
或 Trace
的所有消息。
与版本 3.LogCategories
中的 一样,使用 CategoryLevels
属性可以指定特定类别的日志级别,以便能够微调日志记录输出。 如果在字典中 CategoryLevels
找不到匹配项,则筛选器在确定是否筛选消息时回退到 Default
值。
以下示例构造的筛选器默认会筛选 Warning
级别的所有日志。
Function
和 Host.Results
类别在 Error
级别进行筛选。
LogCategoryFilter
将当前类别与所有已注册的 CategoryLevels
进行比较,并选择最长匹配项。 因此为 Debug
注册的 Host.Triggers
级别将与 Host.Triggers.Queue
或 Host.Triggers.Blob
匹配。 你可以控制更广泛的类别,而无需添加每个类别。
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);
为 Application Insights 实现自定义遥测的过程取决于 SDK 版本。 要了解如何配置 Application Insights,请参阅添加 Application Insights 日志记录。
由于 WebJobs SDK 的版本 3.x 依赖于 .NET Core 通用主机,因此不再提供自定义遥测工厂。 但可以使用依赖关系注入将自定义遥测添加到管道。 本部分中的示例要求使用下列 using
语句:
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Channel;
你可以使用 ITelemetryInitializer
的以下自定义实现将自己的 ITelemetry
实例添加到默认的 TelemetryConfiguration
。
internal class CustomTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
// Do something with telemetry.
}
}
在生成器中调用 ConfigureServices
,以将自定义 ITelemetryInitializer
实例添加到管道。
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 code 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();
}
}
构造 TelemetryConfiguration
时,将包含所有已注册的 ITelemetryInitializer
类型。 有关详细信息,请参阅 用于自定义事件和指标的 Application Insights API。
在版本 3.x 中,主机停止时无需刷新 TelemetryClient
。 .NET Core 依赖项注入系统会自动释放已注册的 ApplicationInsightsLoggerProvider
实例,从而刷新 TelemetryClient
。
在版本 2.x 中,Application Insights 提供程序在内部为 WebJobs SDK 创建的 实例使用 TelemetryClient
ServerTelemetryChannel
。 当 Application Insights 终结点不可用或正在限制传入请求时,此通道将 请求保存在 Web 应用的文件系统中,并在以后重新提交请求。
TelemetryClient
是实现 ITelemetryClientFactory
的类创建的。 默认情况下,此类为 DefaultTelemetryClientFactory
。
如果你要修改 Application Insights 管道的任何部分,可以提供自己的 ITelemetryClientFactory
实例。 然后,主机将使用你的类来构造 TelemetryClient
。 例如,此代码会替代 DefaultTelemetryClientFactory
来修改 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;
}
}
SamplingPercentageEstimatorSettings
对象配置自适应采样。 在这种情况下,在某些大容量方案中,Application Insights 会向服务器发送选定的遥测数据子集。
创建遥测工厂后,可将其传入 Application Insights 日志记录提供程序:
var clientFactory = new CustomTelemetryClientFactory(instrumentationKey, filter.Filter);
config.LoggerFactory = new LoggerFactory()
.AddApplicationInsights(clientFactory);
本文提供了代码片段,演示如何处理使用 WebJobs SDK 的常见方案。 有关完整示例,请参阅 azure-webjobs-sdk-samples。