[原始碼分析] Dynomite 分散式儲存引擎 之 DynoJedisClient(1)
阿新 • • 發佈:2021-02-04
# [原始碼分析] 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