Azure Functions 在缩减之前需要冷却多长时间?
How long do Azure Functions need to cool down before they scale in?
在我们的开发环境中,我们的函数应用程序通常只需要几台服务器。
周末我们意外地引发了连锁反应,导致逐渐、持续地扩展到大约 140 台服务器。现在我们已经解决了根本问题并清除了队列,activity 似乎已经恢复正常。 (呸!)
奇怪的是(30 分钟后)我们所有的服务器仍然在线。我本以为他们会很快开始下线,但我看到 "servers online" 的数量在大约 110 到 140 之间波动(上下波动)。其中绝大多数都坐在那里有 0 requests/s,没有 CPU,没有记忆。
所以,一些问题:
- 这是预期的吗?
- 指导联机/保持联机的服务器数量的启发式是什么?
- 我应该在什么时候看到这个数字回落到我习惯的水平?
Scale Controller 是一种闭源专有技术,具有未记录的行为。您的问题没有官方答案。回答它的唯一方法是做你所做的:运行 实验和测量。不过,这种行为可能会随着时间的推移而改变。
函数确实有一些启发式方法,可以根据事件数量和资源利用率确定何时扩展和缩减。过去,他们非常节俭,但这并没有提供足够好的横向扩展速度。现在,他们倾向于错误地配置太多而不是配置太少。
好消息是,除非出于好奇,否则您不应该真正关心缩放。您无需为闲置服务器付费。
只是为@Mikhail 写的内容添加一些内容 - 您的函数缩放方式取决于所用触发器的类型。如果您使用队列,运行时将根据消息的数量查看队列长度和规模 up/down。对于事件中心,此行为取决于中心中的分区数量 - 您拥有的分区越多,您就越渴望扩展您的功能。
可以查看源代码并尝试至少了解部分功能。事实上,它是基于一个计时器和一个工人的概念,它们更新它们的状态并让运行时决定是否需要扩大或缩小规模。
整体算法描述如下:
protected virtual async Task MakeScaleDecision(string activityId, IWorkerInfo manager)
{
if (DateTime.UtcNow < _scaleCheckUtc)
{
return;
}
try
{
var workers = await _table.ListNonStale();
_tracer.TraceInformation(activityId, manager, workers.GetSummary("NonStale"));
if (await TryRemoveIfMaxWorkers(activityId, workers, manager))
{
return;
}
if (await TryAddIfLoadFactorMaxWorker(activityId, workers, manager))
{
return;
}
if (await TrySwapIfLoadFactorMinWorker(activityId, workers, manager))
{
return;
}
if (await TryAddIfMaxBusyWorkerRatio(activityId, workers, manager))
{
return;
}
if (await TryRemoveIfMaxFreeWorkerRatio(activityId, workers, manager))
{
return;
}
if (await TryRemoveSlaveWorker(activityId, workers, manager))
{
return;
}
}
catch (Exception ex)
{
_tracer.TraceError(activityId, manager, string.Format("MakeScaleDecision failed with {0}", ex));
}
finally
{
_scaleCheckUtc = DateTime.UtcNow.Add(_settings.ScaleCheckInterval);
}
}
更重要的是,为什么你看到活着的工人也可以在源代码中找到答案:
protected virtual async Task PingWorker(string activityId, IWorkerInfo worker)
{
// if ping was unsuccessful, keep pinging. this is to address
// the issue where site continue to run on an unassigned worker.
if (!_pingResult || _pingWorkerUtc < DateTime.UtcNow)
{
// if PingWorker throws, we will not update the worker status
// this worker will be stale and eventually removed.
_pingResult = await _eventHandler.PingWorker(activityId, worker);
_pingWorkerUtc = DateTime.UtcNow.Add(_settings.WorkerPingInterval);
}
// check if worker is valid for the site
if (_pingResult)
{
await _table.AddOrUpdate(worker);
_tracer.TraceUpdateWorker(activityId, worker, string.Format("Worker loadfactor {0} updated", worker.LoadFactor));
}
else
{
_tracer.TraceWarning(activityId, worker, string.Format("Worker does not belong to the site."));
await _table.Delete(worker);
_tracer.TraceRemoveWorker(activityId, worker, "Worker removed");
throw new InvalidOperationException("The worker does not belong to the site.");
}
}
不幸的是,部分实现是密封的(如IWorkerInfo
),所以你无法得到全貌,我们只能猜测(或询问;))
在我们的开发环境中,我们的函数应用程序通常只需要几台服务器。
周末我们意外地引发了连锁反应,导致逐渐、持续地扩展到大约 140 台服务器。现在我们已经解决了根本问题并清除了队列,activity 似乎已经恢复正常。 (呸!)
奇怪的是(30 分钟后)我们所有的服务器仍然在线。我本以为他们会很快开始下线,但我看到 "servers online" 的数量在大约 110 到 140 之间波动(上下波动)。其中绝大多数都坐在那里有 0 requests/s,没有 CPU,没有记忆。
所以,一些问题:
- 这是预期的吗?
- 指导联机/保持联机的服务器数量的启发式是什么?
- 我应该在什么时候看到这个数字回落到我习惯的水平?
Scale Controller 是一种闭源专有技术,具有未记录的行为。您的问题没有官方答案。回答它的唯一方法是做你所做的:运行 实验和测量。不过,这种行为可能会随着时间的推移而改变。
函数确实有一些启发式方法,可以根据事件数量和资源利用率确定何时扩展和缩减。过去,他们非常节俭,但这并没有提供足够好的横向扩展速度。现在,他们倾向于错误地配置太多而不是配置太少。
好消息是,除非出于好奇,否则您不应该真正关心缩放。您无需为闲置服务器付费。
只是为@Mikhail 写的内容添加一些内容 - 您的函数缩放方式取决于所用触发器的类型。如果您使用队列,运行时将根据消息的数量查看队列长度和规模 up/down。对于事件中心,此行为取决于中心中的分区数量 - 您拥有的分区越多,您就越渴望扩展您的功能。
可以查看源代码并尝试至少了解部分功能。事实上,它是基于一个计时器和一个工人的概念,它们更新它们的状态并让运行时决定是否需要扩大或缩小规模。
整体算法描述如下:
protected virtual async Task MakeScaleDecision(string activityId, IWorkerInfo manager)
{
if (DateTime.UtcNow < _scaleCheckUtc)
{
return;
}
try
{
var workers = await _table.ListNonStale();
_tracer.TraceInformation(activityId, manager, workers.GetSummary("NonStale"));
if (await TryRemoveIfMaxWorkers(activityId, workers, manager))
{
return;
}
if (await TryAddIfLoadFactorMaxWorker(activityId, workers, manager))
{
return;
}
if (await TrySwapIfLoadFactorMinWorker(activityId, workers, manager))
{
return;
}
if (await TryAddIfMaxBusyWorkerRatio(activityId, workers, manager))
{
return;
}
if (await TryRemoveIfMaxFreeWorkerRatio(activityId, workers, manager))
{
return;
}
if (await TryRemoveSlaveWorker(activityId, workers, manager))
{
return;
}
}
catch (Exception ex)
{
_tracer.TraceError(activityId, manager, string.Format("MakeScaleDecision failed with {0}", ex));
}
finally
{
_scaleCheckUtc = DateTime.UtcNow.Add(_settings.ScaleCheckInterval);
}
}
更重要的是,为什么你看到活着的工人也可以在源代码中找到答案:
protected virtual async Task PingWorker(string activityId, IWorkerInfo worker)
{
// if ping was unsuccessful, keep pinging. this is to address
// the issue where site continue to run on an unassigned worker.
if (!_pingResult || _pingWorkerUtc < DateTime.UtcNow)
{
// if PingWorker throws, we will not update the worker status
// this worker will be stale and eventually removed.
_pingResult = await _eventHandler.PingWorker(activityId, worker);
_pingWorkerUtc = DateTime.UtcNow.Add(_settings.WorkerPingInterval);
}
// check if worker is valid for the site
if (_pingResult)
{
await _table.AddOrUpdate(worker);
_tracer.TraceUpdateWorker(activityId, worker, string.Format("Worker loadfactor {0} updated", worker.LoadFactor));
}
else
{
_tracer.TraceWarning(activityId, worker, string.Format("Worker does not belong to the site."));
await _table.Delete(worker);
_tracer.TraceRemoveWorker(activityId, worker, "Worker removed");
throw new InvalidOperationException("The worker does not belong to the site.");
}
}
不幸的是,部分实现是密封的(如IWorkerInfo
),所以你无法得到全貌,我们只能猜测(或询问;))