Copilot Chat
链式代理测速实现方案分析
You said: 查看代码并仔细思考,这个项目测速的技术原理是什么?是纯原生还是操控另一个测速工具?
20 references 20个参考文献
- speed.go:18-41subs-check/check/platformMore reference options
- speed.go:61-67subs-check/check/platformMore reference options
- speed.go:43-60subs-check/check/platformMore reference options
- README.md:62-79subs-checkMore reference options
- worker.js:101-126subs-check/doc/cloudflareMore reference options
- README.md:50-60subs-checkMore reference options
- check.go:46-184subs-check/checkMore reference options
- README.md:253-268subs-checkMore reference options
- check.go:117-130subs-check/checkMore reference options
- README.md:33-136subs-checkMore reference options
- README.md:17-31subs-checkMore reference options
- speed.go:1-68subs-check/check/platformMore reference options
- README.md:36-49subs-checkMore reference options
- check.go:347-380subs-check/checkMore reference options
- README.md:7-16subs-checkMore reference options
- app.go:190-208subs-check/appMore reference options
- worker.js:128-145subs-check/doc/cloudflareMore reference options
- memory.go:63-75subs-check/app/monitorMore reference options
- README.md:281-294subs-checkMore reference options
- memory.go:17-45subs-check/app/monitorMore reference options
Copilot said: 通过分析代码,我可以告诉你这个项目的测速技术原理: ## 技术原理总结 **这是纯原生的测速实现,没有调用外部测速工具。** ### 核心测速逻辑 项目在 `check/platform/speed.go` 中实现了自己的测速功能: ```go func CheckSpeed(httpClient *http.Client,
通过分析代码,我可以告诉你这个项目的测速技术原理:
技术原理总结
这是纯原生的测速实现,没有调用外部测速工具。
核心测速逻辑
项目在 check/platform/speed.go 中实现了自己的测速功能:
func CheckSpeed(httpClient *http.Client, bucket *ratelimit.Bucket) (int, int64, error) {
// 创建测速专用客户端
speedClient := &http.Client{
Timeout: time.Duration(config.GlobalConfig.DownloadTimeout) * time.Second,
Transport: httpClient.Transport, // 复用原有的传输层(代理配置)
}
// 发起HTTP GET请求到测速URL
req, err := http.NewRequest("GET", config.GlobalConfig.SpeedTestUrl, nil)
// 记录开始时间
startTime := time.Now()
// 限速下载和大小限制
bucketReader := ratelimit.Reader(resp.Body, bucket)
limitedReader := io.LimitReader(bucketReader, limitSize)
totalBytes, err = io.Copy(io.Discard, limitedReader) // 下载数据但丢弃
// 计算速度
duration := time.Since(startTime).Milliseconds()
speed := int(float64(totalBytes) / 1024 * 1000 / float64(duration)) // KB/s
return speed, totalBytes, nil
}
测速原理
- 纯HTTP下载测试:通过HTTP GET请求下载指定大小的数据
- 时间计算:记录下载开始和结束时间
- 速度计算:
速度(KB/s) = 下载字节数 / 1024 * 1000 / 耗时(毫秒) - 流量控制:使用
ratelimit库控制总体下载速度 - 数据丢弃:下载的数据通过
io.Discard直接丢弃,不占用内存
测速数据源
项目提供了灵活的测速地址配置:
- 默认使用配置文件中的
speed-test-url - 推荐自建测速服务:项目提供了 Cloudflare Worker 脚本 (
doc/cloudflare/worker.js),可以代理 Cloudflare 的测速服务:JavaScriptWrapCopy codeconst speedTestUrl = `https://speed.cloudflare.com/__down?bytes=${bytes}`;
集成在整体检测流程中
根据架构图,测速是节点检测流程的重要环节:
订阅链接 → 节点去重 → 测活 → 测速 → 流媒体测试 → 生成结果
只有通过测活检测的节点才会进行测速,测速达标的节点才会进入流媒体检测环节。
总结:这是一个完全自主实现的测速方案,没有依赖 Speedtest.net 等第三方测速工具,而是通过原生的 HTTP 下载测试来测量代理节点的速度。
You said: 如果我希望增加链式代理测速,帮我思考下实现流程和技术方案会是怎么样的,先不用写代码。技术难点和解决方案
Copilot said: 基于对当前项目代码的理解,让我分析一下增加链式代理测速的实现流程和技术方案: ## 链式代理测速实现方案 ### 1. 技术架构设计 **链式代理的本质**:将多个代理节点串联,数据流经过 `客户端 → 代理1
基于对当前项目代码的理解,让我分析一下增加链式代理测速的实现流程和技术方案:
链式代理测速实现方案
1. 技术架构设计
链式代理的本质:将多个代理节点串联,数据流经过 客户端 → 代理1 → 代理2 → 目标服务器 的路径。
2. 实现流程设计
配置解析 → 链式连接建立 → 测速执行 → 结果分析 → 性能评估
2.1 配置层面
- 扩展现有配置文件,支持链式代理组合定义
- 支持预定义的链式组合和动态组合
- 配置链式代理的优先级和测试策略
2.2 连接建立
- 基于现有的
http.Client架构,扩展Transport层 - 实现代理链的递归连接:每个代理作为下一个代理的"客户端"
- 处理不同协议混合:HTTP → SOCKS5 → VMess 等
2.3 测速执行
- 复用现有的
CheckSpeed函数框架 - 传入链式代理的复合
http.Client - 保持相同的测速逻辑:下载 → 计时 → 计算速度
3. 核心技术难点与解决方案
难点1: 代理链连接建立复杂性
问题:不同协议间的嵌套连接建立 解决方案:
- 借鉴 mihomo 的代理链实现机制
- 创建递归的
Dialer结构:每层代理包装下一层 - 实现统一的连接接口,屏蔽协议差异
难点2: 错误定位和诊断
问题:链式代理中任一节点失败,难以定位故障点 解决方案:
- 实现分段检测:逐层验证连接可用性
- 添加详细的错误上下文:记录失败发生在哪一层
- 提供链路诊断工具:ping 每一跳的延迟
难点3: 超时和性能管理
问题:链式代理的总体超时需要合理分配 解决方案:
- 动态超时计算:根据链长度调整总超时时间
- 分层超时设置:每层代理设置合理的超时阈值
- 性能降级策略:超时后自动尝试更短的链路
难点4: 资源消耗和并发控制
问题:链式代理建立连接的资源开销大 解决方案:
- 连接池复用:缓存已建立的链式连接
- 限制并发数:避免过多链式连接同时建立
- 智能调度:优先测试高成功率的链式组合
4. 具体技术实现思路
4.1 扩展现有架构
// 扩展现有的 proxy 结构
type ProxyChain struct {
Proxies []map[string]any // 代理链节点列表
Name string // 链名称
Client *http.Client // 复合客户端
}
// 扩展 CheckSpeed 函数
func CheckChainSpeed(chain *ProxyChain, bucket *ratelimit.Bucket) (int, int64, error)
4.2 Transport 层实现 4.2 传输层实现
- 创建
ChainTransport实现http.RoundTripper接口 - 递归构建代理链:
Transport1(Transport2(Transport3(...))) - 处理认证信息在链中的传递
4.3 配置文件扩展
# 新增链式代理配置段
proxy-chains:
- name: "chain-1"
proxies:
- type: "http"
server: "proxy1.example.com"
port: 8080
- type: "socks5"
server: "proxy2.example.com"
port: 1080
chain-test:
enabled: true
max-chain-length: 3
timeout-multiplier: 1.5
5. 性能优化考虑
5.1 智能链组合
- 根据历史测速结果,智能推荐高性能链组合
- 避免测试明显会失败的链组合(如两个相同地区的节点)
5.2 缓存机制
- 缓存链式连接的建立结果
- 缓存代理节点的可用性状态,避免重复检测
5.3 并行优化
- 对不同链组合并行测速
- 但控制单个链内的串行连接建立
6. 用户体验设计
6.1 结果展示
- 显示完整链路信息:
节点A → 节点B → 目标 - 展示每跳的延迟和整体链路性能
- 提供链路性价比分析(性能 vs 复杂度)
6.2 配置简化
- 提供链式代理模板
- 支持一键生成常用链组合
- 智能推荐最优链长度
7. 集成现有系统
基于现有的检测流程,链式代理测速可以作为:
- 独立检测模式:专门的链式代理测试
- 扩展检测模式:在常规测速后,对优质节点进行链式组合测试
- 对比检测模式:同时测试单节点和链式性能,提供对比数据
这样的设计既能充分利用现有架构的稳定性,又能为用户提供更强大的代理测试能力。
You said: 详细展开说说http.Transport 扩展的部分,先不要写代码
Copilot said: ## http.Transport 扩展详细方案 ### 1. Transport 在 Go
http.Transport 扩展详细方案
1. Transport 在 Go HTTP 中的作用机制
1.1 现有架构理解
在当前 subs-check 项目中:
http.Client -> http.Transport -> 单个代理 -> 目标服务器
http.Transport 实现了 http.RoundTripper 接口,负责:
- 建立底层网络连接
- 处理代理配置(通过
Proxy字段) - 管理连接池和复用
- 处理 TLS 握手
1.2 链式代理的挑战
链式代理需要实现:
http.Client -> ChainTransport -> 代理1 -> 代理2 -> 代理3 -> 目标服务器
每一跳都需要完整的握手和协议协商过程。
2. ChainTransport 架构设计
2.1 核心设计理念
分层抽象:每个代理层都是一个独立的 Transport,但它们的目标不是直接到达最终服务器,而是到达下一个代理。
递归构建:从最内层(最后一个代理)开始,逐层包装:
BaseTransport -> Proxy3Transport -> Proxy2Transport -> Proxy1Transport
2.2 接口设计思路
type ChainTransport struct {
// 代理链配置
proxies []ProxyConfig
// 每一层的 Transport 实例
transports []http.RoundTripper
// 连接缓存和管理
connPool *ConnectionPool
// 错误追踪
errorTracker *ChainErrorTracker
}
3. 连接建立的分层机制
3.1 第一层:基础 Transport
- 直连到第一个代理服务器
- 处理最基础的网络连接
- 管理 TCP 层面的连接池
3.2 第二层:协议适配 Transport
- 包装第一层,添加代理协议支持
- 处理 SOCKS5/HTTP CONNECT 握手
- 将对"目标地址"的请求重定向到"下一个代理地址"
3.3 第三层:链式管理 Transport
- 协调多个代理层的工作
- 处理链式认证信息传递
- 统一错误处理和重试逻辑
4. 具体技术实现思路
4.1 连接建立流程
阶段1:逆向构建连接链
- 从最后一个代理开始构建 Transport
- 每个代理的 "Dial" 函数指向前一个代理
- 第一个代理的 "Dial" 函数指向真实网络
阶段2:正向建立连接
- 客户端发起请求到第一个代理
- 第一个代理建立到第二个代理的连接
- 依此类推,直到建立完整链路
阶段3:数据传输
- HTTP 请求沿着建立好的链路传输
- 每一跳处理自己的协议封装/解封装
- 响应数据原路返回
4.2 协议适配策略
HTTP 代理适配:
- 使用 CONNECT 方法建立隧道
- 后续数据透明传输
- 处理 407 代理认证
SOCKS5 代理适配:
- 完成 SOCKS5 握手协议
- 处理用户名/密码认证
- 建立数据传输通道
混合协议处理:
- 每层独立处理自己的协议
- 通过统一的
net.Conn接口传递连接 - 协议无关的错误处理机制
5. 连接池和资源管理
5.1 分层连接池设计
问题:传统连接池是针对单一目标的,链式代理有多个中间节点。
解决方案:
- 链路连接池:以整条链路为单位缓存连接
- 节点连接池:每个代理节点维护独立的连接池
- 智能复用:相同前缀的链路可以复用部分连接
5.2 资源清理机制
层次化清理:
- 链路级清理:整条链路失效时的清理
- 节点级清理:单个代理失效时的部分清理
- 连接级清理:底层网络连接的精确管理
超时管理:
- 建立超时:链路建立的总体时间限制
- 空闲超时:链路空闲时的保持时间
- 读写超时:数据传输过程中的超时
6. 错误处理和诊断
6.1 错误分类和定位
网络层错误:
- 无法连接到代理服务器
- 网络超时或连接重置
- DNS 解析失败
协议层错误:
- 代理认证失败
- 协议握手失败
- 不支持的协议特性
应用层错误:
- HTTP 状态码错误
- 响应格式错误
- 业务逻辑错误
6.2 智能错误恢复
分段重试:
- 从失败节点开始重新建立连接
- 避免重新建立整个链路
- 保留已成功的部分连接
降级策略:
- 自动移除问题节点
- 尝试更短的链路配置
- 回退到单节点模式
7. 性能优化考虑
7.1 连接建立优化
并行握手:
- 在保证协议正确性的前提下,尽可能并行化
- 预建立连接:根据历史使用模式提前建立连接
- 连接预热:定期保活重要的链路连接
智能路由:
- 基于历史性能数据选择最优链路
- 动态调整链路配置
- 负载均衡机制
7.2 数据传输优化
缓冲管理:
- 适应链式传输的缓冲区大小
- 避免过多的中间缓冲
- 流式处理大文件传输
压缩和编码:
- 在适当的层次进行数据压缩
- 避免重复的编码/解码操作
- 保持数据完整性
8. 与现有系统的集成点
8.1 配置系统集成
- 扩展现有的代理配置解析
- 保持向后兼容性
- 支持动态配置更新
8.2 监控系统集成
- 扩展现有的进度显示机制
- 添加链路状态监控
- 集成到现有的日志系统
8.3 结果系统集成
- 扩展 Result 结构体
- 保持现有的结果格式兼容
- 添加链式代理特有的指标
9. 测试和验证策略
9.1 单元测试
- 每个 Transport 层的独立测试
- Mock 不同协议的代理服务器
- 异常情况的覆盖测试
9.2 集成测试
- 真实代理环境的端到端测试
- 不同协议组合的兼容性测试
- 性能基准测试
这种分层的 Transport 扩展方案既保持了 Go HTTP 库的设计哲学,又为链式代理提供了强大而灵活的实现基础。
You said: 有一些问题: Subs-Check, [2025/7/23 8:41] 你描述一下怎么实现 Subs-Check, [2025/7/23 8:41] 我能想到的就是你在那个节点上搭一个http服务 Subs-Check, [2025/7/23 11:23] 听不懂,你可以在描述一下嘛 Subs-Check,
Copilot said: ## 链式代理测速技术方案详解 我理解你的疑问,让我用更直观的方式解释这个方案。**重点是:无需额外部署,纯客户端实现。** ### 1. 核心思路图解 ``` 传统单节点测速: [subs-check客户端] -----> [代理节点] ----->
链式代理测速技术方案详解
我理解你的疑问,让我用更直观的方式解释这个方案。重点是:无需额外部署,纯客户端实现。
1. 核心思路图解
传统单节点测速:
[subs-check客户端] -----> [代理节点] -----> [测速服务器]
建立连接 建立连接
链式代理测速:
[subs-check客户端] -----> [代理1] -----> [代理2] -----> [测速服务器]
连接1 连接2 连接3
2. 技术实现的本质
不是在节点上搭建HTTP服务,而是让 subs-check 客户端模拟浏览器的行为,通过编程方式建立多层代理连接。
3. 具体实现流程图
步骤1: 配置解析
┌─────────────────┐
│ 用户配置文件 │
│ proxy-chain: │
│ - 节点A (HTTP) │
│ - 节点B (SOCKS5)│
│ - 节点C (VMess) │
└─────────────────┘
│
▼
步骤2: 连接建立(关键部分)
┌─────────────────────────────────────────────────────────┐
│ subs-check 内部 │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌────────┐ │
│ │TCP连接1 │───▶│TCP连接2 │───▶│TCP连接3 │───▶│HTTP请求│ │
│ │到节点A │ │到节点B │ │到节点C │ │到测速 │ │
│ │ │ │ │ │ │ │服务器 │ │
│ └─────────┘ └─────────┘ └─────────┘ └────────┘ │
│ ▲ ▲ ▲ ▲ │
│ │ │ │ │ │
│ ┌───┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴───┐ │
│ │协议处理│ │协议处理 │ │协议处理 │ │HTTP │ │
│ │HTTP │ │SOCKS5 │ │VMess │ │处理 │ │
│ │CONNECT │ │握手 │ │握手 │ │ │ │
│ └────────┘ └─────────┘ └─────────┘ └────────┘ │
└─────────────────────────────────────────────────────────┘
4. 详细的连接建立过程
4.1 第一步:连接节点A (HTTP代理)
subs-check 发送:
┌─────────────────────────────────────┐
│ CONNECT 节点B的IP:端口 HTTP/1.1 │
│ Host: 节点B的IP:端口 │
│ Proxy-Authorization: Basic xxx │
└─────────────────────────────────────┘
│
▼
节点A 响应:
┌─────────────────────────────────────┐
│ HTTP/1.1 200 Connection established │
└─────────────────────────────────────┘
现在 subs-check 和节点B之间有了一个通过节点A的隧道。
4.2 第二步:通过节点A连接节点B (SOCKS5)
subs-check 通过节点A发送到节点B:
┌─────────────────────────────────────┐
│ SOCKS5 握手包 │
│ 0x05 0x01 0x00 (版本+方法数+无认证) │
└─────────────────────────────────────┘
│
▼
节点B 通过节点A响应:
┌─────────────────────────────────────┐
│ 0x05 0x00 (版本+选择无认证) │
└─────────────────────────────────────┘
然后 subs-check 继续发送:
┌─────────────────────────────────────┐
│ 连接请求到节点C │
│ 0x05 0x01 0x00 0x01 [节点C的IP和端口]│
└─────────────────────────────────────┘
现在有了 subs-check → 节点A → 节点B → 节点C 的连接链路。
4.3 第三步:通过前两个节点连接节点C
这里假设节点C是VMess协议,需要进行VMess握手:
subs-check 通过(节点A→节点B)发送到节点C:
┌─────────────────────────────────────┐
│ VMess 握手数据包 │
│ [加密的认证信息+目标地址] │
└─────────────────────────────────────┘
│
▼
节点C 响应VMess确认包
5. 核心技术难点的解决
5.1 "套娃"连接的实现
问题:如何让一个网络连接"穿过"多个代理?
解决方案:使用Go的 net.Conn 接口进行包装
最终的连接对象:
┌─────────────────────────────────────┐
│ VMessConn │
│ ┌─────────────────────────────┐ │
│ │ SOCKS5Conn │ │
│ │ ┌─────────────────────────┐│ │
│ │ │ HTTPConn ││ │
│ │ │ ┌─────────────────────┐ ││ │
│ │ │ │ RawTCPConn │ ││ │
│ │ │ └─────────────────────┘ ││ │
│ │ └─────────────────────────┘│ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
每一层 Conn 都实现相同的接口:
Read([]byte) (int, error)- 读取数据时自动解包Write([]byte) (int, error)- 写入数据时自动封包Close() error- 关闭时层层向下关闭
5.2 协议识别和处理
subs-check已经支持的协议(基于mihomo):
- HTTP/HTTPS 代理
- SOCKS4/SOCKS5 代理
- Shadowsocks 影子袜子
- VMess 虚拟机
- VLESS 更少
- Trojan 特洛伊木马
- Hysteria 歇斯底里
- 等等...
复用现有能力:
现有代码结构:
config/proxy.go - 解析各种协议配置
adapter/ - 各协议的实现
├── http.go - HTTP代理实现
├── socks.go - SOCKS代理实现
├── vmess.go - VMess实现
└── ...
链式扩展只需要:
chain/
└── transport.go - 组合现有adapter,无需重新实现协议
6. 数据流示意图
测速时的数据流:
1. HTTP请求发起
┌─────────────────┐
│ GET /test HTTP │ ──┐
│ Host: speed.com │ │
└─────────────────┘ │
│
2. 层层封装 │
VMess封装 ──────────────┼──┐
SOCKS5封装 ─────────────┼──┼──┐
HTTP CONNECT ──────────┼──┼──┼──┐
原始TCP ───────────────┼──┼──┼──┼──> 节点A
│ │ │ │
3. 节点间传输 │ │ │
节点A解HTTP,转发────────┼──┼──┼──> 节点B
节点B解SOCKS5,转发─────┼──┼──> 节点C
节点C解VMess,转发──────┼──> 测速服务器
│
4. 响应数据原路返回 │
测速数据 <────────────────┘
7. 实际使用场景示例
场景1:绕过多层网络限制
用户网络环境:
公司网络 → 需要通过公司代理 → 然后通过个人VPN → 最后到测速服务器
配置:
chain-proxies:
- name: "company-to-personal"
proxies:
- {type: http, server: "company-proxy.com", port: 8080}
- {type: vmess, server: "personal-vps.com", port: 443}
场景2:负载均衡测试
测试不同的代理组合性能:
组合1: [香港节点] → [新加坡节点] → 测速
组合2: [日本节点] → [美国节点] → 测速
组合3: [德国节点] → [英国节点] → 测速
找出最快的组合路径
8. 与现有架构的无缝集成
现有测速流程:
check.go 调用 → speed.CheckSpeed() → 创建http.Client → 测速
链式测速流程:
check.go 调用 → speed.CheckChainSpeed() → 创建ChainClient → 测速
↑
内部组装多个代理
但对外接口保持一致
关键优势:
- 零部署:不需要在任何节点上安装额外软件
- 协议无关:支持任意协议组合
- 向下兼容:不影响现有单节点测速功能
- 配置简单:只需要在配置文件中定义链式组合
这就是纯客户端实现的链式代理测速方案,通过软件层面的连接包装和协议处理,实现多跳代理的性能测试。
Workbench
Content preview panel
View files directly within Copilot
Ask to generate a file or app, or use the button below to try it yourself.