6. 细节讨论

6.1. 解决 配置“不一致性读“ 问题

问题描述:

应用系统的配置更新过程,它会涉及到多个配置项的更新,它不是一个原子过程。如果在配置更新的过程中,应用程序去读取配置,这里可能存在些“时间窗口”,从而导致不一致性读问题。

解决方法:

前提:无论何种实现,要实现统一读取,避免“非一致性”问题,就必须要对所有读取操作“统一化”。

Disconf支持Web或非Web系统,对于这个问题,Web系统或非Web系统需要区分来看:

对于Web系统:

要实现统一读取,可以使用ThreadContext+AOP来实现。

AOP的使用:通过对配置的get方法做切面,统一将用户的配置请求转发至 “我们自己的配置仓库” 里获取,从而实现统一读取。

ThreadContext的使用方式有以下几种:

  • 解决方法一:提供ThreadContext包,在每次请求一开始时都复制系统里的所有配置缓存(复制过程要与配置更新Sync互斥),从而保证每次会话的数据的一致性。
  • 解决方法二:提供ThreadContext包,每次请求都绑定一个版本号,如果读取时版本号不一致则报错,需要重新请求。
  • 解决方法三:方法二的加强版,添加一个注解定义,标注它是需要强一制性的,每次会话读取时只复制这些强一制性配置(复制过程要与配置更新Sync互斥)。
  • 解决方法四:提供ThreadContext包,系统内保存有多个配置缓存层,读取时统一读取某个版本的缓存。每当配置更新时,缓存层增加。

第一种方法,代价太大。第二种方法,严重增加用户负担,第三种还是需要用户关心这个事情。我们将采用第四种方法。

对于非Web项目:

比较难解决非一致性读取的问题。因为它没有了会话这样一个概念。Apache的FileChangedReloadingStrategy Reload配置文件的方案也没有解决此问题。所以,我们打算放弃这方面的解决。但是,我们还是会提供一个简单却Ugly的解决方案:提供函数来标识用户读取配置的边界。用户可以放弃使用这个方案,但是我们不保证不会发生“不一致读’问题。

6.2. 配置放在哪里

关于配置应该放在哪里?有许多讨论,可以放在Web平台上,也可以放在Zookeeper上。从Disconf的实现中,可以看到Disconf是将配置数据放在Web平台的,而不是放在Zookeeper上的。那为什么这么设计呢?下面有个表格比较一下优劣。

配置放在ZK上 配置放在Disconf-web平台上 比较
配置管理 不易管理。ZK相比Mysql不易管理。 用Mysql统一存储所有配置数据,非常方便管理,可扩展性强。 配置放在Disconf-web显然容易管理和扩展。
职责分配 ZK负责通知与配置数据存储,并提供给client获取数据。disconf-web负责新建、更新配置,并存储一些数据(非配置数据,用作管理),它不为client提供下载配置服务。 ZK只负责通知。disconf-web负责新建、更新、存储 配置,并为client提供下载配置服务。 如果采用第一种方案,数据分散存储了。第二种方案职责明确,数据统一存储在Disconf-web上,ZK只负责通知。
client的配置获取 client启动时,需要从ZK上下载配置。因此必须使用disconf-web先写到ZK上,否则client启动时就无法使用最新配置。配置更新时,client直接从ZK获取最新配置。disconf-web必须统一的在ZK上新建、更新结点。 client不管是启动还是更新时,均是从disconf-web上获取配置。启动时,client会在ZK监控结点(如果不存在,则新建);更新时,disconf-web更新ZK结点。disconf-web不会在ZK上新建结点。 这两种方案其实都差不多
配置的一致性问题 把数据存储在ZK上,无法像Mysql一样可持久化存储。ZK集群关闭后,数据全部丢失。client直接从ZK上获取配置数据,系统运行久之后,client的配置是否正确无从验证。 数据存储在Mysql上,可持久化存储。不管未来是迁移或者扩展之类都非常方便。client是从Web上获取数据,然后再写到ZK上。理论上来说,ZK上的数据应该是与Web平台数据一样的,这可以作为验证平台正确性的一个方法。 从可持久化性、可验证性方面来讲,第二个方案好。
配置获取 配置存储在ZK上,非client想要获取配置,很不容易。 配置存储在Web平台上,通过提供RestHttp接口,不管是谁想要获取配置都非常方便。 第二种方案获取简单便捷。