【App Service 】WebJobs 多实例实验:谁在运行,什么时候运行?
问题描述
把 Azure App Service 扩展到 3 个实例后,WebJob 会不会也同时运行 3 份?
如果每天定时生成一份报表,会不会生成 3 份?
在门户里手动点击一次 Run,又会由哪个实例执行?
答案取决于 WebJob 的类型,不能仅根据 App Service 的实例数量判断。
持续型默认在所有应用实例上运行;
手动触发和定时触发每次由一个实例执行,不会因为应用有 3 个实例就广播执行 3 次。
这篇文章用同一个 PowerShell 脚本做对照实验,记录实例 ID、UTC 时间和运行批次,让“在哪个实例运行”可以直接从日志判断。
问题解答
门户中的三种常见用法是 Continuous、Triggered/Manual 和 Triggered/Scheduled。
微软文档明确说明,Scheduled 也是 Triggered,只是触发来源从手动操作变成了时间计划。

假设目标应用稳定运行在 3 个实例上,行为如下:
| 使用方式 | 底层类型 | 多实例下的运行方式 | 3 个实例时的稳定状态预期 |
|---|---|---|---|
| 持续运行,Multi Instance | Continuous | 默认在应用的每个实例上启动任务 | 3 份任务进程持续运行 |
| 持续运行,Single Instance | Continuous | 配置 is_singleton: true 后,只在一个实例运行该任务 |
1 份任务进程持续运行 |
| 手动触发 | Triggered | 每次触发由平台选择一个实例执行 | 点击一次 Run,启动一份执行,不是三份 |
| 定时触发 | Triggered | 每个计划触发点由一个实例执行 | 正常情况下,每个触发点启动一份执行,不是三份 |
这里的“一个实例”不意味着“永远固定在同一台机器”。下一次触发、实例替换或持续型单实例任务接管后,执行实例可能发生变化。
平台也不承诺手动触发会依次轮询 A、B、C,所以连续点击几次却一直落在 A 上,并不违反上述规则。
实验准备
在同一段PowerShell脚本中,输出当前webjob运行的时间,实例ID等信息,以判断它是多实例运行还是单实例运行。
param( [ValidateRange(1, 3600)][int]$HeartbeatSeconds = 10, [ValidateRange(0, 3600)][int]$WorkSeconds = 10, [ValidateRange(0, 10000)][int]$MaxHeartbeats = 0, [string]$OutputRoot ) $ErrorActionPreference = 'Stop' $instance = $env:WEBSITE_INSTANCE_ID if (-not $instance) { $instance = 'local-not-azure' } $job = $env:WEBJOBS_NAME if (-not $job) { $job = 'local-demo' } $type = $env:WEBJOBS_TYPE if (-not $type) { $type = 'triggered' } $session = [guid]::NewGuid().ToString('N') if (-not $OutputRoot) { if ($env:WEBSITE_INSTANCE_ID -and $env:HOME) { $OutputRoot = Join-Path $env:HOME 'data\webjob-instance-demo' } else { $OutputRoot = Join-Path ([IO.Path]::GetTempPath()) 'webjob-instance-demo' } } $safeJob = $job -replace '[^a-zA-Z0-9_-]', '_' $safeInstance = $instance -replace '[^a-zA-Z0-9_-]', '_' $directory = Join-Path (Join-Path $OutputRoot $safeJob) $safeInstance $null = New-Item -ItemType Directory -Path $directory -Force $logPath = Join-Path $directory "$session.jsonl" function Write-Record([string]$EventName) { $record = [ordered]@{ utc = [DateTime]::UtcNow.ToString('o') event = $EventName job = $job type = $type instance = $instance computer = $env:COMPUTERNAME processId = $PID session = $session platformRunId = $env:WEBJOBS_RUN_ID } $line = $record | ConvertTo-Json -Compress Write-Output $line # One file per process session: instances never append to the same file. Add-Content -LiteralPath $logPath -Value $line -Encoding UTF8 } function Test-Shutdown { return ($env:WEBJOBS_SHUTDOWN_FILE -and (Test-Path -LiteralPath $env:WEBJOBS_SHUTDOWN_FILE)) } function Wait-OrShutdown([int]$Seconds) { for ($i = 0; $i -lt $Seconds; $i++) { if (Test-Shutdown) { return } Start-Sleep -Seconds 1 } } Write-Record 'START' if ($type -eq 'continuous') { $count = 0 while (-not (Test-Shutdown)) { Write-Record 'HEARTBEAT' $count++ # A finite limit is for LOCAL tests only. Azure continuous jobs restart. if ($MaxHeartbeats -gt 0 -and $count -ge $MaxHeartbeats) { break } Wait-OrShutdown $HeartbeatSeconds } } else { # Simulate a short task. This delay is not an exactly-once guarantee. Wait-OrShutdown $WorkSeconds } if (Test-Shutdown) { Write-Record 'STOP_REQUESTED' } else { Write-Record 'END' }
把以上内容复制到文件 run.ps1 中,然后分别打包为四个zip文件:continuous-multi.zip , continuous-single.zip,manual.zip 和 scheduled.zip。
然后通过App Service的Web Job门户分别创建四个web job,参考在门户上创建WebJob的文档:https://docs.azure.cn/zh-cn/app-service/quickstart-webjobs?tabs=windows#add-the-webjob-in-the-azure-portal

实验结果
1:continuous-multi , 连续运行的多实例部署结果为,在3个实例上,这个webjob在每一个实例上都在执行。

2:continuous-single, 连续运行的单实例部署结果为,在3个实例的环境中,这个webjob只在一个实例上执行。

3:manual,手动触发的webjob,在3个实例的环境中,每次只在一个实例上执行。

4:scheduled,按时触发的webjob,在3个实例的环境中,每次只在一个实例上执行。只是,后一次执行的实例可能发生变化。

参考资料
应用服务 WebJobs 概述 : https://docs.azure.cn/zh-cn/app-service/overview-webjobs
How would I scale a timer triggered webjob on an Azure App service to multiple app service instances? https://learn.microsoft.com/en-us/answers/questions/1689283/how-would-i-scale-a-timer-triggered-webjob-on-an-a
当在复杂的环境中面临问题,格物之道需:浊而静之徐清,安以动之徐生。 云中,恰是如此!

浙公网安备 33010602011771号