排查 Azure 存储帐户中的客户端应用程序错误
本文介绍如何使用 Azure Monitor 中的指标、客户端日志和资源日志来调查客户端应用程序错误。
诊断错误
应用程序用户可能会向你通知客户端应用程序报告的错误。 Azure Monitor 还会记录存储服务的不同响应类型(ResponseType 维度)的计数,例如 NetworkError、ClientTimeoutError 或 AuthorizationError。 虽然 Azure Monitor 仅记录不同错误类型的计数,但可以通过检查服务器端日志、客户端日志和网络日志来获取有关单个请求的详细信息。 通常,存储服务返回的 HTTP 状态代码会指示请求失败的原因。
注意
请记住,你应该会看到一些间歇性错误。 例如,由于暂时性网络条件或应用程序错误而导致的错误。
以下资源对了解与存储相关的状态和错误代码很有帮助:
客户端正在接收“HTTP 403 (禁止访问)”消息
如果客户端应用程序引发“HTTP 403 (禁止访问)”错误,则可能的原因是客户端在发送存储请求时使用了过期的共享访问签名 (SAS)(虽然其他可能的原因包括时钟偏差、无效密钥和空标头)。
通过用于 .NET 的存储客户端库,可以收集与应用程序执行的存储操作相关的客户端日志数据。 有关详细信息,请参阅 Client-side Logging with the .NET Storage Client Library(使用 .NET 存储客户端库的客户端日志记录)。
下表显示了存储客户端库生成的客户端日志的示例,它说明了如何出现此问题:
源 | 详细程度 | 详细程度 | 客户端请求 ID | 操作文本 |
---|---|---|---|---|
Microsoft.Azure.Storage | 信息 | 3 | 85d077ab-... | Starting operation with location Primary per location mode PrimaryOnly. |
Microsoft.Azure.Storage | 信息 | 3 | 85d077ab -… | Starting synchronous request to <https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests#Synchronous_request> |
Microsoft.Azure.Storage | 信息 | 3 | 85d077ab -… | Waiting for response. |
Microsoft.Azure.Storage | 警告 | 2 | 85d077ab -… | Exception thrown while waiting for response: The remote server returned an error: (403) Forbidden. |
Microsoft.Azure.Storage | 信息 | 3 | 85d077ab -… | Response received. Status code = 403, Request ID = <Request ID>, Content-MD5 = , ETag = . |
Microsoft.Azure.Storage | 警告 | 2 | 85d077ab -… | Exception thrown during the operation: The remote server returned an error: (403) Forbidden.. |
Microsoft.Azure.Storage | 信息 | 3 | 85d077ab -… | Checking if the operation should be retried. Retry count = 0, HTTP status code = 403, Exception = The remote server returned an error: (403) Forbidden.. |
Microsoft.Azure.Storage | 信息 | 3 | 85d077ab -… | The next location has been set to Primary, based on the location mode. |
Microsoft.Azure.Storage | 错误 | 1 | 85d077ab -… | Retry policy did not allow for a retry. Failing with The remote server returned an error: (403) Forbidden. |
在此方案中,应调查在客户端将该令牌发送到服务器之前 SAS 令牌即将到期的原因:
通常,不应在为客户端创建 SAS 时设置开始时间,以便客户端立即使用。 如果使用当前时间生成 SAS 的主机与存储服务之间存在较小的时钟差异,则存储服务可能会收到尚未生效的 SAS。
不要在 SAS 上设置非常短的到期时间。 同样,生成 SAS 的主机与存储服务之间的较小时钟差异可能会导致 SAS 早于预期到期。
SAS 密钥(例如
sv
=2015-04-05)中的版本参数是否与正在使用的存储客户端库的版本匹配? 建议始终使用最新版本的存储客户端库。如果重新生成存储访问密钥,可能会使任何现有的 SAS 令牌无效。 如果生成的 SAS 令牌具有较长的到期时间供客户端应用程序缓存,可能会出现此问题。
如果使用存储客户端库生成 SAS 令牌,则可轻松生成有效令牌。 但如果使用的是存储 REST API 并手动构造 SAS 令牌,请参阅使用共享访问签名委派访问权限。
客户端正在接收“HTTP 404 (未找到)”消息
如果客户端应用程序从服务器收到“HTTP 404(未找到)”消息,这意味着客户端正在尝试使用的对象(如实体、表、Blob、容器或队列)在存储服务中不存在。 有多种原因可能会导致此问题,例如:
客户端或其他进程先前删除了该对象。
共享访问签名(SAS)授权问题。
客户端 JavaScript 代码无权访问该对象。
网络故障。
客户端或其他进程以前删除了该对象
在客户端尝试读取、更新或删除存储服务中的数据的情况下,很容易在存储资源中识别之前从存储服务中删除有问题的对象的操作。 通常,日志数据显示其他用户或进程删除了该对象。 Azure Monitor 日志(服务器端)显示在客户端删除对象时。
在客户端尝试插入对象的情况下,鉴于客户端正在创建新对象,因此这可能会导致 HTTP 404 (找不到)响应的原因可能并不明显。 但是,如果客户端正在创建 Blob,则必须能够找到 Blob 容器。 如果客户端正在创建消息,则必须能够找到队列。 如果客户端正在添加行,则它必须能够找到该表。
可以使用存储客户端库中的客户端日志更好地了解客户端何时向存储服务发送特定请求。
存储客户端库生成的以下客户端日志说明了客户端找不到要创建的 Blob 的容器时出现的问题。 此日志包含以下存储操作的详细信息:
请求 ID | 操作 |
---|---|
07b26a5d-... | DeleteIfExists 用于删除 Blob 容器的方法。 此操作包括一个 HEAD 请求,用于检查容器是否存在。 |
e2d06d78-... | CreateIfNotExists 用于创建 Blob 容器的方法。 此操作包括检查 HEAD 容器是否存在的请求。 返回 HEAD 404 消息,但继续。 |
de8b1c3c-... | UploadFromStream 用于创建 Blob 的方法。 请求 PUT 失败并显示 404 消息 |
日志条目:
请求 ID | 操作文本 |
---|---|
07b26a5d-... | Starting synchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer. |
07b26a5d-... | StringToSign = HEAD............x-ms-client-request-id:07b26a5d-....x-ms-date:Tue, 03 Jun 2014 10:33:11 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer.restype:container. |
07b26a5d-... | Waiting for response. |
07b26a5d-... | Response received. Status code = 200, Request ID = eeead849-...Content-MD5 = , ETag = "0x8D14D2DC63D059B". |
07b26a5d-... | Response headers were processed successfully, proceeding with the rest of the operation. |
07b26a5d-... | Downloading response body. |
07b26a5d-... | Operation completed successfully. |
07b26a5d-... | Starting synchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer. |
07b26a5d-... | StringToSign = DELETE............x-ms-client-request-id:07b26a5d-....x-ms-date:Tue, 03 Jun 2014 10:33:12 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer.restype:container. |
07b26a5d-... | Waiting for response. |
07b26a5d-... | Response received. Status code = 202, Request ID = 6ab2a4cf-..., Content-MD5 = , ETag = . |
07b26a5d-... | Response headers were processed successfully, proceeding with the rest of the operation. |
07b26a5d-... | Downloading response body. |
07b26a5d-... | Operation completed successfully. |
e2d06d78-... | Starting asynchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer 。 |
e2d06d78-... | StringToSign = HEAD............x-ms-client-request-id:e2d06d78-....x-ms-date:Tue, 03 Jun 2014 10:33:12 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer.restype:container. |
e2d06d78-... | Waiting for response. |
de8b1c3c-... | Starting synchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer/blobCreated.txt. |
de8b1c3c-... | StringToSign = PUT...64.qCmF+TQLPhq/YYK50mP9ZQ==........x-ms-blob-type:BlockBlob.x-ms-client-request-id:de8b1c3c-....x-ms-date:Tue, 03 Jun 2014 10:33:12 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer/blobCreated.txt. |
de8b1c3c-... | Preparing to write request data. |
e2d06d78-... | Exception thrown while waiting for response: The remote server returned an error: (404) Not Found.. |
e2d06d78-... | Response received. Status code = 404, Request ID = 353ae3bc-..., Content-MD5 = , ETag = . |
e2d06d78-... | Response headers were processed successfully, proceeding with the rest of the operation. |
e2d06d78-... | Downloading response body. |
e2d06d78-... | Operation completed successfully. |
e2d06d78-... | Starting asynchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer. |
e2d06d78-... | StringToSign = PUT...0.........x-ms-client-request-id:e2d06d78-....x-ms-date:Tue, 03 Jun 2014 10:33:12 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer.restype:container. |
e2d06d78-... | Waiting for response. |
de8b1c3c-... | Writing request data. |
de8b1c3c-... | Waiting for response. |
e2d06d78-... | Exception thrown while waiting for response: The remote server returned an error: (409) Conflict.. |
e2d06d78-... | Response received. Status code = 409, Request ID = c27da20e-..., Content-MD5 = , ETag = . |
e2d06d78-... | Downloading error response body. |
de8b1c3c-... | Exception thrown while waiting for response: The remote server returned an error: (404) Not Found.. |
de8b1c3c-... | Response received. Status code = 404, Request ID = 0eaeab3e-..., Content-MD5 = , ETag = . |
de8b1c3c-... | Exception thrown during the operation: The remote server returned an error: (404) Not Found.. |
de8b1c3c-... | Retry policy did not allow for a retry. Failing with The remote server returned an error: (404) Not Found.. |
e2d06d78-... | Retry policy did not allow for a retry. Failing with The remote server returned an error: (409) Conflict.. |
在此示例中,日志显示客户端正在交错来自 CreateIfNotExists
方法(请求 ID e2d06d78...)的请求与来自方法(de8b1c3c-...)的请求 UploadFromStream
。发生这种交错,因为客户端应用程序正在异步调用这些方法。 应修改客户端中的异步代码,以确保客户端在尝试将任何数据上传到该容器中的 Blob 之前已创建该容器。 理想情况下,应该提前创建所有容器。
共享访问签名 (SAS) 授权问题
如果客户端应用程序尝试使用不包括必要的操作权限的 SAS 密钥,则存储服务会向客户端返回“HTTP 404(未找到)”消息。 同时,在 Azure Monitor 指标中,还将看到 ResponseType 维度的 AuthorizationError。
调查客户端应用程序尝试对其未授予权限的操作的原因。
客户端 JavaScript 代码无权访问该对象
如果使用 JavaScript 客户端,并且存储服务返回 HTTP 404 消息,请在浏览器中检查以下 JavaScript 错误:
SEC7120:Access-Control-Allow-Origin 标头中找不到源 http://localhost:56309 。
SCRIPT7002:XMLHttpRequest:网络错误0x80070005,拒绝访问。
注意
在排查客户端 JavaScript 问题时,可以使用 Internet Explorer 中的 F12 开发人员工具来跟踪浏览器与存储服务之间交换的消息。
之所以发生这些错误是因为 Web 浏览器实施了同源策略安全限制,以防止网页调用与它来自的域不同的域中的 API。
若要解决 JavaScript 问题,可以为客户端正在访问的存储服务配置跨域资源共享(CORS)。 有关详细信息,请参阅 Cross-Origin Resource Sharing (CORS) Support for Azure Storage Services(Azure 存储服务的跨域资源共享 (CORS) 支持)。
下面的代码示例演示如何配置 Blob 服务,以允许在 Contoso 域中运行的 JavaScript 访问 Blob 存储服务中的 Blob:
var connectionString = Constants.connectionString;
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
BlobServiceProperties sp = blobServiceClient.GetProperties();
// Set the service properties.
sp.DefaultServiceVersion = "2013-08-15";
BlobCorsRule bcr = new BlobCorsRule();
bcr.AllowedHeaders = "*";
bcr.AllowedMethods = "GET,POST";
bcr.AllowedOrigins = "http://www.contoso.com";
bcr.ExposedHeaders = "x-ms-*";
bcr.MaxAgeInSeconds = 5;
sp.Cors.Clear();
sp.Cors.Add(bcr);
blobServiceClient.SetProperties(sp);
网络故障
在某些情况下,丢失的网络数据包可能会导致存储服务向客户端返回 HTTP 404 消息。 例如,当客户端应用程序从表服务中删除实体时,会看到客户端会引发存储异常,报告表服务中的“HTTP 404(找不到)”状态消息。 调查表存储服务中的表时,看到该服务确实删除了请求的实体。
客户端中的异常详细信息包括表服务为请求分配的请求 ID (7e84f12d...):可以通过在日志项的描述如何对操作进行身份验证的字段中搜索,来使用此信息在 Azure Monitor 的存储资源日志中查找请求详细信息。 此外,还可以使用度量值来确定此类失败何时发生,并基于度量值记录此错误的时间搜索日志文件。 此日志项显示删除失败并返回“HTTP (404) 客户端其他错误”状态消息。 同一日志条目还包括客户端在 client-request-id
列中生成的请求 ID(813ea74f...)。
服务器端日志还包括另一个具有相同 client-request-id
值(813ea74f...)的条目,用于成功执行同一实体和同一客户端的删除操作。 此成功的删除操作发生在失败的删除请求之前不久。
此方案最可能的原因是客户端向表服务发送了实体的删除请求,该请求已成功,但未从服务器收到确认(可能是由于临时网络问题)。 然后,客户端会自动重试操作(使用相同的 client-request-id
),并且此重试失败,因为实体已被删除。
如果此问题频繁出现,应该调查为什么客户端无法从表服务收到确认。 如果此问题是间歇性的,则应捕获“HTTP (404) 找不到”错误并在客户端中记录它,但允许客户端继续执行。
客户端正在接收“HTTP 409 (冲突)”消息
当客户端删除 Blob 容器、表或队列时,名称再次可用前有一段短暂的句点。 如果客户端应用程序中的代码删除,然后立即使用相同的名称重新创建 Blob 容器,该方法 CreateIfNotExists
最终会失败并出现 HTTP 409(冲突)错误。
客户端应用程序在创建新容器时应使用唯一的容器名称(如果“删除/重新创建”模式很常见)。
度量值显示低 PercentSuccess,或者分析日志项包含事务状态为 ClientOtherErrors 的操作
与 Success 值相等的 ResponseType 维度捕获基于其 HTTP 状态代码成功的操作百分比。 状态代码为 2XX 的操作计数为成功,而状态代码在 3XX、4XX 和 5XX 范围内的操作将计为失败并降低成功指标值。 在存储资源日志中,这些操作将使用事务状态 ClientOtherError 进行记录。
这些操作已成功完成,因此不会影响其他指标,例如可用性。 成功执行但可能会导致失败的 HTTP 状态代码的一些操作示例包括:
- ResourceNotFound (找不到 404),例如,从 GET 请求到不存在的 Blob。
- ResourceAlreadyExists (冲突 409),例如,来自
CreateIfNotExist
资源已存在的操作。 - ConditionNotMet (未修改 304),例如,从条件操作(例如,当客户端发送
ETag
值和 HTTPIf-None-Match
标头时)请求图像时,仅当自上次操作以来已更新映像时。
可以在常见的 REST API 错误代码页上找到存储服务返回的常见 REST API 错误代码的列表。
另请参阅
联系我们寻求帮助
如果你有任何疑问或需要帮助,请创建支持请求或联系 Azure 社区支持。 你还可以将产品反馈提交到 Azure 反馈社区。