深入解析淘寶Diamond之客戶端架構
一、什麼是Diamond
diamond是淘寶內部使用的一個管理持久配置的系統,它的特點是簡單、可靠、易用,目前淘寶內部絕大多數系統的配置,由diamond來進行統一管理。
diamond為應用系統提供了獲取配置的服務,應用不僅可以在啟動時從diamond獲取相關的配置,而且可以在執行中對配置資料的變化進行感知並獲取變化後的配置資料。
持久配置是指配置資料會持久化到磁碟和資料庫中。
二、Diamond的特點
- 簡單:整體結構非常簡單,從而減少了出錯的可能性。
- 可靠:應用方在任何情況下都可以啟動,在承載淘寶核心系統並正常執行一年多以來,沒有出現過任何重大故障。
- 易用:客戶端使用只需要兩行程式碼,暴露的介面都非常簡單,易於理解。
三、Diamond的持久機制
訂閱方獲取配置資料時,直接讀取服務端本地磁碟檔案,儘量減少對資料庫壓力。 這種架構用短暫的延時換取最大的效能和一致性,一些配置不能接受延時的情況下,通過API可以獲取資料庫中的最新配置
四、Diamond的容災機制
Diamond作為一個分散式環境下的持久配置系統,有一套完備的容災機制,資料被儲存在:資料庫,服務端磁碟,客戶端快取目錄,以及可以手工干預的容災目錄。 客戶端通過API獲取配置資料按照固定的順序去不同的資料來源獲取資料:容災目錄,服務端磁碟,客戶端快取。
• 資料庫主庫不可用,可以切換到備庫,Diamond繼續提供服務
• 資料庫主備庫全部不可用,Diamond通過本地快取可以繼續提供讀服務
• 資料庫主備庫全部不可用,Diamond服務端全部不可用,Diamond客戶端使用快取目錄繼續執行,支援離線啟動
• 資料庫主備庫全部不可用,Diamond服務端全部不可用,Diamond客戶端快取資料被刪,可以通過拷貝備份的快取目錄到容災目錄下繼續使用
五、Diamond的架構圖
六、Diamond訂閱端(客戶端)分析
先看一個簡單的客戶端訂閱程式碼實現:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> DiamondTestClient { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> DiamondManager manager; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">main</span>(String[] str) { initDiamondManager(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">initDiamondManager</span>() { manager = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DefaultDiamondManager(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"group_test"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"dataId_test"</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ManagerListener() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">receiveConfigInfo</span>(String configInfo) { System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"configInfo="</span>+ configInfo); } }); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
引數的說明:
DefaultDiamondManager有三個引數分別是:groupId,dataId和listener。
group和dataId為String型別,二者結合為diamond-server端儲存資料的惟一key
ManagerListener 是客戶端註冊的資料監聽器, 它的作用是在執行中接受變化的配置資料,然後回撥receiveConfigInfo()方法,執行客戶端處理資料的邏輯。如果要在執行中對變化的配置資料進行處理,就一定要註冊ManagerListener
我們來看一下DefaultDiamondManager的類圖
DefaultDiamondManager的構造方法程式碼如下:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">DefaultDiamondManager</span>(String <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span>, String dataId, ManagerListener managerListener) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.dataId = dataId; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span> = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span>; diamondSubscriber = DiamondClientFactory.getSingletonDiamondSubscriber(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.managerListeners.add(managerListener); ((DefaultSubscriberListener) diamondSubscriber.getSubscriberListener()).addManagerListeners(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.dataId, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.managerListeners); diamondSubscriber.addDataId(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.dataId, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span>); diamondSubscriber.start(); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
說明
1、利用工廠類DiamondClientFactory建立單例訂閱者類。
2、將客戶端建立的偵聽器類新增到偵聽器管理list中並注入到新建立的訂閱者類中。
3、為訂閱者設定dataId和groupId。
4、啟動訂閱者執行緒,開始輪詢訊息。
DiamondSubscriber的類圖如下:
執行diamondSubScriber.start()方法直接進入DefaultDiamondSubscriber子類中,先看如下程式碼:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 啟動DiamondSubscriber:<br> * 1.阻塞主動獲取所有的DataId配置資訊<br> * 2.啟動定時執行緒定時獲取所有的DataId配置資訊<br> */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">start</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (isRun) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> == scheduledExecutor || scheduledExecutor.isTerminated()) { scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); } localConfigInfoProcessor.start(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.diamondConfigure.getFilePath() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/"</span> + DATA_DIR); serverAddressProcessor = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ServerAddressProcessor(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.diamondConfigure, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.scheduledExecutor); serverAddressProcessor.start(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.snapshotConfigInfoProcessor = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> SnapshotConfigInfoProcessor(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.diamondConfigure.getFilePath() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/"</span> + SNAPSHOT_DIR); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 設定domainNamePos值</span> randomDomainNamePos(); initHttpClient(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 初始化完畢</span> isRun = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (log.isInfoEnabled()) { log.info(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"當前使用的域名有:"</span> + <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.diamondConfigure.getDomainNameList()); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (MockServer.isTestMode()) { bFirstCheck = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 設定輪詢間隔時間</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.diamondConfigure.setPollingIntervalTime(Constants.POLLING_INTERVAL_TIME); } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 輪詢</span> rotateCheckConfigInfo(); addShutdownHook(); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li></ul>
說明:
1、ServerAddressProcessor類從服務端獲取提供服務的地址列表(可能會多個)。
2、randomDomainNamePos這個方法是隨機從服務地址列表中選取一個地址。
3、初始化httpClient客戶端,使用initHttpClient方法。
4、設定讀取配置檔案的輪詢時間預設為15秒。
5、rotateCheckConfigInfo這個方法是真正與服務端互動的輪詢方法。
rotateCheckConfigInfo方法的程式碼如下:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 迴圈探測配置資訊是否變化,如果變化,則再次向DiamondServer請求獲取對應的配置資訊 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">rotateCheckConfigInfo</span>() { scheduledExecutor.schedule(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Runnable() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!isRun) { log.warn(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"DiamondSubscriber不在執行狀態中,退出查詢迴圈"</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { checkLocalConfigInfo(); checkDiamondServerConfigInfo(); checkSnapshot(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) { e.printStackTrace(); log.error(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"迴圈探測發生異常"</span>, e); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">finally</span> { rotateCheckConfigInfo(); } } }, bFirstCheck ? <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">60</span> : diamondConfigure.getPollingIntervalTime(), TimeUnit.SECONDS); bFirstCheck = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>; }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li></ul>
說明
1、方法內部啟動一個定時執行緒,預設每隔60秒執行一次。
2、方法內部實際上三個主方法分別是:
* checkLocalConfigInfo:主要是檢查本地資料是否有更新,如果沒有則返回,有則返回最新資料,並通知客戶端配置的listener。
* checkDiamondServerConfigInfo:遠端呼叫服務端,獲取最新修改的配置資料並通知客戶端listener。
* checkSnapshot:主要是持久化資料資訊用的方法。
6.1 checkLocalConfigInfo程式碼分析
<code class="hljs javascript has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">private <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> checkLocalConfigInfo() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Entry<<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* dataId */</span>, ConcurrentHashMap<<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* group */</span>, CacheData>> cacheDatasEntry : cache .entrySet()) { ConcurrentHashMap<<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span>, CacheData> cacheDatas = cacheDatasEntry.getValue(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">null</span> == cacheDatas) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">continue</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Entry<<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span>, CacheData> cacheDataEntry : cacheDatas.entrySet()) { final CacheData cacheData = cacheDataEntry.getValue(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span> configInfo = getLocalConfigureInfomation(cacheData); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">null</span> != configInfo) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (log.isInfoEnabled()) { log.info(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"本地配置資訊被讀取, dataId:"</span> + cacheData.getDataId() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">", group:"</span> + cacheData.getGroup()); } popConfigInfo(cacheData, configInfo); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">continue</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (cacheData.isUseLocalConfigInfo()) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">continue</span>; } } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) { log.error(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"向本地索要配置資訊的過程拋異常"</span>, e); } } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li></ul>
說明:
1、迴圈本地快取資料,比較資料是否更新變化,重點看getLocalConfigureInfomation方法。
2、如果有更新資料則呼叫popConfigInfo方法通知客戶端listener。
再深入看getLocalConfigureInfomation方法,程式碼如下:
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">// 判斷是否變更,沒有變更,返回null if (!filePath<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.equals</span>(cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getLocalConfigInfoFile</span>()) || existFiles<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.get</span>(filePath) != cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getLocalConfigInfoVersion</span>()) { String content = FileUtils<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getFileContent</span>(filePath)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setLocalConfigInfoFile</span>(filePath)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setLocalConfigInfoVersion</span>(existFiles<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.get</span>(filePath))<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setUseLocalConfigInfo</span>(true)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> if (log<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.isInfoEnabled</span>()) { log<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.info</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"本地配置資料發生變化, dataId:"</span> + cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getDataId</span>() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">", group:"</span> + cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getGroup</span>())<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> } return content<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> } else { cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setUseLocalConfigInfo</span>(true)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> if (log<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.isInfoEnabled</span>()) { log<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.debug</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"本地配置資料沒有發生變化, dataId:"</span> + cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getDataId</span>() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">", group:"</span> + cacheData<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getGroup</span>())<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> } return null<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul>
說明:
這段程式碼很關鍵,判斷當前快取的資料是否持久化的檔案資料是否一致,包括版本號,檔案路徑等資訊,如果伺服器端有配置資料更新,客戶端則拿到最新的資料後更新本地檔案內容。
popConfigInfo方法的程式碼如下:
<code class="hljs axapta has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> popConfigInfo(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> CacheData cacheData, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String configInfo) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> ConfigureInfomation configureInfomation = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ConfigureInfomation(); configureInfomation.setConfigureInfomation(configInfo); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String dataId = cacheData.getDataId(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span> = cacheData.getGroup(); configureInfomation.setDataId(dataId); configureInfomation.setGroup(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span>); cacheData.incrementFetchCountAndGet(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.subscriberListener.getExecutor()) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.subscriberListener.getExecutor().execute(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Runnable() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> run() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { subscriberListener.receiveConfigInfo(configureInfomation); saveSnapshot(dataId, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span>, configInfo); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Throwable t) { log.error(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"配置資訊監聽器中有異常,group為:"</span> + <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span> + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">", dataId為:"</span> + dataId, t); } } }); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { subscriberListener.receiveConfigInfo(configureInfomation); saveSnapshot(dataId, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span>, configInfo); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Throwable t) { log.error(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"配置資訊監聽器中有異常,group為:"</span> + <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">group</span> + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">", dataId為:"</span> + dataId, t); } } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li></ul>
說明:
這段程式碼主要是將已經更新的資料通知給客戶端織入的listener程式,使能夠達到最新資料通知給客戶端。
6.2 checkDiamondServerConfigInfo程式碼分析
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">checkDiamondServerConfigInfo</span>() { Set<String> updateDataIdGroupPairs = checkUpdateDataIds(diamondConfigure.getReceiveWaitTime()); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> == updateDataIdGroupPairs || updateDataIdGroupPairs.size() == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) { log.debug(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"沒有被修改的DataID"</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 對於每個發生變化的DataID,都請求一次對應的配置資訊</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (String freshDataIdGroupPair : updateDataIdGroupPairs) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> middleIndex = freshDataIdGroupPair.indexOf(WORD_SEPARATOR); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (middleIndex == -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">continue</span>; String freshDataId = freshDataIdGroupPair.substring(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, middleIndex); String freshGroup = freshDataIdGroupPair.substring(middleIndex + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>); ConcurrentHashMap<String, CacheData> cacheDatas = cache.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>(freshDataId); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> == cacheDatas) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">continue</span>; } CacheData cacheData = cacheDatas.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>(freshGroup); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> == cacheData) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">continue</span>; } receiveConfigInfo(cacheData); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>
說明:
1、通過HttpClient方式從服務端獲取更新過的dataId和groupId集合。
2、根據dataId和groupId再從服務端將相應變化的資料獲取下來。
3、通知客戶端註冊的listener程式。
上面二種方式通知客戶端的listener程式,都是通過allListeners這個屬性獲取的
<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> final ConcurrentMap<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;"><</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* dataId + group */</span>, CopyOnWriteArrayList<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;"><</span>ManagerListener<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">></span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* listeners */</span><span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">></span> allListeners <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">new</span> ConcurrentHashMap<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;"><</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span>, CopyOnWriteArrayList<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;"><</span>ManagerListener<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">>></span>();</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
這行程式碼就是在最開始的那個客戶端使用的例子中註冊在allListeners中的。
七、Diamond客戶端與服務端互動時序圖