前言

Java官方提供了客户端代码,但是对于NodeJS没有提供(有第三方开源包,有的已经不再更新,有的不好使用)。所以使用官方提供的接口进行封装NodeJS+TS的包,方便使用。

实现方案

实现注意点:

实现步骤

步骤一:获取配置

官方提供了两个接口(Apollo其他语言接入指南):

  1. 带缓存读取:读取结果为Json格式数据
  2. 不带缓存读取:读取结果为Apollo平台配置的dotenv格式配置

注意事项

redis.host=redis.com
redis.port=0001
redis.db=1
db.host=dbhost.com
db.port=0001

步骤二:解析配置

将第一步获取到的配置统一解析为Json格式并结构化

将YAML数据转成Json

{
  "redis.host": "redis.com",
  "redis.port": "0001",
  "redis.db": "1",
  "db.host": "dbhost.com",
  "db.port": "0001"
}

将Json数据结构化

{
  redis: {
    host: "redis.com",
    port: "0001",
    db: "1"
  },
  db: {
    host: "dbhost.com",
    port: "0001"
  }
}

使用

配置管理

目前配置项入住Apollo,在项目中有各种配置,大致分为三种和修改形式:

  1. src/common/constants/common.ts中的静态变量配置:注意统一命名规范
  2. src/config/env中的按环境获取的配置:需要已废弃
  3. 部分写在代码中的配置:需要废弃并迁入src/common/constants/common.ts

后续这些配置需要进行整理,对配置进行整理,整理的原则为:

  1. 各个环境不变的配置:全部移动到src/common/constants/common.ts中去,注意命名规范;
  2. 跟环境紧密相关的配置,需要添加到远程,按以下步骤:
  3. Apollo中添加配置并填写完整的备注信息,字段命名规范见下文;
    1. 在项目代码中Config类型中添加该字段并添加与上一步完全相同的备注信息;

配置样例

redis.host=redis.com
redis.port=0001
redis.db=1
db.host=dbhost.com
db.port=0001

配置规范

  1. 只允许定义一层到两层,e.g. keyName = value 或者 scopeName.keyName = value名称使用驼峰命名法
  2. 在项目代码中增加:
    1. ConfigKeyEnum中增加一层配置的keyName或两层配置的scopeName注意必须和Apollo命名保持一致)并在Config中添加该项
    2. 如果是两层的配置,必须在Config接口下添加子层级配置的类型约束,每个属性添加注释和类型(注意必须和Apollo命名保持一致
export interface ConfigKeyEnum {
  /* Redis配置 */
  Redis = 'redis'
  /* 数据库配置 */
  DB = 'db'
}

export interface Config {
  [ConfigKeyEnum.Redis]: RedisConfig,
  [ConfigKeyEnum.DB]: DBConfig
}

export interface RedisConfig {
  /* Redis主机地址 */
  host: string
  port: number
  db: number
}

export interface DBConfig {
  host: string
  port: number
}

配置使用

NodeJS

import { getApolloConfig } from '@shinho-sh/node-apollo';
 
(async function() {
  const apolloConfig = await getApolloConfig({
    apolloConfig: {
      url: process.env.APOLLO_META,
      token: process.env.APOLLO_ACCESSKEY_SECRET,
      appId: APOLLO_APP_ID,
      cluster: 'default',
      namespace: ['application'],
      env: EnvEnum.Dev,
      useCache: true
    }
  });
 
  const app = await NestFactory.create(AppModule);
})();

NestJS

// config.helper.ts
// 将ConfigModule及配置注入到全局
export function registerConfig({ config, configEnum }: RegisterOptions) {
  const baseConfig = ConfigModule.forRoot(config);
  const configProviders = Object.keys(configEnum).map(item => {
    return {
      provide: configEnum[item],
      useFactory(configService: ConfigService) {
        return configService.get(configEnum[item]);
      },
      inject: [ConfigService],
    };
  });
  baseConfig.providers.push(...configProviders);
  baseConfig.exports.push(...configProviders.map(o => o.provide));
  return baseConfig;
}
 
 
export function setupConfig () {
  // ...做一下其他的预处理或处理环境变量
  const config = await setApolloIntoEnv({
    apolloConfig: {
      url: process.env.APOLLO_META,
      token: process.env.APOLLO_ACCESSKEY_SECRET,
      appId: APOLLO_APP_ID,
      cluster: 'default',
      namespace: ['application'],
    },
    coverValue: coverValue,
  });
  return config
}
 
 
// app.module.ts
@Global()
@Module({
  imports: [
    registerConfig({
      config: {
        isGlobal: true,
        load: [setupConfig],
      },
      configEnum: ConfigKeyEnum,
    })
  ],
})
export class AppModule {}
 
 
// some.service.ts
@Injectable()
export class SomeServie {
  constructor (
    /* 注入配置 */
    @Inject(ConfigKeyEnum.DB)
    private readonly dbConfig: Config[ConfigKeyEnum.DB]
  ) {
    /* 使用 */
    this.dbConfig.host
  }
}

覆盖配置

覆盖配置需要配置和远程,相同的配置结构,主要配合调试不同环境使用

// 远程配置
redis.host = redis.com
redis.port = 0001
redis.index = 1
 
 
// 项目代码
import { getApolloConfig } from '@shinho-sh/node-apollo';
 
(async function() {
  const apolloConfig = await getApolloConfig({
    apolloConfig: {
        ...// config
    },
    // 覆盖配置
    coverValue: {
      "redis.host": "<Custom Value>",
      "redis.port": "<Custom Value>",
    }
  });

  const app = await NestFactory.create(AppModule);
})();

综合

以上为目前方案,如有问题可按需求进行调整。