首页 / 文档 / 管道与中间件

管道与中间件

Aneiang.Pa 内置 8 个中间件,组成生产级执行管道。全部默认开启,零配置。

管道执行顺序

请求 → Logging → Metrics → Tracing → Cache → RateLimit → CircuitBreaker → Retry → Timeout → DouYinCookie → Fetch → Parse → 响应

中间件一览

中间件默认行为关闭/调整
LoggingILogger<> 注入不注入即空日志
MetricsMeter Aneiang.PaPa.Configure(c => c.Options.EnableMetrics = false)
TracingActivitySource Aneiang.Pa同上 EnableTracing
CacheMemory 5min.NoCache() 单次跳过
Retry3 次指数退避.NoRetry() 单次跳过
Timeout30s.WithTimeout(TimeSpan)
RateLimit不启用显式开启 + 配置
CircuitBreaker5 次失败熔断 60s自动
DouYinCookie抖音 Cookie 自动获取仅对 DouYin 生效

缓存

默认 5 分钟内存缓存。相同 Source 的重复请求直接返回缓存数据。

// 第一次:实际请求
var data1 = await Pa.Source("WeiBo").GetAsync();
// data1.FromCache == false

// 第二次(5 分钟内):命中缓存
var data2 = await Pa.Source("WeiBo").GetAsync();
// data2.FromCache == true

// 跳过缓存,强制请求
var fresh = await Pa.Source("WeiBo").NoCache().GetAsync();

调整全局缓存时间:

Pa.Configure(c => c.Options.DefaultCacheDuration = TimeSpan.FromMinutes(10));

重试

默认 3 次指数退避:500ms → 2s → 8s,最长约 14 秒。

// 跳过重试
var data = await Pa.Source("WeiBo").NoRetry().GetAsync();

超时

默认 30 秒硬超时。

// 自定义超时
var data = await Pa.Source("WeiBo")
    .WithTimeout(TimeSpan.FromSeconds(10))
    .GetAsync();

熔断器

连续 5 次失败后自动熔断 60 秒,期间所有请求直接返回失败,不再实际发起 HTTP 请求。

熔断时间过后自动进入半开状态,尝试恢复。

限流

默认不启用。需要时显式开启:

Pa.Configure(c => c.Options.EnableRateLimit = true);
Pa.Configure(c => c.Options.RateLimit = new RateLimitOptions
{
    PermitLimit = 10,
    Window = TimeSpan.FromSeconds(1)
});

代理池

支持轮询/随机代理池,带健康跟踪和失败自动禁用:

Pa.Configure(c => c
    .UseProxyPool(new[]
    {
        "http://127.0.0.1:7890",
        "http://127.0.0.1:7891",
        "http://127.0.0.1:7892",
    })
    .ProxyStrategy(ProxyStrategy.RoundRobin));

// 单次代理
var data = await Pa.Source("WeiBo")
    .WithProxy("http://127.0.0.1:7890")
    .GetAsync();

自定义中间件

需要特殊处理(签名、Token、登录态等)时,写一个中间件:

using Aneiang.Pa.Abstractions;

public class MyAuthMiddleware : IScrapeMiddleware
{
    public int Order => 5;
    public string Name => "MyAuth";

    public async Task<ScrapeResult> InvokeAsync(ScrapeContext ctx, ScrapeDelegate next)
    {
        if (ctx.Recipe.Name == "MySite")
            ctx.Variables["token"] = await GetTokenAsync();
        return await next(ctx);
    }

    private async Task<string> GetTokenAsync()
    {
        // 获取 token 的逻辑
        return "my-token";
    }
}

注册中间件:

Pa.Configure(c => c.UseMiddleware(new MyAuthMiddleware()));

Recipe 中用 {token} 占位:

headers:
  Authorization: "Bearer {token}"

IScrapeMiddleware 接口

属性/方法说明
Order执行顺序(数字越小越先执行)
Name中间件名称(用于日志)
InvokeAsync(ctx, next)处理逻辑,调用 next(ctx) 继续管道
ctx.Variables共享变量字典
ctx.Recipe当前 Recipe 信息
ctx.CancellationToken取消令牌