配置系统概述
Xray 的配置系统将人类可读的 JSON(或 YAML/TOML)转换为核心引擎使用的 Protobuf 消息。该流程有明确的分层:infra/conf/ 负责解析和验证,而 core/ 和 app/ 仅使用 Protobuf 类型。
流水线架构
flowchart LR
A[JSON/YAML/TOML 文件] --> B[Reader 解码器]
B --> C["conf.Config(Go 结构体)"]
C --> D["conf.Config.Build()"]
D --> E["core.Config(Protobuf)"]
E --> F["core.New(config)"]
F --> G[Xray 实例]
subgraph "infra/conf/"
B
C
D
end
subgraph "core/"
E
F
G
end顶层 Config 结构体
文件: infra/conf/xray.go
type Config struct {
Transport map[string]json.RawMessage `json:"transport"` // 已弃用
LogConfig *LogConfig `json:"log"`
RouterConfig *RouterConfig `json:"routing"`
DNSConfig *DNSConfig `json:"dns"`
InboundConfigs []InboundDetourConfig `json:"inbounds"`
OutboundConfigs []OutboundDetourConfig `json:"outbounds"`
Policy *PolicyConfig `json:"policy"`
API *APIConfig `json:"api"`
Metrics *MetricsConfig `json:"metrics"`
Stats *StatsConfig `json:"stats"`
Reverse *ReverseConfig `json:"reverse"`
FakeDNS *FakeDNSConfig `json:"fakeDns"`
Observatory *ObservatoryConfig `json:"observatory"`
BurstObservatory *BurstObservatoryConfig `json:"burstObservatory"`
Version *VersionConfig `json:"version"`
}每个字段对应一个顶层 JSON 键。结构体内嵌的子配置类型知道如何构建对应的 Protobuf 消息。
Buildable 接口
文件: infra/conf/buildable.go
type Buildable interface {
Build() (proto.Message, error)
}每个配置结构体都实现此接口。Build() 方法验证配置并生成等效的 Protobuf 消息。
多格式支持
文件: infra/conf/serial/loader.go
Xray 支持 JSON、YAML 和 TOML 配置。所有格式都通过解码器函数转换为 conf.Config:
var ReaderDecoderByFormat = map[string]readerDecoder{
"json": DecodeJSONConfig,
"yaml": DecodeYAMLConfig,
"toml": DecodeTOMLConfig,
}JSON: 通过去注释读取器(json_reader.Reader)直接解析,并带有语法错误位置追踪。
YAML: 通过 yaml.YAMLToJSON() 转换为 JSON,然后按 JSON 解析。
TOML: 先反序列化为 map[string]interface{},再序列化为 JSON,最后按 JSON 解析。
配置合并
文件: infra/conf/serial/builder.go
支持合并多个配置文件:
func mergeConfigs(files []*core.ConfigSource) (*conf.Config, error) {
cf := &conf.Config{}
for i, file := range files {
c, _ := ReaderDecoderByFormat[file.Format](r)
if i == 0 {
*cf = *c
continue
}
cf.Override(c, file.Name)
}
return cf, nil
}Config 上的 Override() 方法处理合并逻辑:
- 简单字段(log、routing、DNS、policy 等)整体替换
- Inbound/outbound 按标签合并:匹配的标签被更新,新标签被添加
- Outbound 的顺序取决于文件名:文件名包含 "tail" 的追加到末尾,其他的前置
Build 流水线
文件: infra/conf/xray.go -- Config.Build()
Build() 方法构造最终的 core.Config:
func (c *Config) Build() (*core.Config, error) {
PostProcessConfigureFile(c) // lint 和验证
config := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&dispatcher.Config{}), // 始终存在
serial.ToTypedMessage(&proxyman.InboundConfig{}), // 始终存在
serial.ToTypedMessage(&proxyman.OutboundConfig{}),// 始终存在
},
}
// 构建并追加各配置段
if c.API != nil { config.App = append(config.App, serial.ToTypedMessage(apiConf)) }
if c.Stats != nil { config.App = append(config.App, serial.ToTypedMessage(statsConf)) }
// Log 前置(最先初始化)
config.App = append([]*serial.TypedMessage{logConfMsg}, config.App...)
if c.RouterConfig != nil { config.App = append(config.App, serial.ToTypedMessage(routerConfig)) }
if c.DNSConfig != nil { config.App = append(config.App, serial.ToTypedMessage(dnsApp)) }
if c.FakeDNS != nil { config.App = append([]*serial.TypedMessage{...}, config.App...) } // 前置!
// ... observatory、reverse、policy、version
// 构建 inbound 和 outbound
for _, rawInboundConfig := range inbounds {
ic, _ := rawInboundConfig.Build()
config.Inbound = append(config.Inbound, ic)
}
for _, rawOutboundConfig := range outbounds {
oc, _ := rawOutboundConfig.Build()
config.Outbound = append(config.Outbound, oc)
}
}顺序很重要:
- Log 配置始终排在最前(使其他模块在初始化时即可记录日志)
- FakeDNS 前置于 DNS 之前(必须先初始化以满足
RequireFeatures) - Dispatcher、InboundConfig、OutboundConfig 作为基础应用始终存在
Inbound 配置构建
type InboundDetourConfig struct {
Protocol string `json:"protocol"`
PortList *PortList `json:"port"`
ListenOn *Address `json:"listen"`
Settings *json.RawMessage `json:"settings"`
Tag string `json:"tag"`
StreamSetting *StreamConfig `json:"streamSettings"`
SniffingConfig *SniffingConfig `json:"sniffing"`
}Build() 方法:
- 构建
ReceiverConfig(监听地址、端口、流设置、嗅探) - 通过
inboundConfigLoader.LoadWithID()加载协议特定设置 - 调用加载的配置的
Build()获取 Protobuf 代理设置 - 使用
serial.ToTypedMessage()将两者包装在core.InboundHandlerConfig中
Outbound 配置构建
type OutboundDetourConfig struct {
Protocol string `json:"protocol"`
SendThrough *string `json:"sendThrough"`
Tag string `json:"tag"`
Settings *json.RawMessage `json:"settings"`
StreamSetting *StreamConfig `json:"streamSettings"`
ProxySettings *ProxyConfig `json:"proxySettings"`
MuxSettings *MuxConfig `json:"mux"`
TargetStrategy string `json:"targetStrategy"`
}Build() 方法:
- 构建
SenderConfig(出口地址、流设置、mux、代理链) - 处理
targetStrategy(outbound DNS 解析的域名策略) - 通过
outboundConfigLoader.LoadWithID()加载协议特定设置 - 包装在
core.OutboundHandlerConfig中
协议配置加载器
文件: infra/conf/xray.go
两个 JSONConfigLoader 注册表将协议名称映射到配置构造器:
var inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"dokodemo-door": func() interface{} { return new(DokodemoConfig) },
"http": func() interface{} { return new(HTTPServerConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) },
"socks": func() interface{} { return new(SocksServerConfig) },
"vless": func() interface{} { return new(VLessInboundConfig) },
"vmess": func() interface{} { return new(VMessInboundConfig) },
"trojan": func() interface{} { return new(TrojanServerConfig) },
// ...
}, "protocol", "settings")
var outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"freedom": func() interface{} { return new(FreedomConfig) },
"blackhole": func() interface{} { return new(BlackholeConfig) },
"vless": func() interface{} { return new(VLessOutboundConfig) },
"vmess": func() interface{} { return new(VMessOutboundConfig) },
"dns": func() interface{} { return new(DNSOutboundConfig) },
// ...
}, "protocol", "settings")LoadWithID() 将原始 JSON settings 反序列化到对应的配置结构体中。
关键源文件
| 文件 | 用途 |
|---|---|
infra/conf/xray.go | 顶层 Config、Build()、协议加载器 |
infra/conf/buildable.go | Buildable 接口 |
infra/conf/serial/builder.go | BuildConfig()、mergeConfigs() |
infra/conf/serial/loader.go | JSON/YAML/TOML 解码器 |
infra/conf/dns.go | DNSConfig、NameServerConfig |
infra/conf/router.go | RouterConfig |
infra/conf/api.go | APIConfig |
infra/conf/common.go | 共享类型:Address、PortList、StringList |
core/config.go | core.Config Protobuf、格式注册 |
实现要点
JSON 读取器(
infra/conf/json/reader.go)在解析前去除 C 风格注释(//和/* */),支持 "JSONC" 文件。PostProcessConfigureFile()在Build()之前调用,处理预处理和验证,这也是 lint 检查运行的位置。Transport字段(全局传输设置)已弃用,使用时会返回错误,引导用户使用按 inbound/outbound 配置的streamSettings。协议别名:
"block"映射到BlackholeConfig,"direct"映射到FreedomConfig,"tunnel"映射到DokodemoConfig,"mixed"映射到SocksServerConfig。Override()方法处理多文件配置合并。当 outbound 配置具有相同标签时,后面的文件优先。来自非 "tail" 文件的新 outbound 会被前置以维持路由优先级。Config上的BuildMPHCache()方法为域名匹配器生成最小完美哈希缓存文件,可在启动时加载以跳过解析 geosite 数据文件。