细节讨论
=======

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

###问题描述：###

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

###解决方法：###

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

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

**对于Web系统：**

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

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

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

- 解决方法一：提供ThreadContext包，在每次请求一开始时都复制系统里的所有配置缓存(复制过程要与配置更新Sync互斥)，从而保证每次会话的数据的一致性。
- 解决方法二：提供ThreadContext包，每次请求都绑定一个版本号，如果读取时版本号不一致则报错，需要重新请求。
- 解决方法三：方法二的加强版，添加一个注解定义，标注它是需要强一制性的，每次会话读取时只复制这些强一制性配置(复制过程要与配置更新Sync互斥)。
- 解决方法四：提供ThreadContext包，系统内保存有多个配置缓存层，读取时统一读取某个版本的缓存。每当配置更新时，缓存层增加。
   
第一种方法，代价太大。第二种方法，严重增加用户负担，第三种还是需要用户关心这个事情。我们将采用第四种方法。

**对于非Web项目：**

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

## 配置放在哪里 ##

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

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



