egg采坑日记之异步加载配置文件

更新:
在下面的RFC issues中有个老哥提到的在configWillLoad中同步执行外部脚本的方法阻塞进程,并使用进程返回值传递配置,这个是比较好的解决方式

背景

egg RFC

由于egg生命周期中,加载配置文件在configWillLoad之前,并且configWillLoad只支持同步方法,导致在实践中希望从配置中心拉取配置,或者希望异步方式加载配置文件的方式无法实现。 官方在issues中提出的在agent进程中获取配置,并写入文件,在worker进程中加载该文件的方式。 经过实践测试发现,好像并不能实现需求,因为,按照官方文档,egg源码,实践发现,agent的生命周期与app的生命周期几乎一样(此处指加载方式与生命周期函数),所以配置文件的加载依然在configWillLoad之前,并且如上所述,此函数不支持异步方法。在现实应用中陷入了僵局,秉持着继续试用egg框架不更换框架的前提下,我使用了一种曲线救国的方式,放弃egg-script的启动方式。

思路

使用index.js启动脚本,在启动脚本中异步获取配置文件,并写入文件中,启动后加载此文件,index.js启动脚本优先于所有agent,或者worker进程,所以可以适用于在agent进程中就需要的一些异步配置的获取。

> 注:项目中使用了etcd存储配置 1、首先改变egg启动方式,放弃egg-script的启动,使用[egg-cluster](https://github.com/eggjs/egg-cluster)自定义启动文件index.js

2、项目目录下的index.js

//index.js

'use strict';

const startCluster = require('egg-cluster').startCluster;
const Etcd3 = require('etcd3');
const fs = require('fs');
const moment = require('moment');

//node env 与 egg env 映射
const env = {
  dev: 'local',
  local: 'local',
  development: 'local',
  test: 'testing',
  testing: 'testing',
  prod: 'production',
  production: 'production',
  product: 'production'
}
//egg-cluster启动配置
const serverOptions = {
  baseDir: __dirname,
};
//etcd配置
const etcdConfig = {};

//正式服启动环境变量必须,防止连接到测试服
if (!process.env.NODE_ENV && !process.env.EGG_SERVER_ENV) {
  const errMessage = moment().format('YYYY-MM-DD HH:mm:ss') + 'There is no NODE_ENV or EGG_SERVER_ENV'
  fs.writeFileSync('./logs/startError.log', errMessage, { flag: 'w' })
  process.exit(1);
} else if (process.env.NODE_ENV) {
  //node环境映射到egg环境
  process.env.EGG_SERVER_ENV = env[process.env.NODE_ENV]
}

if (process.env.EGG_SERVER_ENV === 'local') {
  serverOptions.workers = 1;
  etcdConfig.hosts = [
    "http://127.0.0.1:32771",
    "http://127.0.0.1:32769",
    "http://127.0.0.1:32773"
  ]
} else if (process.env.EGG_SERVER_ENV === 'testing') {
  serverOptions.workers = 1;
  etcdConfig.hosts = [
    "http://127.0.0.1:32771",
    "http://127.0.0.1:32769",
    "http://127.0.0.1:32773"
  ]
} else if (process.env.EGG_SERVER_ENV === 'production') {
  serverOptions.workers = 2;
  etcdConfig.hosts = [
    "127.0.0.1:32771",
    "127.0.0.1:32769",
    "127.0.0.1:32773"
  ]
} else {
  const errMessage = moment().format('YYYY-MM-DD HH:mm:ss') + 'There is no NODE_ENV or EGG_SERVER_ENV';
    fs.writeFileSync('./logs/startError.log', errMessage, { flag: 'w' })
  process.exit(1)
}


(async () => {
  const etcd = new Etcd3.Etcd3(etcdConfig);
  try {
    const res = await etcd.getAll();
    //获取到etcd的配置后,写入文件
    fs.writeFileSync("./config/etcd.json", JSON.stringify(res), { flag: 'w' })
  } catch (error) {
    const errMessage = moment().format('YYYY-MM-DD HH:mm:ss')
      + 'There is an error occured for getting config from Etcd =>'
      + error.toString()
    fs.writeFileSync('./logs/startError.log', errMessage, { flag: 'w' })
    process.exit(1)
  }

  startCluster(serverOptions);
})();

3、写入config目录下的配置文件etcd.json

//etcd.json
{
    "MYSQL_DBNAME": "test",
    "MYSQL_PORT": "3306",
    "MYSQL_READ_HOST": "127.0.0.1",
    "MYSQL_READ_PASSWORD": "test",
    "MYSQL_READ_USERNAME": "test"
}

4、在config中使用配置文件

//config.env.js

'use strict';

const etcd = require('./etcd.json');

const MYSQL_DBNAME = etcd.MYSQL_DBNAME
const MYSQL_HOST_READ = etcd.MYSQL_READ_HOST
const MYSQL_PORT_READ = etcd.KANKANTAOBAO_MYSQL_PORT
const MYSQL_USERNAME_READ = etcd.MYSQL_READ_USERNAME
const MYSQL_PASSWORD_READ = etcd.MYSQL_READ_PASSWORD

module.exports = appInfo => {
  return {
    sequelize: {
      datasources: [
        {
          dialect: 'mysql',
          delegate: 'test',
          baseDir: 'model/test',
          database: MYSQL_DBNAME,
          logQueryParameters: true,
          replication: {
            read: [
              {
                database: MYSQL_DBNAME,

                host: MYSQL_HOST_READ,
                port: MYSQL_PORT_READ,
                username: MYSQL_USERNAME_READ,
                password: MYSQL_PASSWORD_READ,
              }
            ],
            write: {

            }
          },
          freezeTableName: false,
          underscored: true,
          timezone: '+08:00',
        }
      ]
    },
  }
};

5、关于此种方式的思考
在我们项目的实际部署使用中,测试服使用pm2统一维护node项目,在正式服中,用docker部署项目,所以几乎是用不到egg-script的维护功能,所以,此种方式可以在当前是比较好的解决方式,如果大家有什么更好的方式,或者我的错误,欢迎指点。

如果喜欢我的文章,请点赞支持一下,感谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值