架构概览
简介
本文从整体架构层面解释 Xray-core 的组成、核心数据流和各模块之间的关系,以及 REALITY 在架构中的位置。
本文回答
- Xray-core 的整体架构是怎样的
- 各核心模块的职责和交互关系
- 一条代理连接从进入到离开的完整流程
- REALITY 在整个流程中扮演什么角色
本文在项目中的位置
本文处于文档集的"整体结构层",读完本文后应能理解项目的主要模块和数据流方向。建议先阅读 index.md 了解核心概念。
适合读者
所有想深入理解 Xray-core 设计的读者。
前置知识
Xray-core 的核心概念(Inbound、Outbound、路由、回落),基本的代理原理。
文章理解难度
中等。需要理解网络代理的基本概念和一定的架构思维。
重点
- Feature 注册和依赖注入系统
- 入站/出站处理器的生命周期
- Dispatcher 的核心路由逻辑
- REALITY 在 TLS 层的插入位置
难点
- 功能依赖解析的反射机制
- 并发连接处理模型
- 回落和路由的组合逻辑
上级文档和相关文档
- index.md:项目概述和核心概念
- source/00-reading-map.md:功能注册机制源码分析
- source/00-reading-map.md:分发器源码分析
整体架构
Xray-core 采用基于功能注册的插件式架构,所有核心能力通过 Feature 接口统一管理。
核心模块职责
core.Instance(核心实例)
源码位置:core/xray.go
Instance 是整个 Xray 运行时的根对象,负责:
- 解析配置并创建所有功能模块
- 管理功能模块的生命周期(注册、启动、关闭)
- 提供功能依赖解析(通过反射实现 IoC)
- 维护一个全局
context.Context
每个功能模块在注册时声明它依赖哪些其他功能模块。当所有依赖就绪后,回调函数被调用完成装配。
源码位置:core/xray.go 行 82-91 行数范围:82-91
type Instance struct {
statusLock sync.Mutex
features []features.Feature // 所有已注册的功能模块
pendingResolutions []resolution // 等待依赖就绪的回调
pendingOptionalResolutions []resolution // 可选的等待回调
running bool
resolveLock sync.Mutex
ctx context.Context
}功能模块的依赖声明通过 RequireFeatures 实现。每个回调函数的参数类型即为其依赖的功能类型——Xray 使用反射自动装配。
源码位置:core/xray.go 行 279-316
func (s *Instance) RequireFeatures(callback interface{}, optional bool) error {
callbackType := reflect.TypeOf(callback)
// 提取回调函数每个参数的类型 → 即为依赖的功能类型
var featureTypes []reflect.Type
for i := 0; i < callbackType.NumIn(); i++ {
featureTypes = append(featureTypes, reflect.PtrTo(callbackType.In(i)))
}
r := resolution{deps: featureTypes, callback: callback}
// 如果所有依赖已就绪 → 立即调用回调
// 如果尚有依赖未注册 → 加入待处理队列
if foundAll {
return r.callbackResolution(s.features)
} else {
s.pendingResolutions = append(s.pendingResolutions, r)
}
}Inbound Manager(入站管理器)
源码位置:app/proxyman/inbound/
- 管理所有 Inbound Handler 的生命周期
- 为每个 Inbound 创建监听器(TCP/UDP)
- 在接受到新连接时,创建 worker goroutine 处理连接
- 支持动态添加/移除 Inbound Handler(通过 API)
Outbound Manager(出站管理器)
源码位置:app/proxyman/outbound/
- 管理所有 Outbound Handler 的生命周期
- 提供
GetHandler(tag string)接口,根据标签获取出站处理器 - 支持动态添加/移除 Outbound Handler
Dispatcher(分发器)
源码位置:app/dispatcher/
分发器是入站和出站之间的核心桥接。每次入站连接解析出目标地址后,调用分发器获取匹配的出站处理器并转发。
分发器的核心调度逻辑:
func (d *DefaultDispatcher) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
// 1. 查询路由 → 获取出站标签
obTag, err := d.router.PickRoute(ctx)
// 2. 获取对应的 Outbound Handler
handler := d.ohm.GetHandler(obTag)
// 3. 调用 Outbound Handler 的 Dispatch → 建立到目标的连接
return handler.Dispatch(ctx, dest)
}Router(路由器)
源码位置:app/router/
- 根据配置的路由规则决定流量的出站目标
- 支持基于域名、IP、协议、端口、入站标签、用户等级等条件的路由
- 支持
geosite(域名分类)和geoip(IP 地理位置)数据库 - 支持负载均衡(多出站随机选择)
DNS Client
源码位置:app/dns/
- 提供 DNS 解析服务,支持多种 DNS 服务器
- 支持 DNS over HTTPS (DoH)、DNS over TLS (DoT)、DNS over QUIC (DoQ)
- 支持 DNS 分流(不同域名使用不同 DNS 服务器)
- 支持 DNS 缓存,减少重复查询
Policy Manager(策略管理器)
源码位置:app/policy/
- 管理系统级别的策略(连接超时、缓冲区大小等)
- 策略可按用户等级分别配置
Stats Manager(统计管理器)
源码位置:app/stats/
- 提供流量统计功能
- 支持按入站/出站/用户维度的上下行流量计数
代理连接生命周期
一条从客户端到目标的完整代理连接经过以下阶段:
关键设计点:
- 连接级别的并发:每个入站连接在独立的 goroutine 中处理
- 流控与数据中继:连接建立后,入站和出站之间直接双向拷贝数据,不经过中间缓冲
- 链接(Link)抽象:在入站和出站之间传递的是一个
Link对象,其中包含Reader和Writer接口
REALITY 在架构中的位置
REALITY 在传输安全层工作,位于 TCP 连接之上、代理协议之下:
TCP 连接
↓
[REALITY TLS Layer] ← 流量伪装层
↓
[XTLS Vision Flow] ← 流控层(可选)
↓
[VLESS Protocol] ← 代理协议层
↓
[Router/Dispatcher] ← 路由层服务端流程
当一个新的 TCP 连接到达服务端 443 端口时:
- TLS ClientHello 到达:REALITY 改造的 TLS 服务端读取 ClientHello
- Session ID 检测:检查 ClientHello 中的 Session ID 字段
- 如果 Session ID 符合 REALITY 格式(包含版本号、时间戳、加密的认证密钥)→ 进入 REALITY 认证流程
- 如果 Session ID 不符合 REALITY 格式 → 将连接作为普通 TLS 连接转发到
target配置的网站(回落)
- 认证验证:使用服务端私钥解密 Session ID 中的认证信息,验证 ShortId 和 HMAC 签名
- 临时证书签发:如果认证通过,生成临时 Ed25519 密钥对,使用认证密钥的 HMAC 作为证书签名,完成 TLS 1.3 握手
- 交棒给代理协议:TLS 握手完成后,解密后的数据流交给 VLESS Inbound 进一步处理
客户端流程
- TLS 指纹模拟:使用 uTLS 库构造一个看起来像 Chrome/Firefox 的 ClientHello
- Session ID 嵌入:在 ClientHello 的 Session ID 中嵌入认证信息
- 临时证书验证:收到服务端证书后,使用 HMAC(认证密钥, 公钥) 验证证书签名
- 如果签名匹配 → 连接正常可用
- 如果签名不匹配但证书链有效 → 可能遇到 MITM 或被重定向,进入"爬虫模式"
- 如果证书无效 → TLS alert,断开连接
关键设计决策
为什么使用反射实现依赖注入
源码位置:core/xray.go 的 RequireFeatures 方法
Go 没有原生的 IoC 容器,使用反射可以避免:
- 全局变量和
init()函数带来的隐式初始化依赖 - 手动管理模块间的创建和传递顺序
代价是类型安全被推迟到运行时。
为什么 VLESS 无加密层
VLESS 本身不提供加密,加密由传输安全层(TLS/REALITY)负责。这个设计的原因是:
- 避免双层加密带来的开销
- 允许不同的传输安全层灵活组合
- 在 XTLS 流控中,需要看到内部的 TLS 流量以进行分流处理
回落(Fallback)的设计逻辑
回落不仅是"连接不是代理协议时的处理",还承担了"主动防御"的角色。当审查方进行主动探测时,REALITY 会将探测连接转发到真实网站——探测方收到的响应与访问真实网站完全一致,从而无法区分代理服务器和普通网站。
外部参考资料
资料:Xray-core 代码结构和设计 类型:源码 链接:https://github.com/XTLS/Xray-core 可信度:A
资料:Project X 设计理念 类型:官方文档 链接:https://xtls.github.io 可信度:A
下一步阅读
- 源码导读入口:从源码层面深入
- 分发器模块:理解请求分发机制
- REALITY 协议详解:理解握手流程