关于配置的思考

2015-06-04

以前研究某个网游的源代码时,发现它的配置是存在数据库的。从游戏的经验倍数/暴率/某个怪物的经验/物品掉落,到登陆服务器IP端口数据库IP端口等等,差不多全部的配置都是。当时比较反感,数据是数据,配置是配置,为什么要把配置的东西存到数据库里面去呢。更底层的心理是,我讨厌任何复杂的东西。不就是一点点配置么,读配置文件就行了,凭什么还要搞出一个对数据库的依赖呢?

慢慢体会到,一个大的系统,会有好些子系统,配置文件分散在好多地方,每项可能还不知道是什么含义。我又开始怀念windows软件了那种界面的东西。点开配置,勾勾叉叉点几项就好。然后也慢慢能理解用数据库存配置的做法:可视化以及查询修改特别方便!这是配置文件远远做不到的。

后来接触的更多了。发现有许多更深层次的东西,比如灵活性vs简单。emacs可以配置得无所不能,什么按键都可以配,想做什么东西都可以改,只要改下配置文件就行了,而vi却简单得多。用户的数量表明,大多数人还是喜欢简单的。我也是喜欢简单。所以,能写死就写死,不要多搞出一项配置。能约定的就用约定风格,不要做成可配置风格。

继续说"喜欢简单"这个话题。当我的程序只需要运行在一台机器上面,改改配置文件就可以了。我喜欢这种简单。再到后来,程序要分布在各个机器上,每个机器上都需要去配置,万一哪里配置出错了还会造成很严重的问题。大多数时候发生的错误,并不是代码出了bug什么的,往往是简单的配置没弄对。比如配置系统一边写到消息队列A,而另一边却是从消息队列B在读取,系统当然不会正常工作了。这个时候单纯的配置文件好像有点力不从心了。

我开始反思这个问题,其实这是问题规模复杂性vs解决方案复杂性所导致的必然。分布式系统成长到一定规模时,大概会需要做一个配置中心。配置中心这种解决方案明显是复杂的,不符合我喜欢的简单原则。但是简单的方案又没法解决问题。说到底,是问题规模的复杂度变高了,导致相应的解决也变得复杂。当问题规模变得复杂之后,为了处理这种复杂,有两套典型的哲学。window哲学是,封装起来,掩盖这种复杂,以保持用户友好,比如GUI。unix哲学是,暴露给用户,但是让每一处细节尽量简单,简单到能够理解的程度。

上面谈的都是设计哲学的取舍,下面看一些实际的跟分布式配置相关的东西。

基于etcd/zookeeper自己撸一套解决方案。这类开源软件提供的是灵活性,可以提供分布式锁,可以用于元数据存储,当然,也可以用于服务发现和中心化配置信息。灵活的代价就是不好用,需要自己做较多工作。

disconf/diamond分别是百度和淘宝开源的分布式配置中心。不过都是Java派系的,难以跟我们的Go的生态结合。

如果要有一个分布式的配置中心,我希望有哪些东西呢?

可用性是需要保证的。一旦配置中心挂了,读不了配置,所以服务就全部起不来了。这里有一篇为什么不要把ZooKeeper用于服务发现,指出需要的是一个AP的系统。而在CP之上构建AP系统,根本就是错误的。

可视化修改。用户友好性非常重要。

动态更新。一旦改了中心化的配置,各个客户端能获取到配置的变化并相应地进行更新。

library or not? 一般都会提供一套相应的库,封装好跟配置中心的交互。客户端使用时直接调库的方法来获取配置。从简单的角度,是比读本地配置文件变复杂了,引入了更多的依赖。原本可以在自己机器就可以跑测试,而后来离开公司环境就测不了了。

内部配置和外部配置。比如说业务对redis或者nsq的依赖,这种属于外部配置。而业务内部某个队列的长度,某个超时时间,或者缓存大小等等,这种属于内部配置。外部配置我希望写成服务发现,类似一层域名到ip的转换,必要场景下还可以高可用。

既然说到了服务发现,有个很有趣的idea是利用DNS来做,比如SkyDNS。DNS是算是互联网非常基础的部件,经过验证过的东西在云时代发挥新的作用。反对的声音也有,比如这里就有提到不靠谱,由于缓存更新是不够及时的,甚至传递过程的延迟多久生效都不可知。

最后说一个考虑中的方案consul+confd。

把配置使用的数据统一到一个地方,其实随便用什么数据库都可以存储。但是这样做有单点问题,可用性达不到要求。为了处理这个问题,可以用etcd或者zookeeper这样的项目来做这里的数据库,就可以解决单点问题了。但是接下来面临的问题,是需要自己做的工作比较多。像封装客户端,可视化方面。

使用consul替换etcd可以解决这个问题。一句话描述consul是什么:"Service discovery and configuration made easy. Distributed, highly available, and datacenter-aware."

我们现在的项目是大量使用json配置文件的。改成写一个客户端库去读配置,工作量会比较大。最直接的方式,使用confd。依然维持原来的配置文件形式,不过数据却是从consul中获取得到。

配置中心服务发现