moleculerjs / moleculer

:rocket: Progressive microservices framework for Node.js
https://moleculer.services/
MIT License
6.13k stars 580 forks source link

any idea to implement a config center? #377

Closed soluty closed 5 years ago

soluty commented 6 years ago

to implement microservice, config management is a very important thing. i want to implement a config center using moleculer i need to change config online and if require i can let service restart use awesome moleculer we can easily create a config service, but how to let other service to consume. i get some trouble on how to implement a config client. now my way is copy moleculer-runner.js's code to my index.js an add my custom logic to start a broker to get config data and restore it in global object. is there any elegent way ?

edurdias commented 6 years ago

@soluty Your config service could emit events to notify all the respective services. You could use a naming convention such as ${serviceName}.config.changed. That would allow you to "route" notifications more efficiently.

icebob commented 6 years ago

I'm working on a central config service+mixin, which makes possible to configure services (not ServiceBrokers). The service stores configurations in DB as key & value pairs. The mixin downloads the necessary configurations and loads them to this.config. If a config is changed, the service sends events to the concerned services.

I don't say my solution is the most common solution, so just copy the codes and write your own solution.

Service: https://github.com/icebob/kantab/blob/914e9697665668ba52d98b33ba4ab45f83d9b272/backend/services/config.service.js

Mixin: https://github.com/icebob/kantab/blob/914e9697665668ba52d98b33ba4ab45f83d9b272/backend/mixins/config.mixin.js

Usage in other services:

const ConfigLoader = require("../mixins/config.mixin");

module.exports = {
    name: "accounts",
    version: 1,

    mixins: [
        ConfigLoader([
            "site.**",
            "accounts.**"
        ])
    ],

    actions: {
        async register(ctx) {
            // Access via key
            if (!this.config["accounts.signup.enabled"])
                throw new MoleculerClientError("Sign up is not available.", 400, "ERR_SIGNUP_DISABLED");

            // or like an object
            if (!this.configObj.accounts.signup.enabled)
                throw new MoleculerClientError("Sign up is not available.", 400, "ERR_SIGNUP_DISABLED");
        }
    }
}
soluty commented 6 years ago

thank you for your solution. the solution is similar to my first idea. and i found another moleculer-config project also similar, https://github.com/WoLfulus/moleculer-config but it has a drawback. it can not change the broker's config. for example, i maybe change one broker's loglevel from 'info' to 'debug' in my production env to solve problem. my current solution is copy moleculer-runner's code in my index.js and add some custom start logic contains loading config from config service and make an local usecase to load from yml file.

function run () {
  return Promise.resolve()
    .then(loadConfigFromConfigCenter)
    .then(loadEnvFile)
    .then(loadConfigFile)
    .then(mergeOptions)
    .then(startBroker)
    .catch((err) => {
      logger.error(err)
      process.exit(1)
    })
}
function loadConfigFromConfigCenter () {
  if (flags.local) {
    return loadConfigFromLocal()
  }
  if (process.env.IS_CONFIG_CENTER) {
    // 配置中心,自己不走本地配置
    return Promise.resolve()
  }
  if (process.env.IS_CONFIG_FROM_LOCAL) {
    return loadConfigFromLocal()
  }
  let broker = new Moleculer.ServiceBroker({
    nodeID: `ConfigCenterClient-${utils.getNodeID()}`,
    transporter: 'nats',
  })
  return broker
    .start()
    .then(() => {
      return broker
        .call('config.all')
        .then((config) => {
          global.config = config
          broker.stop()
        })
        .catch((err) => {
          console.log(err)
          // 配置服务如果挂了,则可以通过本地来读取
          return loadConfigFromLocal()
        })
    })
    .then((config) => {
      global.config = config
      broker.stop()
    })
}

now in my moleculer.config.js i can use in global.config.{service}.{file}.{key} to get my config

soluty commented 6 years ago

@soluty Your config service could emit events to notify all the respective services. You could use a naming convention such as ${serviceName}.config.changed. That would allow you to "route" notifications more efficiently.

thank you for suggestion, my current event is config.${serviceName}.changed