从移动端的配置说起

2,312 阅读8分钟

出了什么问题

大多数客户端都有远程配置的功能和需求,项目规模由小到大以后,对客户端动态配置的需求就会迅速增加。就会出现新的问题和需求。

  • 配置项逐渐增多,配置内容大小逐步增大
  • 客户端版本逐渐曾多,版本之间的配置差异较大,配置管理混乱,牵一发而动全身
  • 运营和产品对业务配置的需求逐渐增加,需要有独特的配置关联逻辑
  • 配置不断庞大,下发到客户端的时候需考虑流量问题
  • 配置服务端考虑并发问题
  • 配置动态下发,考虑不同的更新策略,考虑安全性

历史及现状

可能很多移动端的开发者都有这样的经历: 通过某一个接口,获取某一业务功能的配置信息,有时候需要根据业务触发的逻辑,在不同的时机调用接口,获取配置,又有些时候需要把客户端的一些信息,如版本号、时间、上次的缓存等等,传入接口获取最新配置,这其实就是最简单的远程配置需求,根据配置相关条件,通过不同的更新策略,获取远程配置。

1.0阶段

设计接口如下:

$ curl https://api.xxx.com/v0/configuration/getMessageConfig?appVersion=1.0.0&platform=iOS&messageType=1 \
-H "SS-Cache-Version: XXXXXXXXX"

{
	"code":"0",
	"result":{
		"config_value":"******",
		"cache_version":"******"
	}
}

通过不断开发新的接口,满足各个业务线对配置功能需求。 优点:需求较少的时候,开发特别快 缺点:客户端需要不断开发新的API调用,服务端则需要不断开发新接口,配置与客户端的关联信息发生变化,API则需要进行升级,而客户端存在多版本并存的情况,老版本API也无法下线,占用服务端资源。

2.0阶段

为减轻服务端开发依赖,有的开发者通过使用第三方开发平台进行配置分发。 例如,使用umeng的在线参数进行远程配置

优点: 无需服务端开发,第三方实现SDK与其服务端的通信,不需要做客户端开发

缺点: 无法与客户端信息做相关,只能通过约定配置参数名称例如 配置项名称+平台名称+客户端版本作为配置项名称,做硬关联,大量生成重复配置项。无法控制更新策略。业务相关配置放置在第三方平台,无法保证数据安全。

3.0阶段

由此前的经历和需求痛点,我们设计出新的移动端配置中心。 主要有如下功能

  1. 支持配置客户端信息与配置项相关,做到不同的客户端获取不同的配置项
  2. 支持配置增量更新
  3. 支持客户端配置加密下发,保证安全
  4. 提供统一后台配置界面,方便进行配置的管理
  5. 做到配置更新,主动推送到客户端
  6. 支持配置回滚,删除恢复

解决思路

根据以上需求,整体方案的解决思路可以是如下这样:

  1. 移动端开发远程配置组件,用于获取最新的后端配置
  2. 客户端与接口交互的时候,将诸如appid(存在对个应用)、appVersion、platform、channel(渠道)、systemVersion等基础信息传入接口,方便配置项与其关联
  3. 客户端与接口交互的时候,支持全量更新,支持通过生成增量包进行增量更新
  4. 服务端与客户端进行数据传输的时候,进行通信数据加密
  5. 对于服务端,客户端提供定时配置同步策略,服务端通过推送和长连接通讯主动推送配置
  6. 配置项后台的增删改查,都采取版本叠加,而不是重新覆盖,支持回滚与删除回复
  7. 通过标签系统,提供更灵活的配置项与客户端的匹配关系

涉及方面

有了大概的解决思路以后,可以对涉及到的方面进行整体盘点,其实这也是移动端基础设施的摸查,涉及以下几个方面:

  • 客户端配置组件,与配置中心交互,获取配置信息,定时轮询更新策略,内存缓存与本地持久化配置信息,增量包应用,接收服务端推送事件
  • 配置中心,提供对客户端获取配置的API接口服务,接口支持根据客户端信息和标签返回增量数据包/全量更新包
  • 后台管理系统,提供应用管理和配置管理,支持新建配置,修改配置,回滚和删除配置
  • 认证服务,客户端通过认证服务以后,获取通讯管道加密的会话密钥,提供接口数据加密功能
  • 标签系统,提供对客户端、设备、用户等多个维度的标签设置和获取服务
  • 消息中心,提供根据标签系统,将配置更新的事件推送到客户端
  • 定时任务系统,提供定时服务,将配置生效、推送配置等设置为定时任务

整体设计

API设计

客户端与配置中心进行API交互,接口定义如下: queryConfiguration 入参:

  • clientInfo,包含appid、appVersion、platform、channel等
  • cacheInfo
{
	"clientInfo":{
		"appid":"***",
		"appVersion":"1.0.0",
		"platform":"1", //1为iOS 2为Android
		"channel":"AppStore"
	},
	"cacheInfo":[{
			"configName":"routerConfigMap",
			"version":"*****"
		},
		{
			"configName":"conditionConfiguration",
			"version":"*****"
		}
	]
}

返回值:

  • configurationData,一个数组,包含配置项全量或者增量数据包
code 含义 备注
0 增量包 返回增量包数据
1 全量数据 返回配置全量数据
2 已删除 表示该配置项已删除
{
    configurationData:[
        {
            "configName":"routerConfigMap",
            "version":"*****",
            "code":0,
			  "hash":"*****",
            "patch":[]
        },
        {
            "configName":"orderActivity",
            "version":"xxxxxx",
            "code":1,
			  "hash":"*****",
            "value":"xxxxx"
        },
        {
            "configName":"conditionConfiguration",
            "version":"xxxxxx",
            "code":2
        },
    ]
}

客户端与API交互流程图

配置中心管理平台

App管理平台

配置平台

配置管理后台,提供对应用的管理与配置项的管理,配置项与基本客户端信息关联。 配置项存储与客户端信息进行关联映射,关系有OR和AND两种,每个配置项设置关联条件。例如配置项的关联条件为:

configId <—> appId AND appVersion AND channel AND platform OR tag

新建、更新或者删除某一配置,可以设置定时生效,Job系统会定时执行操作,并调用消息中心,按照关联条件,进行推送通知给客户端,客户端接到更新事件后,再调用API接口完成配置项更新。

服务端查询配置项流程

这个流程里有三个关键点:

  1. 根据clientInfo查询匹配的配置列表,其中,AND关系和OR关系,需要通过构建一个配置id与条件的字典表,查询的时候将AND或者OR的条件相关的配置id全部查询出来,然后进行AND计算(取交集),OR计算取并集,其中appVersion是一个范围,例如设置某一配置项的appVersion为”<=2.3.0 & >=2.1.0”, 根据语义化版本,进行单独匹配,语义化版本 2.0.0 | Semantic Versioning的规范请详细查看文档
  2. 如果客户端上传有cacheInfo,将缓存的配置id列表上传,那么,根据条件查出的最新配置id列表为A,上传的缓存配置id列表为B,分别计算出A与B的交集,逐个对比hash值,算出增量包,再算出属于A但不属于B的部分,属于新增配置项,做全量包,再算出属于B不属于A的,属于要删除的部分。进行上述计算,只需要定义一套数组的交集、并集与补集的计算,就可计算出结果。
  3. 生成增量包的时候,需要根据客户端配置id和对应hash值,找到旧版本的配置项,再跟最新的配置项做文本对比生成增量包。文本增量算法有google官方实现的一套:GitHub - google/diff-match-patch: Diff Match Patch is a high-performance library in multiple languages that manipulates plain text.

推送更新消息

这个比较简单,通过后台增删改查的配置项,根据其标签和客户端信息,使用消息中心,将更新事件静默推送到设备,而设备客户端信息和标签,与设备唯一标示的绑定关系,在标签系统维护。

总结改进

新的配置中心,满足了现阶段的需求,通过将配置项的关联条件抽象成标签,再加上增量更新,满足了节省流量的需求。 未来还有进一步改进的空间:

  1. 配置后台通过jsonschema来效验配置的格式,方式配置错误
  2. 客户端的配置组件改善通讯方式,使用推拉结合的方式,目前的推送基于消息中心,未来可以使用UDP+心跳方式维持客户端与服务端的数据同步
  3. 实现更多的更新发布场景,例如,更新配置项,可按照一定比例,逐步进行灰度更新,失败可回退,提高可用性

多谢您花费宝贵的时间阅读,希望能够与大家多多交流