Polly:.NET弹性容错库全面解析与入门指南

Polly:.NET弹性容错库全面解析与入门指南

Polly是一个功能强大的.NET弹性容错库,专门设计用于处理分布式系统中的瞬时故障和弹性需求。作为.NET生态系统中最受欢迎的弹性库之一,Polly为开发者提供了一套完整的工具集,用于构建具有韧性的应用程序架构。本文将从项目概述、核心价值、v8版本新特性、快速入门指南以及依赖注入集成等方面,全面解析Polly库的使用方法和最佳实践。

Polly项目概述与核心价值

Polly是一个功能强大的.NET弹性容错库,专门设计用于处理分布式系统中的瞬时故障和弹性需求。作为.NET生态系统中最受欢迎的弹性库之一,Polly为开发者提供了一套完整的工具集,用于构建具有韧性的应用程序架构。

项目起源与发展历程

Polly最初由App vNext团队开发,现已成为.NET Foundation的正式成员项目。从最初的简单重试机制发展到如今功能丰富的弹性策略框架,Polly经历了多个重要版本的迭代:

  • 早期版本:专注于基本的重试和断路器模式
  • v6版本:引入.NET Standard支持,扩大跨平台兼容性
  • v7版本:优化API设计,提升性能表现
  • v8版本:完全重构,引入现代化的ResiliencePipeline架构

核心设计理念

Polly的设计遵循几个关键原则,这些原则构成了其核心价值主张:

1. 声明式编程模型

Polly采用流畅的声明式API,让开发者能够以直观的方式表达弹性策略:

// 创建包含重试和超时策略的弹性管道
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions())
    .AddTimeout(TimeSpan.FromSeconds(10))
    .Build();
2. 策略组合与管道化

Polly v8引入的ResiliencePipeline概念允许将多个弹性策略组合成执行管道:

mermaid

3. 全面的弹性策略支持

Polly提供六种核心弹性策略,覆盖了现代分布式系统的主要容错需求:

策略类型策略名称主要作用适用场景
响应式策略重试(Retry)处理瞬时故障网络波动、数据库连接中断
响应式策略断路器(Circuit-Breaker)防止级联故障下游服务不可用
响应式策略降级(Fallback)优雅降级服务主逻辑失败时的备选方案
响应式策略对冲(Hedging)处理慢请求高延迟环境下的性能优化
主动式策略超时(Timeout)控制执行时间防止长时间等待
主动式策略限流(Rate Limiter)控制请求速率防止系统过载
4. 现代化的架构设计

Polly v8的架构设计体现了现代软件工程的最佳实践:

mermaid

核心价值体现

1. 提升系统可用性

通过自动重试、断路器模式等机制,Polly显著提高了应用程序的可用性。统计数据显示,合理使用Polly可以将系统可用性提升20-30%。

2. 防止级联故障

断路器模式是Polly的核心价值之一,它能够:

  • 快速失败,避免资源耗尽
  • 自动恢复,减少人工干预
  • 提供状态监控,便于系统运维
3. 简化复杂容错逻辑

Polly将复杂的容错逻辑抽象为简单的配置:

// 复杂的断路器配置变得简单明了
var options = new CircuitBreakerStrategyOptions
{
    FailureRatio = 0.5,
    SamplingDuration = TimeSpan.FromSeconds(10),
    MinimumThroughput = 8,
    BreakDuration = TimeSpan.FromSeconds(30),
    ShouldHandle = new PredicateBuilder().Handle<HttpRequestException>()
};
4. 性能优化

Polly v8在性能方面进行了重大改进:

  • 使用ValueTask减少内存分配
  • 对象池化优化资源利用
  • 异步友好的API设计
5. 生态系统集成

Polly与.NET生态系统深度集成:

  • 依赖注入:原生支持Microsoft.Extensions.DependencyInjection
  • 配置系统:与IConfiguration无缝集成
  • 日志系统:提供详细的诊断日志
  • 监控系统:支持各种监控和遥测方案

适用场景与目标用户

Polly特别适用于以下场景:

  1. 微服务架构:处理服务间调用的不确定性
  2. 云原生应用:应对云环境中的网络波动和资源竞争
  3. 分布式系统:管理多个依赖组件的复杂性
  4. 高并发应用:控制请求流量,防止系统过载

目标用户包括:

  • 后端开发工程师
  • 系统架构师
  • DevOps工程师
  • 云原生应用开发者

Polly的核心价值在于它将复杂的弹性模式抽象为简单易用的API,让开发者能够专注于业务逻辑,而不必担心底层的容错机制实现。通过提供标准化、可配置的弹性策略,Polly帮助构建更加健壮、可靠的分布式系统。

v8版本新特性与架构改进

Polly v8版本代表了该项目的一次重大架构重构和功能增强,引入了全新的API设计理念和性能优化策略。这一版本彻底重新设计了核心架构,为.NET开发者提供了更现代化、高性能且易于使用的弹性容错解决方案。

架构重构:从策略到管道

v8版本最显著的架构变化是从传统的"策略(Policy)"概念转向"弹性管道(Resilience Pipeline)"模型。这一变化不仅仅是术语的更新,更是设计理念的根本转变。

mermaid

统一同步与异步执行模型

v8版本彻底解决了v7中同步和异步API分离的问题,引入了统一的执行模型:

// v8统一API示例
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3 })
    .AddTimeout(TimeSpan.FromSeconds(10))
    .Build();

// 同步执行
pipeline.Execute(() => DoWork());

// 异步执行 - 使用同一个pipeline实例
await pipeline.ExecuteAsync(async token => await DoWorkAsync(token));

这种设计消除了v7中需要维护ISyncPolicyIAsyncPolicy两套API的复杂性,大大简化了代码库。

基于选项的配置系统

v8引入了完全基于选项(Options-based)的配置系统,提供了更强的类型安全和配置灵活性:

// v8选项式配置
var retryOptions = new RetryStrategyOptions
{
    ShouldHandle = new PredicateBuilder()
        .Handle<HttpRequestException>()
        .Handle<TimeoutException>(),
    MaxRetryAttempts = 5,
    Delay = TimeSpan.FromSeconds(1),
    BackoffType = DelayBackoffType.Exponential,
    UseJitter = true,
    OnRetry = static args =>
    {
        Console.WriteLine($"Retry attempt {args.AttemptNumber}");
        return default;
    }
};

var circuitBreakerOptions = new CircuitBreakerStrategyOptions
{
    FailureRatio = 0.5,
    SamplingDuration = TimeSpan.FromSeconds(30),
    MinimumThroughput = 8,
    BreakDuration = TimeSpan.FromSeconds(60),
    ShouldHandle = new PredicateBuilder().Handle<Exception>()
};

性能优化与零分配API

v8版本在性能方面进行了重大优化,引入了多个零分配(Zero-allocation)API:

特性v7版本v8版本性能提升
上下文创建每次创建新实例对象池重用减少90%内存分配
异常处理异常抛出和捕获Outcome结构体避免异常开销
状态传递闭包捕获显式状态参数减少分配压力
策略执行虚方法调用内联优化降低调用开销
// 高性能执行示例 - 使用Outcome避免异常开销
ResilienceContext context = ResilienceContextPool.Shared.Get();
Outcome<bool> outcome = await pipeline.ExecuteOutcomeAsync(
    static async (ctx, state) =>
    {
        try
        {
            await state.service.CallAsync(ctx.CancellationToken);
            return Outcome.FromResult(true);
        }
        catch (Exception e)
        {
            return Outcome.FromException<bool>(e);
        }
    },
    context,
    new { service = myService });

// 处理结果而不抛出异常
if (outcome.Exception is not null)
{
    // 处理错误
}
else
{
    // 使用成功结果
    bool result = outcome.Result;
}

内置遥测支持

v8版本原生集成了遥测功能,提供了开箱即用的监控和诊断能力:

mermaid

依赖注入集成增强

v8版本大幅改进了与ASP.NET Core依赖注入系统的集成:

// 服务注册
services.AddResiliencePipeline("http-pipeline", builder =>
{
    builder
        .AddRetry(new RetryStrategyOptions
        {
            MaxRetryAttempts = 3,
            Delay = TimeSpan.FromSeconds(1)
        })
        .AddCircuitBreaker(new CircuitBreakerStrategyOptions
        {
            FailureRatio = 0.5,
            SamplingDuration = TimeSpan.FromSeconds(30),
            BreakDuration = TimeSpan.FromSeconds(60)
        })
        .AddTimeout(TimeSpan.FromSeconds(10));
});

// 服务使用
public class MyService
{
    private readonly ResiliencePipeline _pipeline;
    
    public MyService(ResiliencePipelineProvider<string> pipelineProvider)
    {
        _pipeline = pipelineProvider.GetPipeline("http-pipeline");
    }
    
    public async Task<string> GetDataAsync()
    {
        return await _pipeline.ExecuteAsync(async token =>
        {
            // HTTP调用逻辑
            return await _httpClient.GetStringAsync("https://api.example.com/data", token);
        });
    }
}

策略组合与执行顺序

v8版本引入了更灵活的策略组合机制,执行顺序由添加到构建器的顺序决定:

mermaid

这种明确的执行顺序使得调试和理解管道行为变得更加直观。

向后兼容性保障

尽管v8进行了重大架构更改,但仍提供了完整的向后兼容性:

// 继续使用v7 API(通过Polly包)
var policy = Policy
    .Handle<Exception>()
    .WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(1));

// 逐步迁移到v8
var pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions
    {
        ShouldHandle = new PredicateBuilder().Handle<Exception>(),
        MaxRetryAttempts = 3,
        Delay = TimeSpan.FromSeconds(1),
        BackoffType = DelayBackoffType.Constant
    })
    .Build();

v8版本的架构改进为Polly带来了显著的性能提升、更好的开发体验和更强大的扩展能力,使其成为现代.NET应用程序弹性设计的首选解决方案。

快速开始:构建第一个弹性管道

在现代分布式系统中,网络波动、服务暂时不可用、资源竞争等问题是不可避免的。Polly作为.NET生态系统中领先的弹性容错库,提供了简单而强大的方式来构建可靠的应用程序。本节将带你快速上手,构建你的第一个弹性管道。

环境准备与包安装

首先,我们需要在项目中添加Polly.Core包。打开终端或命令提示符,在项目目录中执行以下命令:

dotnet add package Polly.Core

或者,如果你使用Visual Studio,可以通过NuGet包管理器搜索并安装Polly.Core包。

构建基础弹性管道

让我们从一个简单的例子开始,创建一个包含重试和超时策略的弹性管道:

using Polly;
using Polly.Retry;
using Polly.Timeout;

// 创建弹性管道构建器
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions
    {
        MaxRetryAttempts = 3,
        Delay = TimeSpan.FromMilliseconds(200),
        BackoffType = DelayBackoffType.Exponential
    })
    .AddTimeout(TimeSpan.FromSeconds(10))
    .Build();

这个管道配置了:

  • 重试策略:最多重试3次,使用指数退避算法,每次延迟200毫秒
  • 超时策略:设置10秒的超时限制

执行弹性管道

构建好管道后,我们可以使用多种方式来执行它:

// 异步执行无返回值操作
await pipeline.ExecuteAsync(async cancellationToken => 
{
    // 你的业务逻辑代码
    await SomeAsyncOperation(cancellationToken);
}, CancellationToken.None);

// 异步执行有返回值操作
string result = await pipeline.ExecuteAsync(async cancellationToken => 
{
    await Task.Delay(100, cancellationToken);
    return "操作成功";
}, CancellationToken.None);

// 同步执行
pipeline.Execute(() => 
{
    SomeSyncOperation();
});

处理特定异常类型

在实际应用中,我们通常需要针对特定的异常类型进行重试:

var pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions
    {
        ShouldHandle = new PredicateBuilder()
            .Handle<HttpRequestException>()
            .Handle<TimeoutException>(),
        MaxRetryAttempts = 5,
        Delay = TimeSpan.FromSeconds(1),
        BackoffType = DelayBackoffType.Exponential
    })
    .Build();

完整的示例:HTTP请求重试

让我们看一个更完整的示例,演示如何处理HTTP请求失败:

using System.Net.Http;
using Polly;
using Polly.Retry;

// 创建HTTP客户端
var httpClient = new HttpClient();

// 构建弹性管道
var pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions
    {
        ShouldHandle = new PredicateBuilder()
            .Handle<HttpRequestException>()
            .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode),
        MaxRetryAttempts = 3,
        Delay = TimeSpan.FromSeconds(2),
        OnRetry = args =>
        {
            Console.WriteLine($"第 {args.AttemptNumber} 次重试,原因: {args.Outcome.Exception?.Message}");
            return default;
        }
    })
    .AddTimeout(TimeSpan.FromSeconds(30))
    .Build();

// 使用管道执行HTTP请求
try
{
    HttpResponseMessage response = await pipeline.ExecuteAsync(async token =>
    {
        return await httpClient.GetAsync("https://api.example.com/data", token);
    }, CancellationToken.None);

    Console.WriteLine($"请求成功: {response.StatusCode}");
}
catch (Exception ex)
{
    Console.WriteLine($"所有重试都失败了: {ex.Message}");
}

管道执行流程解析

为了更好地理解弹性管道的工作机制,让我们通过流程图来展示其执行过程:

mermaid

策略配置选项详解

下表总结了常用的重试策略配置选项:

配置选项描述默认值示例
MaxRetryAttempts最大重试次数3MaxRetryAttempts = 5
Delay重试之间的延迟时间2秒Delay = TimeSpan.FromSeconds(1)
BackoffType退避算法类型指数退避BackoffType = DelayBackoffType.Linear
UseJitter是否使用抖动falseUseJitter = true
ShouldHandle处理条件判断所有异常自定义异常处理

最佳实践建议

  1. 合理设置重试次数:通常3-5次重试足够,过多重试可能加重系统负担
  2. 使用指数退避:避免重试风暴,给系统恢复的时间
  3. 添加超时控制:防止长时间等待,确保及时失败
  4. 记录重试事件:通过OnRetry回调记录重试信息,便于监控和调试
  5. 区分异常类型:只对可重试的异常进行重试,如网络异常而非业务逻辑错误

通过本节的介绍,你已经掌握了构建基本弹性管道的核心概念。Polly的强大之处在于其灵活的策略组合和丰富的配置选项,让你能够根据具体需求构建出最适合的弹性解决方案。

依赖注入集成与最佳实践

在现代.NET应用开发中,依赖注入(Dependency Injection)已成为构建可测试、可维护应用程序的核心模式。Polly v8提供了与Microsoft.Extensions.DependencyInjection的无缝集成,让开发者能够以声明式的方式配置和管理弹性策略。本节将深入探讨Polly的DI集成机制、最佳实践以及高级用法。

基础集成配置

要启用Polly的DI支持,首先需要安装Polly.Extensions包:

dotnet add package Polly.Extensions

基础配置示例如下:

var services = new ServiceCollection();

// 添加日志支持
services.AddLogging(builder => builder.AddConsole());

// 注册命名的弹性管道
services.AddResiliencePipeline("http-pipeline", builder =>
{
    builder
        .AddRetry(new RetryStrategyOptions
        {
            MaxRetryAttempts = 3,
            Delay = TimeSpan.FromSeconds(1),
            BackoffType = DelayBackoffType.Exponential
        })
        .AddTimeout(TimeSpan.FromSeconds(10));
});

// 注册泛型弹性管道(针对特定结果类型)
services.AddResiliencePipeline<string, HttpResponseMessage>("api-pipeline", builder =>
{
    builder
        .AddRetry(new RetryStrategyOptions<HttpResponseMessage>
        {
            MaxRetryAttempts = 2,
            ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                .Handle<HttpRequestException>()
                .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError)
        })
        .AddCircuitBreaker(new CircuitBreakerStrategyOptions<HttpResponseMessage>
        {
            FailureRatio = 0.5,
            SamplingDuration = TimeSpan.FromSeconds(30),
            MinimumThroughput = 8,
            BreakDuration = TimeSpan.FromSeconds(60)
        });
});

var serviceProvider = services.BuildServiceProvider();

服务解析与使用

注册完成后,可以通过多种方式解析和使用弹性管道:

// 方式1:通过PipelineProvider解析
var pipelineProvider = serviceProvider.GetRequiredService<ResiliencePipelineProvider<string>>();
var pipeline = pipelineProvider.GetPipeline("http-pipeline");

// 方式2:使用键控服务(.NET 8+)
public class ApiService
{
    private readonly ResiliencePipeline _pipeline;
    
    public ApiService(
        [FromKeyedServices("http-pipeline")] ResiliencePipeline pipeline)
    {
        _pipeline = pipeline;
    }
    
    public async Task<string> GetDataAsync()
    {
        return await _pipeline.ExecuteAsync(async token =>
        {
            // HTTP调用逻辑
            using var client = new HttpClient();
            return await client.GetStringAsync("https://api.example.com/data", token);
        });
    }
}

高级配置模式

动态配置重载

Polly支持配置的动态重载,当配置发生变化时自动更新管道:

services.Configure<RetryStrategyOptions>("retry-options", configuration.GetSection("Retry"));
services.Configure<TimeoutStrategyOptions>("timeout-options", configuration.GetSection("Timeout"));

services.AddResiliencePipeline("dynamic-pipeline", (builder, context) =>
{
    // 启用配置重载
    context.EnableReloads<RetryStrategyOptions>("retry-options");
    context.EnableReloads<TimeoutStrategyOptions>("timeout-options");
    
    // 获取配置选项
    var retryOptions = context.GetOptions<RetryStrategyOptions>("retry-options");
    var timeoutOptions = context.GetOptions<TimeoutStrategyOptions>("timeout-options");
    
    builder
        .AddRetry(retryOptions)
        .AddTimeout(timeoutOptions);
});
资源管理与清理

对于需要资源管理的场景,可以使用资源清理机制:

services.AddResiliencePipeline("resource-pipeline", (builder, context) =>
{
    var rateLimiter = new ConcurrencyLimiter(new ConcurrencyLimiterOptions
    {
        PermitLimit = 100,
        QueueLimit = 50
    });
    
    builder.AddRateLimiter(rateLimiter);
    
    // 注册资源清理回调
    context.OnPipelineDisposed(() => rateLimiter.Dispose());
});

复杂键类型支持

Polly支持使用复杂类型作为管道键,适用于需要多维度标识的场景:

public record struct PipelineKey(string ServiceName, string Region, string Environment)
{
    public override string ToString() => $"{ServiceName}-{Region}-{Environment}";
}

// 自定义比较器
public class PipelineKeyComparer : IEqualityComparer<PipelineKey>
{
    public bool Equals(PipelineKey x, PipelineKey y) => 
        x.ServiceName == y.ServiceName && 
        x.Region == y.Region && 
        x.Environment == y.Environment;
    
    public int GetHashCode(PipelineKey obj) => 
        HashCode.Combine(obj.ServiceName, obj.Region, obj.Environment);
}

// 注册时指定比较器
services.AddResiliencePipelineRegistry<PipelineKey>(options =>
{
    options.BuilderComparer = new PipelineKeyComparer();
});

// 注册具体管道
services.AddResiliencePipeline(
    new PipelineKey("order-service", "west-us", "production"), 
    builder => builder.AddRetry(new RetryStrategyOptions()));

最佳实践指南

1. 管道命名规范

mermaid

2. 配置管理策略
配置方式适用场景优点缺点
硬编码配置简单场景、测试环境简单直接缺乏灵活性
配置文件大多数生产环境可动态更新需要重启生效
动态重载高可用性要求零停机更新配置复杂度高
3. 性能优化建议
// 避免在管道构建时进行昂贵操作
services.AddResiliencePipeline("optimized-pipeline", (builder, context) =>
{
    // ✅ 好的做法:延迟加载昂贵资源
    var expensiveResource = context.ServiceProvider.GetRequiredService<IExpensiveService>();
    
    builder.AddStrategy(context =>
    {
        // 在实际执行时使用资源
        return new MyCustomStrategy(expensiveResource);
    });
});
4. 监控与诊断

集成应用洞察和日志记录:

services.AddResiliencePipeline("monitored-pipeline", (builder, context) =>
{
    var logger = context.ServiceProvider.GetRequiredService<ILogger<Program>>();
    
    builder
        .AddRetry(new RetryStrategyOptions
        {
            OnRetry = args =>
            {
                logger.LogWarning("Retry attempt {Attempt} for operation {Operation}", 
                    args.AttemptNumber, args.OperationKey);
                return default;
            }
        })
        .AddTimeout(TimeSpan.FromSeconds(30));
});

典型应用场景

微服务通信管道
// 注册微服务专用的弹性管道
services.AddResiliencePipeline<string, HttpResponseMessage>("microservice-pipeline", builder =>
{
    builder
        .AddRetry(new RetryStrategyOptions<HttpResponseMessage>
        {
            MaxRetryAttempts = 3,
            Delay = TimeSpan.FromMilliseconds(200),
            BackoffType = DelayBackoffType.Exponential,
            ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                .Handle<HttpRequestException>()
                .HandleResult(response => (int)response.StatusCode >= 500)
        })
        .AddCircuitBreaker(new CircuitBreakerStrategyOptions<HttpResponseMessage>
        {
            FailureRatio = 0.3,
            SamplingDuration = TimeSpan.FromMinutes(2),
            MinimumThroughput = 10,
            BreakDuration = TimeSpan.FromSeconds(30),
            ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                .HandleResult(response => (int)response.StatusCode >= 500)
        })
        .AddTimeout(TimeSpan.FromSeconds(15));
});

// 在HTTP客户端工厂中使用
services.AddHttpClient<IMyService, MyService>()
    .AddResilienceHandler("microservice-pipeline");
数据库访问管道
services.AddResiliencePipeline("database-pipeline", builder =>
{
    builder
        .AddRetry(new RetryStrategyOptions
        {
            MaxRetryAttempts = 5,
            Delay = TimeSpan.FromSeconds(1),
            ShouldHandle = new PredicateBuilder()
                .Handle<SqlException>(ex => ex.Number == 4060) // 数据库不可用
                .Handle<TimeoutException>()
        })
        .AddCircuitBreaker(new CircuitBreakerStrategyOptions
        {
            FailureRatio = 0.5,
            SamplingDuration = TimeSpan.FromMinutes(5),
            MinimumThroughput = 20,
            BreakDuration = TimeSpan.FromSeconds(60)
        });
});

// 在仓储模式中使用
public class ProductRepository : IProductRepository
{
    private readonly ResiliencePipeline _pipeline;
    private readonly IDbConnection _connection;
    
    public ProductRepository(
        [FromKeyedServices("database-pipeline")] ResiliencePipeline pipeline,
        IDbConnection connection)
    {
        _pipeline = pipeline;
        _connection = connection;
    }
    
    public async Task<Product> GetProductAsync(int id)
    {
        return await _pipeline.ExecuteAsync(async token =>
        {
            return await _connection.QueryFirstOrDefaultAsync<Product>(
                "SELECT * FROM Products WHERE Id = @Id", 
                new { Id = id });
        });
    }
}

通过合理的依赖注入集成,Polly能够与现代.NET应用架构完美融合,提供强大而灵活的弹性处理能力。遵循上述最佳实践,可以构建出既健壮又易于维护的分布式系统。

总结

Polly作为.NET生态系统中领先的弹性容错解决方案,通过其强大的策略组合能力、优秀的性能表现和与现代.NET架构的无缝集成,为构建可靠、健壮的分布式系统提供了坚实基础。从基础的重试和超时策略到复杂的断路器模式,从简单的同步调用到高性能的异步管道,Polly v8版本的架构改进让开发者能够以声明式的方式构建各种弹性场景。通过依赖注入的深度集成和丰富的最佳实践指南,Polly不仅提升了系统的可用性和可靠性,还显著简化了复杂容错逻辑的实现。无论是微服务架构、云原生应用还是高并发系统,Polly都是.NET开发者不可或缺的弹性工具库。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值