apollo c++客户端

简介

apollo是携程开源的一款较为成熟的远程配置系统。
通过这个远程配置,用户可以在界面上配置参数,然后让参数自动同步到服务上,减少中间不必要的CICD流程。
目前apollo提供了java和.net的客户端,但没有c++版本的,本文将设计并实现一款c++语言版本的客户端。
对于直接使用的用户,可以直接跳到“使用案例”环节。

设计

痛点

配置文件解析一直是c++同学的痛,每次添加配置就需要一段额外的代码解析。
而解析的步骤必须要先判断key在不在,然后判断value的类型,接着把value取出来。
如果value比较深,就要解析好几回,费时费力不说,中间环节还不能少,一少就容易出coredump(没错,就是rapidjson)。
像某些语言就不一样了,直接一键反序列化(比如jackson),还是很方便的。
一套方便的反序列化方式,还是很有必要的。

引入远程配置系统之前,通常配置文件是本地配置,分为一次性加载和热加载两种,前者在服务启动时读取并解析;后者需要开相应的线程区侦测文件变化,文件发生变化后,重新加载解析,并替换内存中的数据。
接入远程配置之后,客户端需要知道配置的变更,并同步下来并替换内存中的数据。
目前apollo并没有中间者,无法从中间者订阅更新,需要客户端根据已有的接口,实现一套更新机制。

本地配置加载失败通常是配置文件解析失败,或者磁盘坏了加载不了。前者只要流水线完善基本可以避免,后者虽然避免不了,但是发生的概率极低,并且出现问题的实例也不会很多。
引入远程配置系统后,远程配置的服务稳定性影响着接入远程配置的服务的稳定性。
虽然这类基础服务的服务稳定性较高,但是没有到达100%,当接入实例较多时,一次重启总会出现几个失败的实例。
一套有效的容错机制还是有必要的。

解决方案

反序列化问题

目前apollo支持properties、json、yaml、yml、xml、txt等格式,其中json、yaml、yml、xml、txt不支持分级发布。
properties支持kv结构,value不支持复杂结构。
目前c++中能支持“一键反序列化”的就数protobuf和thrift了,本文将使用protobuf作为反序列化工具。

上面提到的哪几种不支持分级发布的格式,可以认为是兼容格式,应该逐渐迁移到properties中。
properties各种中的value本身是字符串,可以填充json,然后使用protobuf进行反序列化。
使用的时候根据key获取对应Message数据,减少解析的环节。
这种方式一方面能对配置进行分组管理,避免配置较多时出现杂乱的情况;
另一方面,远程配置客户端通常是不保障同一个业务请求中多次调用获取同一个值的。
如果配置基础类型,通常在一个业务请求中会使用多次,这时如果每次的值不一样,可能导致异常的情况,并且还不好定位。
综上,本文的客户端强制要求value是json格式,然后使用对应的Message进行反序列化。

更新问题

apollo提供了三套接口,带缓存接口、不带缓存接口、long poll接口。

“带缓存接口”每次请求都会返回相应的数据,需要用户自行判断数据是否更新。
一般这个接口是固定事件间隔轮询,查询一次就反序列化数据,并更新内存数据,实际也没啥大的问题,除了看上去比较low之外。

“不带缓存接口”需要用户每次请求带上releaseKey,然后如果配置更新则返回数据和新的releaseKey,否则返回304表示配置没更新。
通过这个接口可以发现数据是否已经更新,方便替换。但是这个接口不知道性能怎么样。

“long poll”接口,需要客户端请求服务,然后服务把接口挂起,直到配置有更新,才会有相应。
这个需要客户端有特殊处理,不然超过60s,自动超时。

权衡之后,选择第二种“不带缓存接口”,客户端需要额外记录每个namespace的releaseKey,方便后续更新。

实现

使用案例

参考