nameko笔记2-启动自定义配置文件格式

前言

  1. nameko 默认的启动文件格式yaml 或者直接命令行输入某些参数
  2. 是否可以 自定义nameko的配置格式呢,比如利用json格式 或者改为直接读取consul 的远程配置

nameko 的配置文件加载过程

从nameko源码 可以看到 加载yaml 大概如下(比如执行 nameko run --config foo.yaml service ):

  1. 启动参数传到命令Run 然后调用 nameko.cli.run.main 方法
  2. 利用yaml依赖 解析 foo.xml文件生成 config(一个字典类型)配置
  3. 调用 nameko.cli.run.run 继续初始化 ( add_service 然后 start)

ServiceRunner 中用到config

  1. init 方法 获取 container_cls 时候是从 config 读取 ‘SERVICE_CONTAINER_CLS’
  2. add_service 中根据 container_cls 创建 container 对象需要传入config

因为 ServiceRunner 默认是不支持 自定义的,所以 目前还是必须存在 一个 yaml配置文件.

nameko 添加 consul 远程配置

为什么需要这个呢?

  1. 可以把微服务的配置统一到 一个地方管理,这样如果改配置 不用更新线上所有配置?这样为什么 不用redis 或者 mongo 存储配置呢? 其实也是可以的?我这里举出使用consul 的两点好处
    1. consul 提供了 kv 的后台管理
    2. 通过我们的实现 我们可以实现 程序内部准实时感知配置变化,本地访问配置更高效

基于consul kv 实现nameko 插件

方案一

上面 已经分析过 配置 在 serviceRunner 会传入 container 实例,而且我们从上面 知道 servicecontainer 是可以 重写的,通过在foo.yaml 中定义 SERVICE_CONTAINER_CLS 我们用自己实现的 servicecontainer 替换 默认的 servicecontainer,这样后面的拓展都可以 用到最新 的 consul kv 配置.

  1. 我们 可以自定义实现 servicecontainer 在初始化时候 直接 把远程 consul kv 配置 和本地的 config 合并
  2. 远程 consul kv k可以用 servicename 命名 比如(k=lyconf/ranklkist) v 存储json 文本

但是我不建议这么做,因为 consul kv 一般是 动态配置更新的,但是 foo.yaml 一般是重启才会更新的,这样合并配置 到 servicecontainer.config 反而会让人混淆 的感觉. 我更推荐 讲consul kv 和 foo.yaml 分开配置,下面按照方案二说明.

方案二

我们写一个拓展, 将远程 consul kv 映射伟本地 的lyconf 并且实现动态更新.

  1. 当我们需要动态更新的配置时候 可以把配置 放在lyconf,当然一些静态的也可使用(还是有一个好处的,就是配置统一,更新配置,可以不更新线上代码)
  2. 按照第一点讨论,我建议 可以 一个微服务 可以在 consul kv 配置多个文件,如果文件最后一层 以 static_ 则 不动态更新,需要重启 服务才会更新本地服务配置,否则会动态更新本地服务配置.

最终我们的设计 是这样的:
假设我们有个微服务叫做 mshello

  1. consul kv 远程 key 名称 是 root dir 是 /lyconf/mshello/ ,我们 可以在该目录下创建多个配置文件(不再识别文件夹,没必要搞这么复杂)
  2. nameko 编写consul kv 拓展,从而 可以读取 /lyconf/mshello/ 下的文件配置, 我们 为了处理起来简单,改成 只读取 该目录下的,指定文件名,auto 和 static. static 文件 不做动态更新,只在服务启动时候,加载 一次;auto 实现本地动态更新.

源码初版实现如下:

# -*- coding:utf-8 -*-
import json

import consul
from eventlet import Event
from nameko.extensions import DependencyProvider, SharedExtension

from consul_lib.consul_lib import LyConf
from ly_nameko.constants import LYCONF, LYCONF_LOOP_INTERVAL, LYCONSUL_KEY


class NamekoConsul(SharedExtension):

    def setup(self):
        consul_conf = self.container.config.get(LYCONSUL_KEY, {})
        if not consul_conf:
            consul_conf.update({'host': '127.0.0.1', 'port': 8500})
        self.consul_client = consul.Consul(**consul_conf)


class ConsulConf(object):
    '''
    读取远程 consul kv 的配置
    '''

    def __init__(self, consul_key, consul_client):
        self.consul_client = consul_client
        self._conf = {}
        self.consul_key = consul_key
        self.consul_key_index = None

    def __getitem__(self, item):
        return self._conf.get(item)

    def reload_conf(self):
        curr_index = self.consul_key_index
        index, val = self.consul_client.kv.get(self.consul_key, curr_index)
        print('val', val)
        if curr_index != index:
            self.consul_key_index = index
            if val:
                body = val.get('Value')
                if body is not None:
                    conf = json.loads(body)
                    self._conf = conf
                    return True

    def get_conf(self):
        self._conf.copy()


class LyConfExtension(SharedExtension):
    nameko_consul = NamekoConsul()

    auto = 'auto'
    static = 'static'
    config_names = [auto, static]

    def setup(self):
        self.key_prefix = '/LYCONF/%s/' % self.container.service_name
        self.my_conf = self.container.config.get(LYCONF, {})
        self.loop_interval = self.my_conf.get(LYCONF_LOOP_INTERVAL)

        self.cache = {}
        self.should_stop = Event()

        self.auto_config = None
        self.static_config = None

    def start(self):
        self.consul_client = self.nameko_consul.consul_client
        self.init_conf()
        self.container.spawn_managed_thread(self.run)

    def init_conf(self):
        _cache = {}
        if self.auto_config is None:
            k = self.key_prefix + LyConf.auto
            c = ConsulConf(k, self.consul_client)
            if c.reload_conf():
                _cache.update(c.get_conf())

            self.auto_config = c
        if self.static_config is None:
            k = self.key_prefix + LyConf.static
            c = ConsulConf(k, self.consul_client)
            if c.reload_conf():
                _cache.update(c.get_conf())
            self.static_config = c
        if _cache:
            self.cache = _cache

    def _sync_conf(self):
        if self.auto_config.reload_conf():
            _cache = {}
            _cache.update(self.auto_config.get_conf())
            _cache.update(self.static.get_conf())
            if _cache:
                self.cache = _cache

    def run(self):
        while True:
            # sleep for `sleep_time`, unless `should_stop` fires, in which
            # case we leave the while loop and stop entirely
            with Timeout(self.loop_interval, exception=False):
                self.should_stop.wait()
                break
            self._sync_conf()

    def stop(self):
        self.should_stop.send()

    def get_conf(self):
        return self.cache.copy()


class LyConf(DependencyProvider):
    lyConfExtension = LyConfExtension()
    
    def get_dependency(self, worker_ctx):
        return self.container.config.copy().update(
            self.lyConfExtension.get_conf())

源码中的问题:

  1. LyConfExtension 依赖于 NamekoConsul NamekoConsul初始化 放在 setup 而 LyConfExtension 初始化 在start .还没找到更好的初始化顺序
  2. 代码第一版没有考虑并发问题 ,貌似 好像不用考虑这个问题
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值