English / 中文

RSSHub-Gateway

GoReleaser Go Version Release License

一个入口,掌控多套 RSSHub 与 Upvote RSS。RSSHub-Gateway 是面向生产的 反向代理与调度层,保持 RSSHub 路由兼容,同时实现可观测与可运维。

为什么团队选它: - 一个稳定入口覆盖多套上游实例 - 前缀路由 + 组内负载均衡 + 健康检查 - 网关鉴权兼容 RSSHub code 机制 - Prometheus + pprof + JSON 结构化日志 - 热加载 + 缓存,适合长期运行

English README

亮点一览

提示:在已部署的实例中访问 /wiki 可阅读完整项目指南。

核心概念

典型场景

架构示意

请求流程很简单:鉴权 → 前缀路由 → 选 upstream → 转发。

flowchart LR
    Client -->|HTTP| Gateway
    Gateway -->|/short/*| Short
    Short -->|301/302| Redirect
    Redirect --> Target
    Short -->|proxy internal| Router
    Short -->|proxy external| External
    Gateway --> Router
    Router --> Group
    Group --> LB
    LB --> RSSHub
    LB --> Upvote
    Gateway -->|/metrics| Prometheus
    Gateway -->|logs| Logger
sequenceDiagram
    participant C as Client
    participant G as Gateway
    participant R as Router
    participant LB as Load Balancer
    participant U as Upstream
    participant E as External

    C->>G: GET /short/fearnation?key=...
    alt short 重定向(外部)
        G->>G: 解析 short + 去除 key/code
        G-->>C: 301 Location: https://fearnation.club/rss/?...
    else short 代理(外部)
        G->>G: 解析 short + 去除 key/code
        G->>E: 代理请求(UA + SNI)
        E-->>G: 响应
        G-->>C: 返回
    else 业务代理
        C->>G: GET /rsshub/path?key=...
        G->>G: 校验 key/code
        G->>R: 前缀选组
        R-->>G: 组名
        G->>LB: 选 upstream
        LB-->>G: upstream
        G->>G: 删除 key/code,注入 upstream code(rsshub)
        G->>U: 代理转发
        U-->>G: 响应
        G-->>C: 返回
    end

60 秒上手

本地构建并用示例配置启动:

# 构建
make build

# 运行
./rsshub-gateway serve -c config.example.yaml

Docker

docker build -t rsshub-gateway:latest .
docker run --rm -p 8080:8080 rsshub-gateway:latest

预构建镜像: - docker pull nerdneils/rsshub-gateway:latest - docker pull ghcr.io/nerdneilsfield/rsshub-gateway:latest

自定义配置文件:

docker run --rm -p 8080:8080 \
  -v "$(pwd)/config.example.yaml:/app/config.yaml:ro" \
  rsshub-gateway:latest \
  /app/rsshub-gateway serve -c /app/config.yaml

部署示例(Docker Compose)

services:
  rsshub-gateway:
    image: ghcr.io/nerdneilsfield/rsshub-gateway:latest
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - ./config.yaml:/app/config.yaml:ro
    command: ["/app/rsshub-gateway", "serve", "-c", "/app/config.yaml"]
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/"]
      interval: 30s
      timeout: 5s
      retries: 3

配置

完整配置见 config.example.yaml

配置说明(折叠) - `routing.default_group` 必须存在于 groups 中。 - allow/deny 使用前缀匹配,deny 优先,前缀需包含 `/`。 - `backend` 只能是 `rsshub` 或 `upvote`(默认 `rsshub`)。 - `strip_prefix` 用于转发前剥离服务前缀(如 `/rsshub`、`/upvote`)。 - `gateway_auth` 需要 `access_key`,且 `accept_key`/`accept_code` 至少开一个。 - `gateway_auth.bypass_paths` 可跳过网关鉴权(精确路径匹配,仍会转发/注入 code)。 - upstream `access_key` 仅 RSSHub 用于 code 注入和健康检查 `?key=`。 - 健康检查使用 `path`、`interval_ms`、`timeout_ms`、`retries`。 - `failover.passive_eject` 要求 `base_eject_ms <= max_eject_ms`。 - 启用 metrics 时需要配置 `metrics.accesskey`。 - 启用 pprof 时需要配置 `pprof.accesskey`。 - 缓存需要配置 provider(`memory` 或 `redis`)、TTL 和大小限制;仅缓存 GET 2xx/3xx。 - 自动轮询重载通过配置 hash 对比(`reload.auto.enabled` + `interval_ms`)。 - 启用 short 时 `short.path` 必须以 `/` 开头,name 唯一,method 必须为 `301`/`302`/`proxy`;外部目标会剥离 `key`/`code`。
完整配置示例 ```yaml server: listen: ":8080" timeout_ms: 8000 gateway_auth: enabled: true access_key: "ILoveRSSHub" accept_key: true accept_code: true bypass_paths: - "/favicon.ico" - "/logo.png" - "/robots.txt" - "/manifest.json" metrics: enabled: true path: "/metrics" accesskey: "PROM_KEY_123" pprof: enabled: false path: "/debug/pprof" accesskey: "PPROF_KEY_123" cache: enabled: false provider: "memory" ttl_ms: 3600000 max_item_bytes: 2097152 max_total_bytes: 52428800 redis: addr: "127.0.0.1:6379" password: "" db: 0 dial_timeout_ms: 1000 read_timeout_ms: 1000 write_timeout_ms: 1000 key_prefix: "rsshub_gateway" reload: auto: enabled: false interval_ms: 30000 short: enabled: true path: "/short" entries: - name: "latepost" target: "/rsshub/latepost/4" method: "301" - name: "reddit-top" target: "https://example.com/rss?platform=reddit" method: "302" routing: default_group: "rsshub-public" failover: retry: enabled: true max_retries: 1 passive_eject: enabled: true fail_threshold: 3 base_eject_ms: 10000 max_eject_ms: 60000 groups: - name: "rsshub-public" backend: "rsshub" strip_prefix: "/rsshub" priority: 10 allow: ["/rsshub/"] deny: [] lb: policy: "wrr" fallback_groups: ["rsshub-backup"] health: active: enabled: true path: "/healthz" interval_ms: 30000 timeout_ms: 10000 retries: 3 upstreams: - url: "http://rsshub-1:1200" weight: 3 access_key: "UP1KEY" - url: "http://rsshub-2:1200" weight: 2 access_key: "UP2KEY" - name: "rsshub-backup" backend: "rsshub" strip_prefix: "/rsshub" priority: 1 allow: ["/rsshub/"] deny: [] lb: policy: "hash" upstreams: - url: "http://rsshub-b1:1200" weight: 1 access_key: "B1KEY" - name: "upvote" backend: "upvote" strip_prefix: "/upvote" priority: 5 allow: ["/upvote/"] deny: [] lb: policy: "wrr" upstreams: - url: "http://upvote-rss:80" weight: 1 ```

鉴权

网关支持两种访问方式:

http://127.0.0.1:8080/rsshub/latepost/4?key=ACCESS_KEY
http://127.0.0.1:8080/rsshub/latepost/4?code=md5(path+ACCESS_KEY)
http://127.0.0.1:8080/upvote/?platform=reddit&key=ACCESS_KEY

迁移说明(code 方式): 如果之前用 ?code= 访问 /latepost/...,需要改成 /rsshub/latepost/..., 并按 md5("/rsshub/latepost/4"+ACCESS_KEY) 计算。key 方式不变。

上游注入规则: - 删除客户端 keycode - 仅 RSSHub 注入 code=md5(path+upstream_access_key)

订阅缩写

short 入口支持 method: 301|302|proxy。 重定向会把原始 query 追加到目标 URL;外部目标会剥离 key/code 以避免泄露。proxy 会直接转发请求(内部目标保留 key/code,外部目标剥离 key/code)。

GET /short/latepost?key=ACCESS_KEY
-> 301 Location: /rsshub/latepost/4?key=ACCESS_KEY

GET /short/reddit-top?sort=top&key=ACCESS_KEY
-> 302 Location: https://example.com/rss?platform=reddit&sort=top

GET /short/cdt?key=ACCESS_KEY
-> proxy https://chinadigitaltimes.net/chinese/feed

路由与负载均衡

健康检查

主动健康检查请求 /healthz。当 RSSHub 设置 ACCESS_KEY 时,健康检查会 自动追加 ?key=<upstream_access_key>

Docker Compose 建议:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:1200/healthz?key=${ACCESS_KEY}"]

指标

访问方式(带 accesskey):

GET /metrics?accesskey=<METRICS_ACCESS_KEY>
指标列表 - rsshub_gateway_requests_total{method,group,route_prefix,status} - rsshub_gateway_request_duration_seconds_bucket{group,route_prefix} - rsshub_gateway_upstream_requests_total{group,upstream,status} - rsshub_gateway_upstream_health{group,upstream} - rsshub_gateway_upstream_eject_total{group,upstream} - rsshub_gateway_retry_total{group} - rsshub_gateway_fallback_total{from,to} - rsshub_gateway_config_reload_total{result}

pprof

访问方式(带 accesskey):

GET /debug/pprof/?accesskey=<PPROF_ACCESS_KEY>

日志

访问日志为 JSON,每请求一条;事件日志记录健康变更、剔除和重载等事件。

访问日志字段建议 - ts - level - req_id - method - path - group - upstream - route_prefix - status - duration_ms - retries - fallback_chain - err_type - err

热加载

kill -HUP <pid>

开发

make test
make cover

发布

goreleaser release --snapshot --clean --skip-publish

License

MIT