toxic-johann / toxic-johann.github.io

my blog
6 stars 0 forks source link

对 config 进行 lint 的一些想法 #38

Open toxic-johann opened 6 years ago

toxic-johann commented 6 years ago

这还是一个颇为常见的问题,一个正常的 node 服务,可能只是作一些很轻的转发和数据收拢。这造成了它需要依靠大量的微服务。

为了方便维护,大家一般会将所有微服务的相关配置写在一份配置中,在不同的环境下替换该份配置。但是随着项目越来越大,依赖的服务越来越多,会发现这个配置越来越大。几百行不在话下。

而且这种配置,一般是多人使用多人维护,在不同环境下多人操作的。

而且配置是否正确还只有运行时才能校验。于是这让这份配置很容易出问题。包括但不限于:

  1. 被人改错
  2. 被人删了关键性的东西
  3. 怕出错,大家都不敢删,越来越冗余
  4. 每次建新环境都要重新配,还不知道有没有配对。

所以今天被人问到一个问题,可不可以建一个插件进行校验。

需求

场景很简单,一般情况下,开发自己在本地环境会有一份开发的配置,这份配置一般比较准确。

所以我们需要做的事有两个:

  1. 校验其他配置和开发配置格式是否一致
  2. 校验开发配置是否合法

思考

我们分两步思考:

1. 校验其他配置和开发配置格式是否一致

其实这个校验非常简单。因为我们已经有两份配置,我们遍历配置的格式判断即可。

不过有的时候会需要较松的校验,所以这里我们增加一些容错配置。

例如:

  1. 容许缺失
  2. 容许增多
  3. 容许差异

这些配置应该以字段进行保存,但是为了维护的方便性,也不应该太多。

总体来说,实现这个需求难度不大。

2. 校验该开发配置(或最终配置)是否合法

其实我们这个需求的终极目的就是希望我们不要在运行时才发现配置错误。

刚刚第一个需求已经校验了类型。而至于具体内容是否填对这个非常难分析(除非勤劳地写单元测试)。

那么我们想想除了类型合法我们还关心什么?

这个配置有没有被使用!!!

而且我们希望一启动就检测,或者静态检测

其实启动(静态)检测的原理挺简单的,就是使用前先声明。

那么我们当然不希望我使用个配置还要这么麻烦。。所以太多声明挺烦人的。。

不过我们至少有一样东西可以做到。因为配置在运行时不变化,所以我们在启动时立即取出,继而通过监听是否有使用即可。

这在以前 thinkjs 还挺好做的,因为以前 thinkjs 使用 config 是直接 require 的

const { a, b } = require('config');

我们只需要给 config 对象简单加 object.definePropery 即可。

但是 egg 这块估计要大家改改写码习惯:

'use strict';

const Controller = require('egg').Controller;

const { test } = require('../../run/agent_config.json').config;

console.log(test, 'test');

class HomeController extends Controller {
  async index() {
    console.log(test);
    this.ctx.body = 'hi, egg';
  }
}

module.exports = HomeController;

这个估计比较蛋疼。但我比较偏向于这种取 config 的方式,因为这样展示了他常量的本质。

而且用没用可以由 eslint 保证。

不过这里还有一个问题,怎么将文件中的 json 取出来再绑定呢。又或者怎么做到一开始就可以挂钩读取这个对象呢。这个需要具体调研。现在观看可能性不是特别大。

toxic-johann commented 6 years ago

单元测试可以完美防止配置错误。

但是单元测试不能完成以下情况:

  1. 某些公司场景是上线后再读取配置,所以没有单元测试这关
  2. 单元测试不能解决冗余配置问题
toxic-johann commented 6 years ago
  1. /run/agent_config.json 属于滞后写出的
  2. 应用启动才拼接 config,因此准确的 config 只有启动后才能获取。 这种做法强迫我们写在 function 里,对于用 class 的不是很方便。
toxic-johann commented 6 years ago

既然 egg 的 config 输出是滞后的。那么我们就将这个 config 从 egg 的 config 逻辑抽取出来。

使用时遵循静态的使用方式

const { test } = require('path/to/config');

config 中使用 object.defineProperty 进行监听

使用 getter 记录这个值是否有被使用

在 egg 启动后检测结果

因为是静态声明,所以在 egg 启动前我们就有获取相关的值。在 egg.ready 的回调函数中判断是否有使用过,没有使用过则 crash 即可。

开发环境下的监听重启

这个需要和 egg 这边挂钩一下。编写时仔细探讨。

toxic-johann commented 6 years ago

第一步:解决配置对测试黑盒的问题。