前言

利用kubernetes部署应用越来越流行,而运行在kubernetes中的服务需要的各种各样的配置如何才能实现热更新?难道需要在kubernetes中再部署zookeeper或者etcd之类的服务么?本文采用的方案是利用ConfigMap作为服务配置的持久化方案,并利用kubernetes提供的watch能力主动发现ConfigMap更新并及时更新到服务的配置中。这样运维人员只需要利用kubernetes的控制台(cli或者web)修改线上服务的配置,比如修改日志等级、降级、调整阈值等等。

本文引用源码https://github.com/jindezgm/konfig/blob/master/konfig.go

实现

konfig是利用kubernetes的一个ConfigMap实现的一个配置树,虽然ConfigMap.Data是map[string]string类型,但是konfig会对ConfigMap.Data中的值做进一步(递归)yaml解析,前提条件是值是以"---\n"开头。这样设计的目的是让konfig支持多级配置,而ConfigMap只有一级在一些使用场景并不友好。当然,利用一级也是可以实现多级配置,只是把多级体现在key上,例如:"a.b.c", "c.d",笔者认为视觉上不太优雅。

konfig支持以多种类型获取相同的配置,可以根据需要转换成指定类型,如下接口定义所示:

// 所有接口都会返回配置的版本号,即ConfigMap.ResourceVersion,keys是多级的key,当keys为空时表示根,即整个ConfigMap.
type Interface interface {// 如果keys已经被注册某种类型(参看下面的RegValue接口),则返回指定类型的值,否则返回原生类型的值。Get(keys ...string) (interface{}, int64)// 获取布尔型GetBool(keys ...string) (bool, int64)// 获取64、32位整型、GetInt64(keys ...string) (int64, int64)GetInt(keys ...string) (int, int64)GetInt32(keys ...string) (int32, int64)// 获取64、32位浮点型GetFloat64(keys ...string) (float64, int64)GetFloat32(keys ...string) (float32, int64)// 获取字符串GetString(keys ...string) (string, int64)// 将指定keys下的值注册为一种类型,配合Get()接口使用可以将keys下的值转换为注册的类型返回,其中tag是成员变量的tag名称,比如jsonRegValue(ptr interface{}, tag string, keys ...string) (interface{}, int64)// 获取指定类型的value,konfig会将map[string]interface{}转换为value对象,其中tag是成员变量的tag名称,比如json.GetValue(ptr interface{}, tag string, keys ...string) int64// 将指定的keys下值挂载/卸载到环境变量,MountEnv(keys ...string)UnmountEnv()// 获取版本号Revision() int64
}

konfig在Get指定类型的配置时,除了原生类型外,尽最大努力将原生类型转为指定类型,以下是konfig支持的类型转换:

  • int,int32,int64:支持浮点型转整型、支持字符串转整型(string->float64->intxx)、支持布尔型转整型(true:1,false:0)
  • float32,float64:支持字符串转浮点型
  • bool:支持整型、浮点型转布尔型(非零:true,零:false),支持字符串转布尔型(不区分大小写的"True":true,不区分大小写的"False":false)
  • string:支持所有类型转字符串,采用fmt.Sprintf("%v")返回

konfig保证了单个接口的原子性,但是如果连续调用两次接口可能会返回两个不同的版本,说明在两次调用接口之间ConfigMap发生了变化。如果两次调用接口获得的配置参数对于版本一致性要求比较高的话,就需要重新调用,直到所有的配置的版本相同为止。这种情况发生的概率比较低,并且绝大部分重新调用一次就可以解决,因为ConfigMap的更新频率极低。但是,这种方法貌似有点丑陋,使用者可以将此类的配置定义一种struct,然后通过GetValue()一次性拿到所有的配置。笔者常用的方法就是把整个ConfigMap定义为一个类型,然后一次性读取所有的配置。如下代码所示:

import "github.com/jindezgm/konfig"// 定义自己的配置类型
type MyConfig struct {Bool   bool   `json:"bool"`Int    int    `json:"int"`String string `json:"string"`
}kfg, _ := konfig.NewWithClientset(...)
var my MyStruct
var rev int64// 应用引用配置的功能实现
for {// 版本发生更新时重新获取配置if r := kfg.Revision(); r > rev {// 此处的keys为空,这是将整个ConfigMap.Data映射为MyStructrev = kfg.GetValue(&my, "json")}// 使用配置...
}

因为GetValue()接口会执行一次类似Unmarshal的过程,所以是有一定开销的,适用于调用频率不高的场景。如果需要高频调用,建议应用缓存配置(如上代码),并根据revision决定是否调用该接口。如果应用想省去这些麻烦的操作,那就调用RegValue()接口将类型注册到konfig,由konfig按需解析,在配合Get()接口就可以满足高频调用的场景了。如下代码所示:

// 将"my"下的所有值注册为MyConfig类型
kfg.RegValue(&MyConfig{}, "json", "my")
for {// 每次引用直接调用Get,konfig保证一致性、隔离性以及原子性value, _ = kfg.Get("my")my := value.(*MyConfig)...
}

在一些场景,某个功能点只需要引用一个配置项,使用者每次引用时可以直接调用接口,不用在自己的代码中缓存配置(当revision变大再读取配置更新缓存),因为konfig的读取性能还是有保证的。如下代码所示,按配置打印:

for {if p, _ := kfg.GetBool("print"); p {fmt.Println("Hello world")}
}

当然,如果习惯读取环境变量的方法获取配置,而容器更新环境变量又会造成容器重启,那么可以用MountEnv()接口将配置挂载到环境变量,如下代码所示:

// keys为空,将ConfigMap.Data挂载到环境变量. 需要注意的是,MountEnv()的keys下应该只有一级配置,如果是多级,konfig会用fmt.Sprintf("%v")进行格式化
kfg.MountEnv()
defer kfg.UnmountEnv()
for {if strings.ToLower(os.Getenv("print")) == "true" {fmt.Println("Hello world")}
}

konfig支持两种创建方式:1.利用Clientset;2.利用SharedInformerFactory。前者适用于应用无需不访问kubernetes场景,需要为konfig单独创建一次clientset,这部分可以参考官方实例代码;后者适用于应用需要访问kubernetes的场景,那么konfig与应用共享Informer。无论哪一种情况,都需要授权pod读取ConfigMap的权限。

那么,问题来了,为什么不用ConfigMap挂在成文件的方式?答:时效性不好,因为ConfigMap更新到Pod内文件更新可能需要数秒钟(我记忆中好像是10秒),如果线上需要紧急更新配置(比如降级处理)不是很好用,而konfig就没有这个问题。

不足

  1. 当前konfig递归解析只支持yaml格式,其实json也很容易支持,感觉不是很必要;
  2. konfig不支持回调,即ConfigMap更新后,将变化的部分回调给用户,当前的解决方式是通过revision解决;
  3. GetValue()虽然能够一次获取多个配置,但是需要所有的配置都在一个键下,如果一次获取配置树不同分支的多个配置,konfig还不支持,感觉可以用flag.FlagSet实现;
  4. MountEnv()不会比较两次配置的差异,然后删除新ConfigMap中没有的配置,他只是简单的将每次ConfigMap中的值设置到环境变量中;而UnmountEnv()也不会删除环境变量,只是不再更新环境变量而已;这些都可以实现,只是暂时还没有看到必要性;

konfig:采用ConfigMap实现线上配置热更新相关推荐

  1. java 热更新class_线上java热更新代码实现

    游戏上线后难免会有功能性bug,这些bug很多只做一些小的改动即可修复.设想假如每次有bug修复之后,都要重启服务器,势必会导致部分玩家流失,对游戏产生不好的影响.在这个背景下,代码热更新还是很有必要 ...

  2. Nacos配置管理-配置热更新

    配置热更新 我们最终的目的,是修改nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新. 要实现配置热更新,可以使用两种方式: 方式一 在@Value注入的变量所在类上添加注解@Re ...

  3. 基于选项模式实现.NET Core的配置热更新

    作者 | 秦元培 出品 | CSDN(ID:CSDNnews) 头图 | CSDN 下载自东方 IC 最近在面试的时候,遇到了一个关于 .NET Core 配置热更新的问题,顾名思义,就是在应用程序的 ...

  4. Nacos配置热更新两种方式。

    1:目的: 修改nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新 2:方式 (1)在@Value注入的变量所在类上添加注解@RefreshScope:(在这里呢应该辉出现空指针异 ...

  5. Nacos配置管理——配置热更新

    文章目录 Nacos配置热更新 1.方式一 2.方式二 Nacos配置热更新 我们引入Nacos配置中心的最终目的,是修改nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新. 要实 ...

  6. IDEA SpringBoot项目配置热更新,无需每次手动重启服务器

    IDEA SpringBoot项目配置热更新的步骤 在pom.xml中添加依赖: <dependency><groupId>org.springframework.boot&l ...

  7. Mysql服务器线上配置主从同步

    我们一般在线上搭建MYSQL都会部署一套主从同步方案: 当master(主)库的数据发生变化的时候,变化会实时的同步到slave(从)库. 主从复制的过程: Mysql同步过程的第一部分就是maste ...

  8. YARP+AgileConfig 5分钟实现一个支持配置热更新的代理网关

    YARP 是微软开源的一个反向代理项目,英文名叫 Yet Another Reverse Proxy .所谓反向代理最有名的那就是 nginx 了,没错 YARP 也可以用来完成 nginx 的大部分 ...

  9. flutter已经支持安卓热更新_flutter 在 android 上的热更新

    热更新是一种需求吧. 自然会想到flutter 是否支持热更新. 然后一些群里问了问普遍反映不可以热更新,还说咸鱼的文章写了不支持热更新. 然后我表示很怀疑. 我的结论可以做到热更新 1.你需要把fl ...

最新文章

  1. Change Fiori launchpad logo
  2. 【POJ - 3037】Skiing (Dijkstra算法)
  3. 13. OD-内嵌补丁,过期的软件DVD Menu Studio破解,switch函数,break等于KillTimer
  4. java组合与继承始示例_Java示例中的组合
  5. MySQL使用小技巧(二)——MySQL忘记密码怎么办
  6. 统计学计算机实验教程,清华大学出版社-图书详情-《统计学计算机实验教程——基于Excel软件》...
  7. 深入游戏变速底层原理以及内核变速的实现
  8. 期货计算机撮合成交的原则,期货ABC之行情及基本术语:八、价格优先、时间优先及撮合成交价的确定...
  9. 链家深圳二手房房价数据分析
  10. java游戏征途2008_醉剑逍遥-征途天下
  11. git/github入门
  12. win 10 安装visual studio 2010
  13. 爬取图片-工作常用小工具01
  14. 幻方萤火 | 高速读写文件系统 3FS
  15. 《士兵突击》能让你学到什么
  16. Linux 设备树下的 platform 驱动示例
  17. 「Pygame经典合集」​​​​​​终极 大招:让你玩儿到爽
  18. 青蛙跳台阶python解法
  19. 荧光定量PCR(qPCR)科研技术服务
  20. 【Linux 内核】进程管理 - 进程优先级 ① ( 限期进程 | 实时进程 | 普通进程 | 进程优先级相关字段 )

热门文章

  1. 杰理之调音限幅器 压缩器【篇】
  2. 关于netty的@Sharable注解含义,你可bie瞎说了
  3. performance API 中什么指标可以衡量首屏时间
  4. lol1月24服务器维护,《lol》1月24日停机到几点 1月24日停机维护内容一览
  5. 20万华人的迪拜:亚洲的另一大Web3之都
  6. vivo手机互传的文件怎么找到_基于 P2P 的在线文件传输工具,电脑与手机互传文件...
  7. 怎么在微信小程序中设置密码重置
  8. 冷数据、温数据、热数据,难道数据也是有温度的?
  9. 记微信开发者工具登录网络连接失败
  10. 接口测试实战| GET/POST 请求区别详解