Skip to content

架构概览

简介

本文从整体架构层面解释 Xray-core 的组成、核心数据流和各模块之间的关系,以及 REALITY 在架构中的位置。

本文回答

  • Xray-core 的整体架构是怎样的
  • 各核心模块的职责和交互关系
  • 一条代理连接从进入到离开的完整流程
  • REALITY 在整个流程中扮演什么角色

本文在项目中的位置

本文处于文档集的"整体结构层",读完本文后应能理解项目的主要模块和数据流方向。建议先阅读 index.md 了解核心概念。

适合读者

所有想深入理解 Xray-core 设计的读者。

前置知识

Xray-core 的核心概念(Inbound、Outbound、路由、回落),基本的代理原理。

文章理解难度

中等。需要理解网络代理的基本概念和一定的架构思维。

重点

  • Feature 注册和依赖注入系统
  • 入站/出站处理器的生命周期
  • Dispatcher 的核心路由逻辑
  • REALITY 在 TLS 层的插入位置

难点

  • 功能依赖解析的反射机制
  • 并发连接处理模型
  • 回落和路由的组合逻辑

上级文档和相关文档


整体架构

Xray-core 采用基于功能注册的插件式架构,所有核心能力通过 Feature 接口统一管理。

核心模块职责

core.Instance(核心实例)

源码位置:core/xray.go

Instance 是整个 Xray 运行时的根对象,负责:

  • 解析配置并创建所有功能模块
  • 管理功能模块的生命周期(注册、启动、关闭)
  • 提供功能依赖解析(通过反射实现 IoC)
  • 维护一个全局 context.Context

每个功能模块在注册时声明它依赖哪些其他功能模块。当所有依赖就绪后,回调函数被调用完成装配。

源码位置:core/xray.go 行 82-91 行数范围:82-91

go
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

go
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/

分发器是入站和出站之间的核心桥接。每次入站连接解析出目标地址后,调用分发器获取匹配的出站处理器并转发。

分发器的核心调度逻辑:

go
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/

  • 提供流量统计功能
  • 支持按入站/出站/用户维度的上下行流量计数

代理连接生命周期

一条从客户端到目标的完整代理连接经过以下阶段:

关键设计点:

  1. 连接级别的并发:每个入站连接在独立的 goroutine 中处理
  2. 流控与数据中继:连接建立后,入站和出站之间直接双向拷贝数据,不经过中间缓冲
  3. 链接(Link)抽象:在入站和出站之间传递的是一个 Link 对象,其中包含 ReaderWriter 接口

REALITY 在架构中的位置

REALITY 在传输安全层工作,位于 TCP 连接之上、代理协议之下:

TCP 连接

[REALITY TLS Layer]  ← 流量伪装层

[XTLS Vision Flow]   ← 流控层(可选)

[VLESS Protocol]     ← 代理协议层

[Router/Dispatcher]  ← 路由层

服务端流程

当一个新的 TCP 连接到达服务端 443 端口时:

  1. TLS ClientHello 到达:REALITY 改造的 TLS 服务端读取 ClientHello
  2. Session ID 检测:检查 ClientHello 中的 Session ID 字段
    • 如果 Session ID 符合 REALITY 格式(包含版本号、时间戳、加密的认证密钥)→ 进入 REALITY 认证流程
    • 如果 Session ID 不符合 REALITY 格式 → 将连接作为普通 TLS 连接转发到 target 配置的网站(回落)
  3. 认证验证:使用服务端私钥解密 Session ID 中的认证信息,验证 ShortId 和 HMAC 签名
  4. 临时证书签发:如果认证通过,生成临时 Ed25519 密钥对,使用认证密钥的 HMAC 作为证书签名,完成 TLS 1.3 握手
  5. 交棒给代理协议:TLS 握手完成后,解密后的数据流交给 VLESS Inbound 进一步处理

客户端流程

  1. TLS 指纹模拟:使用 uTLS 库构造一个看起来像 Chrome/Firefox 的 ClientHello
  2. Session ID 嵌入:在 ClientHello 的 Session ID 中嵌入认证信息
  3. 临时证书验证:收到服务端证书后,使用 HMAC(认证密钥, 公钥) 验证证书签名
    • 如果签名匹配 → 连接正常可用
    • 如果签名不匹配但证书链有效 → 可能遇到 MITM 或被重定向,进入"爬虫模式"
    • 如果证书无效 → TLS alert,断开连接

关键设计决策

为什么使用反射实现依赖注入

源码位置:core/xray.goRequireFeatures 方法

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

下一步阅读