从配置文件到分布式配置管理QConf

QConf是奇虎360广泛使用的配置管理服务,现已开源:
https://github.com/Qihoo360/QConf。欢迎大家关注使用。

本文从设计初衷,架构实现,使用情况及相关产品比较四个方面进行介绍。

设计初衷

在分布式环境中,出于负载、容错等种种原因,几乎所有的服务都需要在不同的机器节点上部署多个实例。当然,业务项目中总少不了各种类型的配置文件,我们常常会遇到这样的问题,有时仅仅是一个配置内容的修改,便需要重新进行代码提交svn/git,打包,分发上线的流程。当部署的机器有很多时,分发上线本身也是一个很繁杂的工作。而配置文件的修改频率又远远大于代码本身。
追本溯源,我们认为所有的这些麻烦的根源是我们对配置和代码在管理和发布过程中不加区分。配置本身源于代码,是我们为了提高代码的灵活性而提取出来的一些经常变化的或需要定制的内容,而正是配置的这种天生的变化特征给我们带了很大的麻烦。
因此,我们开发了分布式配置管理系统QConf,并依托QConf在360内部提供了一整套配置管理服务,QConf致力于将配置内容从代码中完全分离出来,及时可靠高效地提供配置访问和更新服务。

整体认识

为了让大家对之后的内容有个直观的认识,先来介绍一下如果你要在自己的项目中使用QConf应该怎么做:

  • 首先,需要在机器上部署QConf,采用cmake构建。

cmake ..
make
make install

  • 之后,通过Zookeeper客户端或QConf管理界面在Zookeeper上建立自己的节点结构,节点完整路径便是QConf的key值,以360公司内部QConf管理界面为例:
    图1 QConf管理界面
    图1 QConf管理界面
  • 最后,选择自己项目的语言版本的QConf库,并在需要需要获得配置内容的位置,直接调用类似于get_conf(key)这样的接口,并放心每次取得的都是最新鲜出炉的配置。以shell命令为例:


    图2 QConf shell接口使用
    图2 QConf shell接口使用

需要说明的是,使用QConf后已经没有所谓的配置文件的概念,你要做的就是在你需要的地方获取正确的内容,我们认为,这才是你真正想要的。

架构介绍

了解了QConf的设计初衷和使用方式,相信大家已经对QConf有一个整体的认识和对其实现的猜想。在介绍架构之前,还需要向大家申明一下我们对配置信息的定位,因为这个定位直接决定了我们的结构设计和组件选择。

  • 单条数据量小
  • 更新频繁(较代码而言)
  • 配置总数可能巨大,但单台机器关心配置数有限
  • 读多写少

进入主题,开始介绍QConf的架构实现:


图3 QConf整体结构
图3 QConf整体结构

上图展示的是QConf的基本结构,从角色上划分主要包括QConf客户端,QConf服务端,QConf管理端。

QConf服务端。

QConf使用ZooKeeper集群作为服务端提供服务。众所周知,ZooKeeper是一套分布式应用程序协调服务,根据上面提到的对配置内容的定位,我们认为可以将单条配置内容直接存储在ZooKeeper的一个ZNode上,并利用ZooKeeper的Watch监听功能实现配置变化时对客户端的及时通知。
按照ZooKeeper的设计目标,其只提供最基础的功能,包括顺序一致,原子性,单一系统镜像,可靠性和及时性。另外Zookeeper还有如下特点:

  • 类文件系统的节点组织
  • 稳定,无单点问题
  • 订阅通知机制

关于Zookeeper,更多见:https://zookeeper.apache.org/

QConf客户端

但在接口方面,ZooKeeper本身只提供了非常基本的操作,并且其客户端接口原始,所以我们需要在QConf的客户端部分解决如下问题

  • 降低与ZooKeeper的链接数
    原生的ZooKeeper客户端中,所有需要获取配置的进程都需要与ZooKeeper保持长连接,在生产环境中每个客户端机器可能都会有上百个进程需要访问数据,这对ZooKeeper的压力非常大而且也是不必要的。
  • 本地缓存
    当然我们不希望客户端进程每次需要数据都走网络获取,所以需要维护一份客户端缓存,仅在配置变化时更新。
  • 容错
    当进程死掉,网络终端,机器重启等异常情况发生时,我们希望能尽可能的提供可靠的配置获取服务
  • 多语言版本接口
    目前提供的语言版本包括:c,php,java,python,go,lua,shell
  • 配置更新及时,可以秒级同步到所有客户端机器
  • 高效的配置读取,内存级的访问速度

下面来看下QConf客户端的架构:


图4 QConf客户端架构
图4 QConf客户端架构

可以看到QConf客户端主要有:agent、各种语言接口、连接他们的消息队列和共享内存。
在QConf中,配置以key-value的形式存在,业务进程给出key获得对应value,这与传统的配置文件方式是一致的。

下面通过两个主要场景的数据流动来说明他们各自的功能和角色:

  • 业务进程请求数据


    图5 数据流动-业务进程请求数据
    图5 数据流动-业务进程请求数据
  1. 业务进程调用某一种语言的QConf接口,从共享内存中查找需要的配置信息;
  2. 如果存在,直接获取,否则会向消息队列中加入该配置key;
  3. agent消息队列中感知需要获取的配置key;
  4. agentZooKeeper查询数据并注册监听;
  5. agent将获得的配置value序列化后放入共享内存
  6. 业务进程从共享内存中获得最新值。
  • 配置信息更新


    图5 数据流动-配置更新
    图5 数据流动-配置更新
  1. ZooKeeper通知agent某配置项发生变化;
  2. agentZooKeeper查询新值并更新watcher;
  3. agent用新值更新共享内存中的该配置项。

通过上面的说明,可以看出QConf的整体结构和流程非常简单。
QConf中各个组件或线程之间仅通过有限的中间数据结构通信,耦合性非常小,各自只负责自己的本职工作和一亩三分地,而不感知整体结构。下面通过几个点来详细介绍:

  • 无锁
    根据上文提到的配置信息的特征,我们认为在QConf客户端进行的是多进程并行读取的过程,对配置数据来说读操作远多于写操作。为了尽可能的提高读效率,整个QConf客户端在操作共享内存时采用的是无锁的操作,同时为了保证数据的正确,采取了如下两个措施:

  • 单点写,将写操作集中到单一线程,其他线程通过中间数据结构与之通信,写操作排队,用这种方法牺牲掉一些写效率。在QConf客户端,需要对共享内存进行写操作的场景有:

    1. 用户进程通过消息队列发送的需获取key;
    2. ZooKeeper 配置修改删除等触发Watcher通知,需更新;
    3. 为了消除watcher丢失造成的不一致,需要定时对共享内存中的所有配置重新注册watcher,此时可能会需要更新;
    4. 发生agent重启、网络中断、ZooKeeper会话过期等异常情况之后,需重新拉数据,此时可能需要更新。
  • 读验证,无锁的读写方式,会存在读到未写入完全数据的危险,但考虑到在绝对的读多写少环境中这种情况发生的概率较低,所以我们允许其发生,通过读操作时的验证来发现。共享内存数据在序列化时会带其md5值,业务进程从共享内存中读取时,利用预存的md5值验证是否正确读取。

  • 异常处理
    QConf中采取了一些处理来应对不可避免的异常情况

  • 采用父子进程keepalive的方式,应对agent进程异常退出的情况;

  • 维护一份落盘数据,应对断网情况下共享内存又被清空的状况;

  • 网络中断恢复后,对共享内存中所有数据进行检查,并重新注册watcher;

  • 定时扫描共享内存;

  • 数据序列化
    QConf 客户端中有多处需要将数据序列化通信或存储,包括共享内存,消息队列,落盘数据中的内容。采取如下协议:


    图6 数据序列化协议
    图6 数据序列化协议
  • agent任务
    通过上面的描述,大家应该大概了解了agent所做的一些事情,下面从agent的内部的线程分工的角度整理一下,如下图:


    图7 Agent内部结构
    图7 Agent内部结构
  • Send线程:ZooKeeper线程,处理网络数据包,进行协议包的解析与封装,并将Zookeeper的事件加入WaitingEvent队列等待处理;

  • Event 线程:ZooKeeper线程,依次获取WaitingEvent队列中的事件,并进行相应处理,这里我们关注节点删除、节点值修改、子节点变化、会话过期等事件。对特定的事件会进行相应的操作,以节点值修改为例,agent会按上边提到的方式序列化该节点key,并将其加入到WaitingWriting队列,等待Main线程处理;

  • msq线程:之前讲数据流动场景的时候有提到,用户进程从共享内存中找不到对应配置后,会向消息队列中加入该配置,Msq线程便是负责从消息队列中获取业务进程的取配置需求,并同样通过WaitingWriting队列发送给Main进程;

  • Scan 线程:扫描共享内存中的所有配置,发现与Zookeeper不一致的情况时,将key值加入WaitingWriting队列。Scan线程会在ZooKeeper重连或轮询期到达时进行上述操作;

  • Main线程:共享内存的唯一写入线程,从Zookeeper获得数据写入共享内存,维护共享内存中的内容;

  • Trigger 线程:该线程负责一些周边逻辑的调用,包括:

    1. dump操作:将共享内存的内容同步一份到本地,QConf采用的gdbm;
    2. feedback操作:QConf支持更新反馈的功能,可向用户指定web服务以一定的格式发送反馈;
    3. script操作:在某些情况下,业务希望当配置变化时,做一些自定义的操作,QConf支持配置变化时调用用户脚本,agent按一种固定的约定在配置发生变化时调用对应的脚本。

QConf管理端

管理端是业务修改配置的页面入口,利用数据库提供一些如批量导入,权限管理,版本控制等上层功能。
由于公司内的一些业务耦合和需求定制,当前开源的QConf管理端这边提供了一个简易的页面,和一套下层的c++接口,如下图:


图8 QConf简易管理界面
图8 QConf简易管理界面

之后计划进一步完善以及跟社区合作提供更友好的界面。

QConf的结构及实现大概就介绍到这,接下来...

One More Thing

QConf 除了存储配置的基本功能外,还在公司内提供了一套简单的服务发现功能,该功能允许业务在QConf上配置一组服务,QConf会监控其服务的存活。当业务进程调用获取服务的接口时,会根据用户需求,返回全部可用服务,或某一可用服务。
不同于普通配置:

  • 结构上多一个Monitor的角色,来监控所有服务的存活, 如下图:


    图9 提供服务发现的QConf结构
    图9 提供服务发现的QConf结构
  • 提供对应的客户端接口,get_host获取某一可用服务,get_allhost获取所有可用服务
  • 管理端页面对应的展示方式及操作,尤其是对指定服务的添加删除,上线下线

需要明确的是,目前Monitor事实上仅仅是通过查看服务端口的存活来判断的,在实际生产环境中,该功能多与实际服务提供者的监控结合,由服务提供者的监控调用QConf的相应接口实现服务的上下线。

使用方式及使用场景

目前360内部已经广泛的使用QConf。覆盖云盘、大流程、系统部、dba、图搜、影视、地图、硬件、手机卫士、好搜等大部分业务。
部署国内外共51几个机房,客户端机器超两万台,稳定运行两年。

使用的方式主要包括:

  • 简单配置
    公司内使用最广泛的用法,QConf非常适合经常需要变动的配置使用,如开关信息、版本信息、推荐信息、超时时间等。
  • 服务方式
    这种方式多被服务提供者采用,如dba,系统部等,采用上述的服务配置的方式,通过QConf向公司的所有业务提供存储,计算及web服务。

配置管理类产品比较

通过上面的介绍,大家应该感觉到QConf与puppet,confd这类产品还是有很大不同的。
国内与之类似的产品包括淘宝的diamond,微博的vintage,百度的disconf。
其中diamond和vintage比较类似,他们将配置数据存在像mysql或redis这样的存储中,并需要通过客户端的轮训来感知配置变化,这样会有很多的无效通讯,因此采取比较md5值的方式来避免整个配置值的传输;
disconf与QConf类似,同样采用ZooKeeper的通知机制,不同的是,其将真实的配置数据存在mysql中,并且整个与java耦合很重,且配置复杂。

QConf因为其对配置信息的定位,使得整个结构非常简单,容易部署和使用。在Github上可以找到完整代码:https://github.com/Qihoo360/QConf 欢迎关注。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容