1. 程式人生 > >[原始碼分析] Dynomite 分散式儲存引擎 之 DynoJedisClient(1)

[原始碼分析] Dynomite 分散式儲存引擎 之 DynoJedisClient(1)

# [原始碼分析] Dynomite 分散式儲存引擎 之 DynoJedisClient(1) [toc] ## 0x00 摘要 前面我們有文章介紹了Amazon Dynamo系統架構 和 NetFlix Dynomite。 我們今天來看看 NetFlix Dynomite 的 Java 客戶端 DynoJedisClient 如何實現。分析客戶端是因為,此客戶端的作用很類似於叢集master,其思路是:java驅動提供多個策略介面,可以用來驅動程式行為調優。包括負載均衡,重試請求,管理節點連線等等。 因為 Dynomite 對於本文來說,過於龐大&底層,而且 DynoJedisClient 與 Dynomite 耦合過於緊密, 所以我們從最簡單的功能點出發看看 DynoJedisClient,於是我們可以想到的功能點是: - 如何提供基本功能,即提供資料庫連線池; - 如何管理節點連線; - 如何拓撲感知; - 如何負載均衡; - 如何故障轉移; - 故障轉移; 所以我們接下來就圍繞這些基本功能點進行分析。 ## 0x01 背景概念 ### 1.1 Amazon Dynamo 亞馬遜在業務發展期間面臨一些問題,主要受限於關係型資料庫的可擴充套件性和高可用性,因此研發了一套新的、基於 `KV` 儲存模型的資料庫,將之命名為 `Dynamo`,其主要採取完全的分散式、去中心化的架構。 相較於傳統的關係型資料庫 `MySQL`,`Dynamo` 的功能目標與之有一些細小的差別,例如: `Amazon` 的業務場景多數情況並不需要支援複雜查詢,卻要求必要的單節點故障容錯性、資料最終一致性(即犧牲資料強一致優先保障可用性)、較強的可擴充套件性等。 ### 1.2 NetFlix Dynomite Dynomite 是 NetFlix 對亞馬遜分散式儲存引擎 Dynamo 的一個開源通用實現,它不僅支援基於記憶體的 K/V 資料庫,還支援持久化的 Mysql、BerkeleyDb、LevelDb 等資料庫,並具有簡單、高效、支援跨資料中心的資料複製等優點。 Dynomite 的最終目標是提供資料庫儲存引擎不能提供的簡單、高效、跨資料中心的資料複製功能。目前,Dynomite 已經實現了對 Redis 和 Memcached 的支援。 ## 0x02 Netflix選型思路 Netflix選擇Dynomite,是因為: - 其具有效能,多資料中心複製和高可用性
的特點; - Dynomite提供分片和可插拔的資料儲存引擎,允許在資料需求增加垂直和水平擴充套件; - Dynomite在Redis之上提供了高可用性、對等複製以及一致性等特性,用於構建分散式叢集佇列。 - Dyno為持久連線提供連線池; - Dyno可以為連線池配置為拓撲感知; - 故障轉移:Dyno為應用程式提供特定的本地機架,us-east-1a的客戶端將連線到相同區域的Dynomite/Redis節點,除非該節點不可用,在這種情況下該客戶端將進行故障轉移。這個屬性被用於通過區域劃分佇列。 Dynomite對於本文來說,過於底層。 所以我們重點就看看 DynoJedisClient 如何實現後面幾點,當然,這幾點其實也無法脫離Dynomite,我們只是力爭剝離出來
。 ## 0x03 基礎知識 ### 3.1 Data Center Data Center 是由多個Rack組成的邏輯集合。 Data Center 可以是一個機房或者一個區域的裝置組合。 ### 3.2 Rack 這是一個邏輯集合,有多個彼此臨近node的組成。比如一個機架上的所有物理機器。可簡單的理解為存放伺服器的機櫃。 資料中心與機架是什麼關係呢?N:1,1:N,M:N。 - 如果只需要幾臺伺服器就能滿足業務需求,這些伺服器至少有2個數據中心,那這種情況下多個數據中心可以放在1個機架上,不過這種情況對資料災備來說是不太保險的。 - 第2種情況是1個數據中心相當於1個機房,那機房裡會有多個機架。 - 第3種情況M:N為多個機房的多個數據中心置於多個機架上。 #### 3.2 Rings and Tokens 由叢集管理的資料就是一個環。環中的每個節點被分配一個或多個由token描述的資料範圍,確定在環中的位置。 Token是用於標識每個分割槽的64位整數ID,範圍是-2^63 -- 2^63-1。通過hash演算法計算partition key的hash值,以此確定存放在哪個節點。 Token也決定了每個節點儲存的資料的分佈範圍,每個節點儲存的資料的key在(前一個節點Token,本節點Token]的半開半閉區間內,所有的節點形成一個首尾相接的環。 ## 0x04 需求 & 思路 因為要為上層遮蔽資訊,所以 DynoJedisClient 就需要應對各種複雜資訊,需要對系統有深刻的瞭解,比如: - 如何維護連線,為持久連線提供連線池; - 如何維護拓撲; - 如何負載均衡; - 如何故障轉移; - 如何自動重試及發現,比如自動重試掛掉的主機。自動發現叢集中的其他主機。 - 如何監控底層機架狀態; 因此,DynoJedisClient 的思路是:java驅動提供多個策略介面,可以用來驅動程式行為調優。包括負載均衡,重試請求,管理節點連線等等
。 ## 0x05 使用 示例程式碼如下: ```java public static void main(String[] args) throws IOException { final String clusterName = args[0]; int version = Integer.parseInt(args[1]); final DynoQueueDemo demo = new DynoQueueDemo(clusterName, "us-east-1e"); Properties props = new Properties(); props.load(DynoQueueDemo.class.getResourceAsStream("/demo.properties")); for (String name : props.stringPropertyNames()) { System.setProperty(name, props.getProperty(name)); } try { demo.initWithRemoteClusterFromEurekaUrl(args[0], 8102, false); if (version == 1) { demo.runSimpleV1Demo(demo.client); } else if (version == 2) { demo.runSimpleV2QueueDemo(demo.client); } Thread.sleep(10000); } catch (Exception ex) { ex.printStackTrace(); } finally { demo.stop(); logger.info("Done"); } } ``` 以及輔助函式: ```java public void initWithRemoteClusterFromEurekaUrl(final String clusterName, final int port, boolean lock) throws Exception { initWithRemoteCluster(clusterName, getHostsFromDiscovery(clusterName), port, lock); } private void initWithRemoteCluster(String clusterName, fi