Skip to content

EnvConfig 是整个 OTX 代理插件中负责多环境配置切换的核心模块。它采用单例模式统一管理应用运行环境(Dev/Test/Staging/Prod),通过哨兵文件检测编程式设置两种机制确定当前环境,并将环境信息注入到 HTTP 客户端的主机地址、证书路径和安全凭据等关键配置中,从而实现同一个 DLL 在不同部署场景下无缝切换后端服务端点。

Sources: jidu_envConfig.h, jidu_envConfig.cpp

环境枚举定义

环境类型由 jidu_macro.h 中的 App_Env_t 枚举统一描述。该枚举定义了六个环境等级,其中 Dev、Test、Staging、Prod 构成主要的四级递进流水线,而 ProdTestProdStaging 作为额外的混合环境预备扩展。

枚举常量整数值含义
App_Env_Dev1开发环境
App_Env_Test2测试环境
App_Env_Staging3预发布/ staging 环境
App_Env_Prod4生产环境
App_Env_ProdTest5生产测试混合(预留)
App_Env_ProdStaging6生产 Staging 混合(预留)

Sources: jidu_macro.h

架构总览:从哨兵文件到端点注入

EnvConfig 在多环境架构中扮演"配置源"角色——它本身只负责检测和存储当前环境标识,真正的环境差异化配置(如服务器 URL、证书路径)由 JiduClient 在其 loadEnvirment() 方法中消费环境标识后完成。下图展示了从环境检测到端点注入的完整数据流:

mermaid
flowchart TD
    subgraph 环境检测层
        SF[哨兵文件检测<br/>useProd.txt / useStaging.txt / ...]
        PROG[编程式设置<br/>setEnvironment]
    end

    subgraph EnvConfig 单例
        EC[EnvConfig::getInstance]
        ENV[environment: App_Env_t]
        GEP[getEnvPath / getEnvironment]
        CK[checkEnvironment]
    end

    subgraph 环境消费者
        LE[loadEnvirment<br/>设置 eolHost / pkiHost / cfgHost / diagHost / certDir]
        LP[loadParam<br/>按环境码索引 certPwd / certName]
        API[getAppEnvironment API<br/>对外暴露环境信息]
    end

    subgraph 配置文件
        WEB[WebApi.json<br/>运行时覆盖 host]
        CFG[Diagnosis.Entry.exe.config<br/>staging_2_prod / 集成测试]
    end

    SF -->|构造时| ENV
    PROG --> ENV
    ENV --> GEP
    CK -->|重新扫描| ENV
    GEP --> LE
    GEP --> LP
    GEP --> API
    LE --> WEB
    LE --> CFG

Sources: jidu_envConfig.cpp, jidu_client.cpp, jidu_client.cpp

哨兵文件检测机制

环境检测的核心逻辑位于 构造函数checkEnvironment() 方法中。两者实现完全相同的检测链:按优先级从高到低依次检查工作目录下是否存在特定的哨兵文件(sentinel file),一旦命中即设置对应环境并终止检查。

哨兵文件的存在性通过 POSIX stat() 调用判定——只需文件存在且为常规文件类型即可,文件内容不参与判断。检测优先级如下表:

优先级哨兵文件对应环境适用工厂
1(最高)useProd.txtProd通用
2useDmaProd.txtProdDMA
3useStaging.txtStaging通用
4usePma.txtStagingPMA
5useDma.txtStagingDMA
6useTest.txtTest通用
7useDmaTest.txtTestDMA
8useDev.txtDev通用
9useDmaDev.txtDevDMA
兜底(无哨兵文件)Prod

关键设计决策:当没有任何哨兵文件存在时,系统默认回退到生产环境(Prod)。这意味着生产环境是"最安全"的缺省选择,避免了因配置疏忽导致测试流量进入生产系统的风险反转。

mermaid
flowchart LR
    A[启动] --> B{useProd.txt?}
    B -->|是| P[Prod]
    B -->|否| C{useDmaProd.txt?}
    C -->|是| P
    C -->|否| D{useStaging.txt?}
    D -->|是| S[Staging]
    D -->|否| E{usePma.txt?}
    E -->|是| S
    E -->|否| F{useDma.txt?}
    F -->|是| S
    F -->|否| G{useTest.txt?}
    G -->|是| T[Test]
    G -->|否| H{useDmaTest.txt?}
    H -->|是| T
    H -->|否| I{useDev.txt?}
    I -->|是| DV[Dev]
    I -->|否| J{useDmaDev.txt?}
    J -->|是| DV
    J -->|否| P

Sources: jidu_envConfig.cpp, jidu_envConfig.cpp

环境输出接口

EnvConfig 对外暴露四个核心访问方法,分别服务于不同的消费场景:

方法返回值用途
getEnvPath()"Dev" / "Test" / "Staging" / "Prod"对外 API(getAppEnvironment)的字符串映射源
getEnvironment()int(1~4)用于 loadParam() 中按环境码索引证书密码和证书文件名
getAppPath()DLL 所在目录的绝对路径所有配置文件和证书的路径前缀
checkEnvironment()bool(true=环境未变化)在每次 callServer 前检查哨兵文件是否变更,实现热切换

getAppPath() 的内部实现值得一提:它通过 GetModuleHandleEx + GetModuleFileName 获取当前 DLL 的加载路径(而非进程工作目录 getcwd),从而确保无论宿主进程从何处启动,配置文件查找始终基于 DLL 的实际部署位置。该路径结果被缓存为静态变量,避免重复调用 Windows API。

Sources: jidu_envConfig.cpp, jidu_envConfig.cpp, jidu_envConfig.cpp

环境热切换:checkEnvironmentinit() 的协作

运行时环境热切换通过 JiduClient::init()EnvConfig::checkEnvironment() 链实现。每次 callServer() 调用都会触发 init(),后者调用 checkEnvironment() 重新扫描哨兵文件。如果哨兵文件集合发生变化(例如管理员在两次请求之间放置了 useStaging.txt),checkEnvironment() 返回 false,随后 init() 会清空已缓存的证书凭据并重新调用 loadEnvirment()loadParam(),实现无缝环境切换

mermaid
sequenceDiagram
    participant CS as callServer
    participant INIT as JiduClient::init()
    participant EC as EnvConfig
    participant FS as 文件系统

    CS->>INIT: 每次请求前调用
    INIT->>EC: checkEnvironment()
    EC->>FS: stat(哨兵文件列表)
    FS-->>EC: 文件存在性结果
    EC-->>INIT: true(未变) / false(已变)
    alt 环境已变化
        INIT->>INIT: keyPem.clear()
        INIT->>INIT: loadEnvirment()
        INIT->>INIT: loadParam()
    end
    INIT-->>CS: 继续执行请求

Sources: jidu_client.cpp, jidu_envConfig.cpp

环境→端点映射:loadEnvirment() 的多维路由

JiduClient::loadEnvirment() 是环境配置的实际消费者,它将环境标识转化为四个服务端点和证书目录。映射逻辑引入了一个额外的维度——工厂标识(Factory)DataCenter::getInstance()->factory 决定使用 DMA、PMA 还是售后/工程的端点集合。每个工厂×环境组合对应独立的域名和证书路径。

PMA 工厂 为例的端点映射(DMA 和售后/工程场景遵循相同模式但使用不同域名):

环境eolHostpkiHostdiagHost
Deveol-server-service-dev.jiduauto.compki-thirdparty.jidudev.comdiagnostic-service.jidudev.com
Testeol-server-service.jidupmatest.compma-pki-thirdparty.jidupmatest.comdiagnostic-service.jidutest.com
Stagingeol-server-service-staging.jiduauto.compma-pki-thirdparty.jidupmastaging.comdiagnostic-service.jidustaging.com
Prodeol-server-service.jidupma.compma-pki-thirdparty.jidupma.comdiagnostic-service.jiduprod.com

证书目录则遵循 {appPath}pkiCertificate/{Factory}/{EnvPath}/ 的路径模板,例如 PMA 工厂在 Staging 环境下的证书路径为 {appPath}pkiCertificate/PMA/Staging/

Sources: jidu_client.cpp

WebApi.json:外部化端点覆盖

loadEnvirment() 的硬编码映射完成之后,系统还会尝试加载 {appPath}WebApi.json 文件。如果该文件存在且包含与当前工厂和环境匹配的 DomainName 配置节,则会覆盖此前设置的 eolHostpkiHostcfgHostdiagHost 不受影响)。这为运维人员提供了一种无需重新编译 DLL 即可调整服务端点的机制。

WebApi.json 的结构层次为 DomainName → {Factory: DMA/PMA} → {Env: Dev/Test/Staging/Prod} → {EOL, PKI, Config},与代码内的分支逻辑一一对应:

json
{
  "DomainName": {
    "PMA": {
      "Staging": {
        "EOL": "https://custom-eol.example.com/eol-server",
        "PKI": "https://custom-pki.example.com",
        "Config": "https://custom-cfg.example.com/api"
      }
    }
  }
}

Sources: jidu_client.cpp

Staging→Prod 桥接:staging_2_prod() 配置开关

在 Staging 环境下,loadEnvirment() 还引入了一个特殊逻辑:调用 staging_2_prod() 检查 Diagnosis.Entry.exe.config 配置文件中的 eolEnvironment 键值。当该值为 "true" 时,Staging 环境的 eolHost 将被替换为生产环境的 EOL 服务器地址。这种设计允许在 Staging 环境下使用生产级的 EOL 数据进行集成验证,同时保持其他服务(PKI、Config、Diag)仍指向 Staging。

该机制同样作用于 WebApi.json 覆盖阶段——当 staging_2_prod() 返回 true 时,WebApi.json 中 PKI 和 Config 仍使用 Staging 的值(从 Staging 节点取出覆盖到 Prod 节点),仅 EOL 使用真正的 Prod 地址。

Sources: jidu_client.cpp, jidu_client.cpp

集成测试 Mock 模式

IsIntegrationTesting()getMockDataFormat() 两个函数同样依赖 EnvConfig 提供的 getAppPath() 来定位 Diagnosis.Entry.exe.config 配置文件。在集成测试模式下,EOL 请求不会真正发出 HTTP 调用,而是从本地 EolServerMockData 目录读取预置的 JSON 或 XML Mock 数据文件。Mock 数据文件的命名规则为 URL 路径中 /open/diagnosis/ 之后的部分经 /_ 替换后拼接 .json/.xml 后缀。

mermaid
flowchart TD
    CS[callServer] --> IT{IsIntegrationTesting?}
    IT -->|否| REAL[真实 HTTP 请求]
    IT -->|是| FMT{getMockDataFormat}
    FMT -->|json| MJ[读取 .json Mock 文件]
    FMT -->|xml| MX[读取 .xml Mock 文件<br/>经 mockDataXml2Json 转换]
    MJ --> RESP[返回 response]
    MX --> RESP

Sources: jidu_client.cpp, jidu_client.cpp, jidu_client.cpp

对外 API:getAppEnvironmentsetFactory

OTX 运行时通过两个 C 语言导出的 API 与环境系统交互:

  • getAppEnvironment():返回简化的环境分类——1 表示 Prod、2 表示 Staging、0 表示其他。该函数通过 EnvConfig::getEnvPath() 获取字符串后做二次映射,为上层 OTX 脚本提供足够的环境感知能力。

  • setFactory(const char* factory):设置工厂标识("DMA""PMA" 等),并立即触发 JiduClient::loadEnvirment()loadParam() 的重新加载。这是工厂切换的入口——改变工厂后,所有后续 callServer 调用都将使用新工厂对应的端点集合和证书。

setFactory 的调用时机通常在诊断会话建立之初,由 OTX 脚本根据诊断设备的部署场景传入。这也解释了为什么 loadEnvirment() 中的端点映射需要按工厂维度分叉——同一套 DLL 需要在 DMA 产线、PMA 产线和售后门店等不同物理场景中使用完全不同的后端服务集群。

Sources: jidu.cpp, jidu.cpp, jidu.h

环境码在证书选择中的应用

loadParam() 中使用环境码的另一个关键场景是加密证书密码的索引pver 文件中的 certPwdcertName 均以环境码为键存储——对于 DMA 工厂,环境码会额外 +6 偏移(例如 Dev=1→7),从而在同一个 JSON 结构中隔离 DMA 和 PMA 的凭据:

环境码映射(loadParam):
  PMA: Dev=1, Test=2, Staging=3, Prod=4
  DMA: Dev=7, Test=8, Staging=9, Prod=10

这确保即使在同一台设备上切换工厂,也不会错误地使用另一工厂的证书凭据。

Sources: jidu_client.cpp

阅读建议

理解 EnvConfig 后,建议继续阅读以下关联文档以建立完整的配置管理认知: