你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

诊断和排查适用于 Azure Cosmos DB 的 Azure Functions 触发器的问题

适用范围: NoSQL

本文介绍在使用适用于 Azure Cosmos DB 的 Azure Functions 触发器时出现的常见问题及其解决方法和诊断步骤。

依赖项

适用于 Azure Cosmos DB 的 Azure Functions 触发器和绑定依赖于基于基础 Azure Functions 运行时的扩展包 Microsoft.Azure.WebJobs.Extensions.CosmosDB。 请始终保持这些包的更新状态,因为它们可能包含用于解决你所遇到的任何潜在问题的修复程序和新功能。

单独使用 Azure Cosmos DB SDK

该扩展包的关键功能是为适用于 Azure Cosmos DB 的 Azure Functions 触发器和绑定提供支持。 该包还包含 Azure Cosmos DB.NET SDK,用于帮助你以编程方式来与 Azure Cosmos DB 交互,而无需使用触发器和绑定。

若要使用 Azure Cosmos DB SDK,请务必不要将项目添加到另一个 NuGet 包引用。 而是让 SDK 引用通过 Azure Functions 的扩展包进行解析。 独立于触发器和绑定使用 Azure Cosmos DB SDK。

此外,如果你手动创建自己的 Azure Cosmos DB SDK 客户端实例,应遵循以下模式:只提供一个使用单一实例模式方法的客户端实例。 此过程可避免操作中出现潜在的套接字问题。

常见情景和解决方法

Azure 函数失败并出现一条错误消息,指出集合“不存在”

Azure 函数失败并出现以下错误消息:“源集合 'collection-name' (在数据库 'database-name' 中)或租约集合 'collection2-name' (在数据库 'database2-name' 中)不存在。 在侦听器启动之前,这两个集合必须存在。 若要自动创建租约集合,请将 'CreateLeaseCollectionIfNotExists' 设置为 'true'”。

这表示运行触发器所需的一个或两个 Azure Cosmos DB 容器处于以下任一状态:

  • 不存在
  • 无法由 Azure 函数访问

错误文本本身告知触发器正在根据配置查找的 Azure Cosmos DB 数据库和容器。

若要解决此问题,请执行下列操作:

  1. 验证 Connection 属性,以及它是否引用了 Azure 函数应用中存在的设置。

    此属性中的值不应是连接字符串本身,而是配置设置的名称。

  2. 验证 databaseNamecontainerName 值是否在你的 Azure Cosmos DB 帐户中存在。

    如果使用自动值替换(使用 %settingName% 模式),请确保该设置的名称在 Azure 函数应用中存在。

  3. 如果未指定 LeaseContainerName/leaseContainerName 值,则默认值为 leases。 验证此类容器是否存在。

    (可选)可将触发器中的 CreateLeaseContainerIfNotExists 属性设置为 true,以自动创建该容器。

  4. 验证 Azure Cosmos DB 帐户的防火墙配置,以确保它未阻止 Azure 函数。

Azure 函数无法启动并出现错误消息“共享吞吐量集合应有分区键”

旧版 Azure Cosmos DB 扩展不支持使用在共享吞吐量数据库中创建的租约容器。

若要解决此问题,请执行下列操作:

Azure 函数无法启动且出现错误消息“必须为此操作提供 PartitionKey”

此错误表示正在使用带有旧版扩展依赖项的分区租约集合。

若要解决此问题,请执行下列操作:

  • 请升级到最新的可用版本。

Azure 函数无法启动,显示错误消息“禁止访问 (403);子状态:5300...数据平面中的 Microsoft Entra 令牌无法授权给定请求 [POST ...]”

此错误意味着函数尝试使用 Microsoft Entra 标识执行非数据操作。 使用 Microsoft Entra 标识时无法使用 CreateLeaseContainerIfNotExists = true

Azure 函数无法启动并出现错误消息“租约集合(如果已分区)必须有与 ID 相同的分区键”

此错误表示当前租约容器已分区,但分区键路径不是 /id

若要解决此问题,请执行下列操作:

  • 使用 /id 作为分区键来重新创建租约容器。

尝试运行触发器时,Azure 函数日志中出现错误消息“值不能为 null。 参数名称: o”

如果使用 Azure 门户,并在检查使用触发器的 Azure 函数时选择“运行”按钮,则可能会出现此问题。 触发器不需要你选择“运行”即可启动。 部署 Azure 函数时它会自动启动。

若要解决此问题,请执行下列操作:

  • 若要在 Azure 门户上检查函数的日志流,请转到受监视的容器并插入一些新项。 触发器将自动运行。

接收更改花费了过长的时间

这种情况可能是多种原因造成的。 请考虑尝试以下任何或所有解决方法:

  • 你的 Azure 函数是否与 Azure Cosmos DB 帐户部署在不同的区域? 为了优化网络延迟,应将 Azure 函数和 Azure Cosmos DB 帐户共置在同一个 Azure 区域。

  • Azure Cosmos DB 容器中发生的更改是连续性的还是偶发性的?

    如果它们是偶发性的,则原因可能是存储更改的时间与 Azure Functions 拾取更改的时间有一段延迟。 这是因为,当触发器在内部检查 Azure Cosmos DB 容器中的更改但未找到任何等待读取的更改时,它将休眠一定的时间(可配置,默认为 5 秒),然后检查新的更改。 它这样做的目的是避免请求单位 (RU) 消耗量过高。 可以通过触发器的配置中的 FeedPollDelay/feedPollDelay 设置来配置休眠时间。 该值应以毫秒为单位。

  • Azure Cosmos DB 容器可能受到速率限制

  • 可以使用触发器中的 PreferredLocations 属性来指定 Azure 区域的逗号分隔列表,以定义自定义的首选连接顺序。

  • 触发器接收新更改的速度取决于处理它们的速度。 验证函数的执行时间或持续时间。 如果函数速度缓慢,则会增加触发器获取新更改所需的时间。 如果你发现持续时间最近有所增加,原因可能是最近的代码影响了函数。 如果在 Azure Cosmos DB 容器上接收操作的速度快于触发器的速度,那么你将始终保持滞后状态。 建议调查函数的代码,以确定最耗时的操作以及如何优化。

  • 可以使用调试日志检查诊断并验证是否存在网络延迟。

某些更改在我的触发器中重复

更改这一概念指的是对某项执行的操作。 收到同一项目的事件的最常见情况包括:

  • 函数在执行期间失败。 如果函数已启用重试策略,或者如果函数执行超出了允许的执行时间,可以再次将同一批更改传递到函数。 这是意料之中的,也是设计使然,请查看函数日志以了解故障的指示,并确保已启用触发器日志以获取更多详细信息。

  • 实例之间存在租用时间的负载均衡。 当实例增加或减少时,负载均衡可能会导致将同一批更改传递到多个函数实例。 这是意料之中的,也是设计使然,并且应该是暂时的。 触发器日志包括实例获取和释放租用时间时的事件。

  • 正在更新该项。 更改源可能包含对同一项的多个操作。 如果该项正在接收更新,则它可能会选取多个事件(每个更新一个事件)。 要对同一项的不同操作进行区分,可以采用一个简单方法,即跟踪_lsn每个更改的属性。 如果属性不匹配,则更改将不同。

  • 如果只通过 id 来标识项,请记住,项的唯一标识符是 id 以及其分区键。 (两个项可以具有相同的 id,但分区键不同)。

触发器中缺少某些更改

你可能发现,Azure 函数未拾取 Azure Cosmos DB 容器中发生的某些更改。 或者在复制更改时,目标位置缺少一些更改。 如果是这样,请尝试本部分所述的解决方法。

  • 确保已启用日志。 验证在处理过程中没有发生错误。

  • 当 Azure 函数收到更改时,它通常会处理这些更改,并可能会选择性地将结果发送到另一个目标。 调查丢失更改的问题时,请确保度量在引入时间点(即启动 Azure 函数时)收到的更改,而不要度量目标上的更改。

  • 如果目标中缺少某些更改,可能意味着在收到更改后执行 Azure 函数期间发生了某种错误。

    在这种情况下,最佳措施是在代码中以及在可能正在处理更改的循环中添加 try/catch 块。 添加此块有助于检测特定的项子集发生的任何失败,并相应地对其进行处理(将这些项发送到另一个存储以做进一步的分析或重试)。 此外,可以配置 Azure Functions 重试策略

    注意

    默认情况下,如果在代码执行期间发生未经处理的异常,则适用于 Azure Cosmos DB 的 Azure Functions 触发器不会重试一批更改。 这意味着,更改未抵达目标的原因可能是无法处理它们。

  • 如果目标是另一个 Azure Cosmos DB 容器,并且你正在执行更新插入操作来复制项,请验证受监视容器和目标容器上的分区键定义是否相同。 由于此配置差异,更新插入操作可能会将多个源项保存为目标上的一个项。

  • 如果你发现触发器未收到某些更改,则最常见的原因是有另一个 Azure 函数正在运行。 另一个函数可能部署在 Azure 中,或者是在开发人员计算机本地运行的、采用完全相同配置的函数(即,相同的受监视容器和租约容器)。 如果是这样,此函数可能正在窃取你的 Azure 函数预期要处理的更改子集。

此外,如果你知道正在运行多少个 Azure 函数应用实例,则也可以验证这种情况。 如果检查租约容器并统计其中包含的租约项数,这些项中的非重复 Owner 属性值应等于函数应用的实例数。 如果所有者数目超过已知的 Azure 函数应用实例数,则表示这些多出的所有者正在“窃取”更改。

若要解决此问题,一个简单的方法是将采用新值或不同值的 LeaseCollectionPrefix/leaseCollectionPrefix 应用到你的函数,或使用新的租约容器进行测试。

需要重启并从头开始重新处理容器中的所有项

若要从头开始重新处理容器中的所有项,请执行以下操作:

  1. 如果 Azure 函数当前正在运行,请将其停止。

  2. 删除租约集合中的文档(或删除租约集合并重新创建一个空集合)。

  3. 将函数中的 StartFromBeginning CosmosDBTrigger 属性设置为 true

  4. 重启 Azure 函数。 现在,它会从头开始读取并处理所有更改。

如果将 StartFromBeginning 设置为 true,则会告知 Azure 函数要从头开始读取集合历史记录的更改,而不是从当前时间开始读取。

此解决方法仅适用于尚未创建租约(即租约集合中的文档)的情况。

如果已创建租约,则将此属性设置为 true 不起作用。 在这种情况下,当某个函数停止并重启时,它将从租约集合中定义的最后一个检查点开始读取。

错误:只能通过 IReadOnlyList<Document> 或 JArray 进行绑定

如果 Azure Functions 项目(或任何引用的项目)包含对 Azure Cosmos DB SDK 的手动 NuGet 引用,而该 SDK 的版本与 Azure Functions 的 Azure Cosmos DB 扩展提供的版本不同,则会发生此错误。

若要解决此问题,请删除已添加的手动 NuGet 引用,并让 Azure Cosmos DB SDK 引用通过 Azure Functions 的 Azure Cosmos DB 扩展包进行解析。

更改 Azure 函数在检测更改时的轮询间隔

如前面的接收更改花费了过长的时间部分中所述,Azure 函数将休眠一定的时间(可配置,默认为 5 秒),然后检查新的更改(以避免 RU 消耗量过高)。 可以通过触发器配置中的 FeedPollDelay/feedPollDelay 设置来配置此休眠时间(该值应以毫秒为单位)。

后续步骤