nestjs中使用携程Apollo配置中心

nest框架官方文档中使用的是本地文件配置,也就是@nestjs/config包。本地配置文件的好处是使用简单,但是对于一些更新较快的项目,难免会增加配置数据,曾经吃过不少配置文件的亏,在发布的时候很容易因为缺少配置文件直接把服务发挂了,或者需要在服务器上修改配置很容易修改错误导致服务发布失败。

集中的配置中心可以解决上面问题,本文以apollo配置中心为例来说明。 在使用的过程中需要注意以下问题:从配置中心获取数据库连接信息,再去连接会连接失败,因为在连接的时候还没有获取到配置信息。 先看代码再解释。

// main.ts
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter, NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import { MyLogger } from './libs/mylog.service';
import { join } from 'path';
const Apollo = require('node-apollo');
const dotenv = require('dotenv');

async function bootstrap() {
    try {
        const root = join(__dirname, '../');
        let envFile = join(root, '.env')
        dotenv.config({ "path": envFile })
        const {
            APOLLO_APPID,
            APOLLO_ENV,
            APOLLO_HOST,
            APOLLO_NAMESPACE,
            APOLLO_PORT,
            APOLLO_TOKEN,
            APOLLO_ClUSTER
        } = process.env;
        let apolloEnv = {
            configServerUrl: `http://${APOLLO_HOST}:${APOLLO_PORT}`,
            appId: `${APOLLO_APPID}`,
            clusterName: `${APOLLO_ClUSTER}`,
            apolloEnv: `${APOLLO_ENV}`,
            token: `${APOLLO_TOKEN}`,
            namespaceName: [`${APOLLO_NAMESPACE}`]
        };
        // 获取到的配置信息
        let zmConf = await Apollo.remoteConfigService(apolloEnv);
        console.log(">>>>>>>main.config", zmConf);
        process.env = Object.assign(process.env, zmConf);
    } catch (err) {
        console.log(`获取环境变量异常:${err}`)
    }

    const app = await NestFactory.create<NestExpressApplication>(AppModule, new ExpressAdapter());
    app.useLogger(app.get(MyLogger));

    await app.listen(3434, () => {
        const logger = new MyLogger('main.ts');
        logger.debug(process.env.NODE_ENV, 'main.ts');
        logger.log('server start on http://localhost:3434');
    });
}
bootstrap();
// app.module.ts
@Module({
    imports: [
        // MongooseModule.forRoot(`mongodb://${process.env.DATABASE_USER}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}/${process.env.DATABASE_DATABASE}`),
        MongooseModule.forRootAsync({
            useFactory: () => ({
                uri: `mongodb://${process.env.DATABASE_USER}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}/${process.env.DATABASE_DATABASE}`
            }),
        }),
        //  Nest can't resolve dependencies of the AppService (?). Please make sure that the argument ArticleService at index [0] is available in the AppModule context.
        // 在article.service中exports:[ArticleService]
        ArticleModule,
        LoggerModule,
        // HttpModule,
    ], // 导入模块所需的导入模块列表
    controllers: [AppController], // 必须创建的一组控制器
    providers: [AppService, MyLogger], // 由 Nest 注入器实例化的提供者,并且可以在整个模块中共享
})
export class AppModule implements NestModule {
    // 中间件模块在此处添加,可以给某一部分增加中间件,如果要全局增加则在main.ts中使用app.use添加
    configure(consumer: MiddlewareConsumer) {
        consumer
            .apply()
            // .with('中间件参数')
            .forRoutes('/*');
    }
}

使用MongooseModule.forRoot连接数据库肯定是不行的,需要改成异步的:

MongooseModule.forRootAsync({
    useFactory: () => ({
        uri: `mongodb://${process.env.DATABASE_USER}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}/${process.env.DATABASE_DATABASE}`
    }),
}),

因为按照nodejs模块导入的顺序,main.ts中先导入AppModule,AppModule中连接数据库,此时main.ts中的Apollo配置还没获取到,所以就不能正常连接了。那么就只能使用动态连接方式咯。