用于 C++ 的 Azure SDK 中的 HTTP 管道和重试

用于C++的 Azure SDK 使用 HTTP 管道体系结构来处理对 Azure 服务的 HTTP 请求。 本文档介绍了 HTTP 管道的工作原理、重试策略的实现方式,以及如何根据应用程序需求自定义这些策略。

HTTP 管道体系结构

什么是 HTTP 管道?

HTTP 管道是一堆 HTTP 策略,可按顺序应用以处理 HTTP 请求和响应。 Azure SDK 中的每个客户端都有自己的 HTTP 管道。 管道中的策略塑造了 HTTP 请求的处理方式,包括以下作:

  • 添加身份验证标头
  • 请求/响应日志记录
  • 失败请求的重试逻辑
  • 遥测收集
  • 传输处理(实际发送 HTTP 请求)

管道拆分为两个主要部分:

  1. 每次调用策略 - 每次 API 操作执行一次
  2. 每次重试策略 - 针对每次重试执行

此结构可确保适当的策略(例如身份验证)在每个操作中仅执行一次,而其他策略(例如日志记录)则在每次重试尝试时执行。

策略排序

Azure SDK for C++ 中典型的 HTTP 管道包括以下策略:

  1. 遥测策略 (每次调用) - 添加 Azure SDK 遥测信息
  2. 请求 ID 策略 (每调用) - 确保每个请求都具有唯一 ID
  3. 特定于服务的 Per-Call 策略 - 特定于服务的自定义策略
  4. 重试策略(每次调用) - 实现重试机制逻辑
  5. 服务特定的 Per-Retry 策略 - 每次重试时运行的服务自定义策略
  6. 请求活动策略(每次重试)- 管理分布式跟踪
  7. 日志策略 (每次重试)- 处理请求和响应的日志记录
  8. 传输策略(每次重试) - 负责发送 HTTP 请求

一个显示 Azure SDK for C++ HTTP 管道策略阶段的示意图。

重试策略

重试的工作原理

重试策略旨在处理向 Azure 服务发出 HTTP 请求时可能发生的暂时性故障。 当请求因暂时性错误而失败时,重试策略将:

  1. 确定失败是否可重试
  2. 计算适当的延迟
  3. 等待该延迟
  4. 重试请求

该策略支持在传输级别故障(网络问题)和某些 HTTP 状态代码上重试。

默认重试行为

默认情况下,重试策略配置为:

  • 最多三次重试尝试
  • 初始重试延迟 800 毫秒
  • 最大重试延迟 60 秒
  • 可重试状态代码:408、429、500、502、503、504

重试延迟使用具有抖动的指数退避策略:

  • 首次重试:约 800 毫秒
  • 第二次重试:约 1,600 毫秒
  • 第三次重试:约 3,200 毫秒
  • 以此类推,直到达到最大重试延迟时间。

当重试发生时

重试策略尝试在以下方案中重试请求:

  1. 传输失败

    • 网络连接问题
    • 连接超时
    • DNS(域名系统)解析失败
  2. HTTP 状态代码

    • 408 (请求超时)
    • 429(请求太多)
    • 500(内部服务器错误)
    • 502(错误的网关)
    • 503(服务不可用)
    • 504 (网关超时)
  3. 特定于服务的重试逻辑

    • 某些服务(例如存储)为故障转移方案实现专用重试逻辑

自定义重试行为

可以通过修改 RetryOptions 客户端选项来自定义创建客户端时的重试行为。

示例:自定义重试选项

#include <azure/storage/blobs.hpp>

int main() 
{
    // Create client options
    Azure::Storage::Blobs::BlobClientOptions options;
    
    // Modify retry options
    options.Retry.MaxRetries = 5;                                    // Increase max retries
    options.Retry.RetryDelay = std::chrono::milliseconds(1000);      // Set initial retry delay to 1 second
    options.Retry.MaxRetryDelay = std::chrono::seconds(30);          // Cap maximum retry delay at 30 seconds
    
    // Add a custom status code to retry on
    options.Retry.StatusCodes.insert(Azure::Core::Http::HttpStatusCode::Forbidden); // Retry on 403 errors
    
    // Create the client with custom retry options
    auto blobClient = Azure::Storage::Blobs::BlobClient::CreateFromConnectionString(
        connectionString,
        containerName,
        blobName,
        options);
    
    // Use the client...
}

添加自定义策略

可以将自定义策略添加到 HTTP 管道以实现专用行为:

添加每个操作策略

无论需要多少次重试,每个 API 操作都会调用一次每个操作的策略。

class MyCustomPolicy final : public Azure::Core::Http::Policies::HttpPolicy {
public:
    ~MyCustomPolicy() override = default;
    std::unique_ptr<HttpPolicy> Clone() const override
    {
        return std::make_unique<MyCustomPolicy>(*this);
    }

    std::unique_ptr<Azure::Core::Http::RawResponse> Send(
        Azure::Core::Http::Request& request,
        Azure::Core::Http::Policies::NextHttpPolicy nextPolicy,
        Azure::Core::Context const& context) const override
    {
        // Custom logic before the request
        
        auto response = nextPolicy.Send(request, context);
        
        // Custom logic after the response
        
        return response;
    }
};

// Adding the policy to client options
Azure::Storage::Blobs::BlobClientOptions options;
options.PerOperationPolicies.emplace_back(std::make_unique<MyCustomPolicy>());

添加重试策略

每次重试尝试都会调用重试策略。

// Similar implementation to above, but add to PerRetryPolicies
options.PerRetryPolicies.emplace_back(std::make_unique<MyCustomRetryPolicy>());

处理辅助终结点

某些 Azure 服务(例如存储)支持辅助终结点以实现高可用性。 SDK 包含对自动故障转移到备份终结点的支持:

Azure::Storage::Blobs::BlobClientOptions options;

// Configure secondary endpoint for Storage
std::string primaryUrl = blobClient.GetUrl();
std::string secondaryUrl = InferSecondaryUrl(primaryUrl); // Your logic to determine secondary URL
std::string secondaryHost = Azure::Core::Url(secondaryUrl).GetHost();

options.SecondaryHostForRetryReads = secondaryHost;

日志记录重试尝试

HTTP 管道包括用于重试的内置日志功能。 可以配置日志记录级别以查看有关重试的信息:

// Set log level to see retry information
Azure::Core::Diagnostics::Logger::SetLevel(Azure::Core::Diagnostics::Logger::Level::Informational);

// Set a custom log listener to capture logs
Azure::Core::Diagnostics::Logger::SetListener([](auto level, auto message) {
    std::cout << "Log [" << static_cast<int>(level) << "]: " << message << std::endl;
});

发生重试时,日志条目如下所示:

  • “HTTP 传输错误: [错误详细信息]”
  • “HTTP 重试尝试 #1 将在 800 毫秒内进行。”
  • “将重试 HTTP 状态代码 503。

最佳做法

  1. 尽可能使用默认重试设置

    • 默认设置针对大多数方案进行了优化,并包括指数退避等最佳做法
  2. 注意非幂等的操作

    • 考虑限制重试不安全的操作(例如非幂等的 POST 请求)
  3. 考虑断路器模式

    • 对于高负载应用程序,实施断路器设计模式,以防止服务因响应错误而变得不堪重负。
  4. 测试重试方案

    • 在重试发生时测试应用程序的行为,以确保正确处理
  5. 监视重试遥测

    • 高重试率可能表示应解决的基础问题

高级:管线内部结构

HTTP 管道在类中 Azure::Core::Http::_internal::HttpPipeline 实现,用于管理策略执行序列。 提出请求时,管道将:

  1. 从管道中的第一个策略开始
  2. 每个策略处理请求,然后将其传递给下一个策略
  3. 最后一个策略通常是实际发送请求的传输策略
  4. 然后,响应以逆序流经各个策略

重试策略的特别之处在于,它可以在管道中重复其后出现的整个政策序列。

故障排除

如果您在重试时遇到问题:

  1. 启用信息日志记录

    • AZURE_LOG_LEVEL 环境变量设置为 Informational 以查看重试情况
  2. 检查传输错误

    • 网络问题通常显示为传输异常
  3. 验证服务运行状况

    • 永久性 500 级错误可能表示 Azure 服务问题
  4. 查看请求 ID

    • 每个请求都有一个唯一的 ID,可在使用 Azure 支持时使用
  5. 检查超时设置

    • 确保应用程序的超时与重试策略兼容