1. 分散式快取
1.1. Redis 有什麼資料型別?分別用於什麼場景?
資料型別 | 可以儲存的值 | 操作 |
---|---|---|
STRING | 字串、整數或者浮點數 | 對整個字串或者字串的其中一部分執行操作 對整數和浮點數執行自增或者自減操作 |
LIST | 列表 | 從兩端壓入或者彈出元素 讀取單個或者多個元素 進行修剪,只保留一個範圍內的元素 |
SET | 無序集合 | 新增、獲取、移除單個元素 檢查一個元素是否存在於集合中 計算交集、並集、差集 從集合裡面隨機獲取元素 |
HASH | 包含鍵值對的無序雜湊表 | 新增、獲取、移除單個鍵值對 獲取所有鍵值對 檢查某個鍵是否存在 |
ZSET | 有序集合 | 新增、獲取、刪除元素 根據分值範圍或者成員來獲取元素 計算一個鍵的排名 |
1.2. Redis 的主從複製是如何實現的?
- 從伺服器連線主伺服器,傳送 SYNC 命令;
- 主伺服器接收到 SYNC 命名後,開始執行 BGSAVE 命令生成 RDB 檔案並使用緩衝區記錄此後執行的所有寫命令;
- 主伺服器 BGSAVE 執行完後,向所有從伺服器傳送快照檔案,並在傳送期間繼續記錄被執行的寫命令;
- 從伺服器收到快照檔案後丟棄所有舊資料,載入收到的快照;
- 主伺服器快照傳送完畢後開始向從伺服器傳送緩衝區中的寫命令;
- 從伺服器完成對快照的載入,開始接收命令請求,並執行來自主伺服器緩衝區的寫命令;
1.3. Redis 的 key 是如何定址的?
背景
(1)redis 中的每一個資料庫,都由一個 redisDb 的結構儲存。其中:
- redisDb.id 儲存著 redis 資料庫以整數表示的號碼。
- redisDb.dict 儲存著該庫所有的鍵值對資料。
- redisDb.expires 儲存著每一個鍵的過期時間。
(2)當 redis 伺服器初始化時,會預先分配 16 個資料庫(該數量可以通過配置檔案配置),所有資料庫儲存到結構 redisServer 的一個成員 redisServer.db 陣列中。當我們選擇資料庫 select number 時,程式直接通過 redisServer.db[number] 來切換資料庫。有時候當程式需要知道自己是在哪個資料庫時,直接讀取 redisDb.id 即可。
(3)redis 的字典使用雜湊表作為其底層實現。dict 型別使用的兩個指向雜湊表的指標,其中 0 號雜湊表(ht[0])主要用於儲存資料庫的所有鍵值,而 1 號雜湊表主要用於程式對 0 號雜湊表進行 rehash 時使用,rehash 一般是在新增新值時會觸發,這裡不做過多的贅述。所以 redis 中查詢一個 key,其實就是對進行該 dict 結構中的 ht[0] 進行查詢操作。
(4)既然是雜湊,那麼我們知道就會有雜湊碰撞,那麼當多個鍵雜湊之後為同一個值怎麼辦呢?redis 採取連結串列的方式來儲存多個雜湊碰撞的鍵。也就是說,當根據 key 的雜湊值找到該列表後,如果列表的長度大於 1,那麼我們需要遍歷該連結串列來找到我們所查詢的 key。當然,一般情況下連結串列長度都為是 1,所以時間複雜度可看作 o(1)。
定址 key 的步驟
- 當拿到一個 key 後,redis 先判斷當前庫的 0 號雜湊表是否為空,即:if (dict->ht[0].size == 0)。如果為 true 直接返回 NULL。
- 判斷該 0 號雜湊表是否需要 rehash,因為如果在進行 rehash,那麼兩個表中者有可能儲存該 key。如果正在進行 rehash,將呼叫一次_dictRehashStep 方法,_dictRehashStep 用於對資料庫字典、以及雜湊鍵的字典進行被動 rehash,這裡不作贅述。
- 計算雜湊表,根據當前字典與 key 進行雜湊值的計算。
- 根據雜湊值與當前字典計算雜湊表的索引值。
- 根據索引值在雜湊表中取出連結串列,遍歷該連結串列找到 key 的位置。一般情況,該連結串列長度為 1。
- 當 ht[0] 查詢完了之後,再進行了次 rehash 判斷,如果未在 rehashing,則直接結束,否則對 ht[1]重複 345 步驟。
1.4. Redis 的叢集模式是如何實現的?
Redis Cluster 是 Redis 的分散式解決方案,在 Redis 3.0 版本正式推出的。
Redis Cluster 去中心化,每個節點儲存資料和整個叢集狀態,每個節點都和其他所有節點連線。
Redis Cluster 節點分配
Redis Cluster 特點:
- 所有的 redis 節點彼此互聯(PING-PONG 機制),內部使用二進位制協議優化傳輸速度和頻寬。
- 節點的 fail 是通過叢集中超過半數的節點檢測失效時才生效。
- 客戶端與 redis 節點直連,不需要中間 proxy 層。客戶端不需要連線叢集所有節點,連線叢集中任何一個可用節點即可。
- redis-cluster 把所有的物理節點對映到[0-16383] 雜湊槽 (hash slot)上(不一定是平均分配),cluster 負責維護 node、slot、value。
- Redis 叢集預分好 16384 個桶,當需要在 Redis 叢集中放置一個 key-value 時,根據 CRC16(key) mod 16384 的值,決定將一個 key 放到哪個桶中。
Redis Cluster 主從模式
Redis Cluster 為了保證資料的高可用性,加入了主從模式。
一個主節點對應一個或多個從節點,主節點提供資料存取,從節點則是從主節點拉取資料備份。當這個主節點掛掉後,就會有這個從節點選取一個來充當主節點,從而保證叢集不會掛掉。所以,在叢集建立的時候,一定要為每個主節點都新增了從節點。
Redis Sentinel
Redis Sentinel 用於管理多個 Redis 伺服器,它有三個功能:
- 監控(Monitoring) - Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。
- 提醒(Notification) - 當被監控的某個 Redis 伺服器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程式傳送通知。
- 自動故障遷移(Automatic failover) - 當一個主伺服器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會將失效主伺服器的其中一個從伺服器升級為新的主伺服器, 並讓失效主伺服器的其他從伺服器改為複製新的主伺服器; 當客戶端試圖連線失效的主伺服器時, 叢集也會向客戶端返回新主伺服器的地址, 使得叢集可以使用新主伺服器代替失效伺服器。
Redis 叢集中應該有奇數個節點,所以至少有三個節點。
哨兵監控叢集中的主伺服器出現故障時,需要根據 quorum 選舉出一個哨兵來執行故障轉移。選舉需要 majority,即大多數哨兵是執行的(2 個哨兵的 majority=2,3 個哨兵的 majority=2,5 個哨兵的 majority=3,4 個哨兵的 majority=2)。
假設叢集僅僅部署 2 個節點
+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+
複製程式碼
如果 M1 和 S1 所在伺服器當機,則哨兵只有 1 個,無法滿足 majority 來進行選舉,就不能執行故障轉移。
1.5. Redis 如何實現分散式鎖?ZooKeeper 如何實現分散式鎖?比較二者優劣?
分散式鎖的三種實現:
- 基於資料庫實現分散式鎖;
- 基於快取(Redis 等)實現分散式鎖;
- 基於 Zookeeper 實現分散式鎖;
資料庫實現
Redis 實現
- 獲取鎖的時候,使用 setnx 加鎖,並使用 expire 命令為鎖新增一個超時時間,超過該時間則自動釋放鎖,鎖的 value 值為一個隨機生成的 UUID,通過此在釋放鎖的時候進行判斷。
- 獲取鎖的時候還設定一個獲取的超時時間,若超過這個時間則放棄獲取鎖。
- 釋放鎖的時候,通過 UUID 判斷是不是該鎖,若是該鎖,則執行 delete 進行鎖釋放。
ZooKeeper 實現
- 建立一個目錄 mylock;
- 執行緒 A 想獲取鎖就在 mylock 目錄下建立臨時順序節點;
- 獲取 mylock 目錄下所有的子節點,然後獲取比自己小的兄弟節點,如果不存在,則說明當前執行緒順序號最小,獲得鎖;
- 執行緒 B 獲取所有節點,判斷自己不是最小節點,設定監聽比自己次小的節點;
- 執行緒 A 處理完,刪除自己的節點,執行緒 B 監聽到變更事件,判斷自己是不是最小的節點,如果是則獲得鎖。
實現對比
ZooKeeper 具備高可用、可重入、阻塞鎖特性,可解決失效死鎖問題。 但 ZooKeeper 因為需要頻繁的建立和刪除節點,效能上不如 Redis 方式。
1.6. Redis 的持久化方式?有什麼優缺點?持久化實現原理?
RDB 快照(snapshot)
將存在於某一時刻的所有資料都寫入到硬碟中。
快照的原理
在預設情況下,Redis 將資料庫快照儲存在名字為 dump.rdb 的二進位制檔案中。你可以對 Redis 進行設定, 讓它在“N 秒內資料集至少有 M 個改動”這一條件被滿足時, 自動儲存一次資料集。你也可以通過呼叫 SAVE 或者 BGSAVE,手動讓 Redis 進行資料集儲存操作。這種持久化方式被稱為快照。
當 Redis 需要儲存 dump.rdb 檔案時, 伺服器執行以下操作:
- Redis 建立一個子程式。
- 子程式將資料集寫入到一個臨時快照檔案中。
- 當子程式完成對新快照檔案的寫入時,Redis 用新快照檔案替換原來的快照檔案,並刪除舊的快照檔案。
這種工作方式使得 Redis 可以從寫時複製(copy-on-write)機制中獲益。
快照的優點
- 它儲存了某個時間點的資料集,非常適用於資料集的備份。
- 很方便傳送到另一個遠端資料中心或者亞馬遜的 S3(可能加密),非常適用於災難恢復。
- 快照在儲存 RDB 檔案時父程式唯一需要做的就是 fork 出一個子程式,接下來的工作全部由子程式來做,父程式不需要再做其他 IO 操作,所以快照持久化方式可以最大化 redis 的效能。
- 與 AOF 相比,在恢復大的資料集的時候,DB 方式會更快一些。
快照的缺點
- 如果你希望在 redis 意外停止工作(例如電源中斷)的情況下丟失的資料最少的話,那麼快照不適合你。
- 快照需要經常 fork 子程式來儲存資料集到硬碟上。當資料集比較大的時候,fork 的過程是非常耗時的,可能會導致 Redis 在一些毫秒級內不能響應客戶端的請求。
AOF
AOF 持久化方式記錄每次對伺服器執行的寫操作。當伺服器重啟的時候會重新執行這些命令來恢復原始的資料。
AOF 的原理
- Redis 建立一個子程式。
- 子程式開始將新 AOF 檔案的內容寫入到臨時檔案。
- 對於所有新執行的寫入命令,父程式一邊將它們累積到一個記憶體快取中,一邊將這些改動追加到現有 AOF 檔案的末尾,這樣樣即使在重寫的中途發生停機,現有的 AOF 檔案也還是安全的。
- 當子程式完成重寫工作時,它給父程式傳送一個訊號,父程式在接收到訊號之後,將記憶體快取中的所有資料追加到新 AOF 檔案的末尾。
- 搞定!現在 Redis 原子地用新檔案替換舊檔案,之後所有命令都會直接追加到新 AOF 檔案的末尾。
AOF 的優點
- 使用預設的每秒 fsync 策略,Redis 的效能依然很好(fsync 是由後臺執行緒進行處理的,主執行緒會盡力處理客戶端請求),一旦出現故障,使用 AOF ,你最多丟失 1 秒的資料。
- AOF 檔案是一個只進行追加的日誌檔案,所以不需要寫入 seek,即使由於某些原因(磁碟空間已滿,寫的過程中當機等等)未執行完整的寫入命令,你也也可使用 redis-check-aof 工具修復這些問題。
- Redis 可以在 AOF 檔案體積變得過大時,自動地在後臺對 AOF 進行重寫:重寫後的新 AOF 檔案包含了恢復當前資料集所需的最小命令集合。整個重寫操作是絕對安全的。
- AOF 檔案有序地儲存了對資料庫執行的所有寫入操作,這些寫入操作以 Redis 協議的格式儲存。因此 AOF 檔案的內容非常容易被人讀懂,對檔案進行分析(parse)也很輕鬆。
AOF 的缺點
- 對於相同的資料集來說,AOF 檔案的體積通常要大於 RDB 檔案的體積。
- 根據所使用的 fsync 策略,AOF 的速度可能會慢於快照。在一般情況下,每秒 fsync 的效能依然非常高,而關閉 fsync 可以讓 AOF 的速度和快照一樣快,即使在高負荷之下也是如此。不過在處理巨大的寫入載入時,快照可以提供更有保證的最大延遲時間(latency)。
1.7. Redis 過期策略有哪些?
- noeviction - 當記憶體使用達到閾值的時候,所有引起申請記憶體的命令會報錯。
- allkeys-lru - 在主鍵空間中,優先移除最近未使用的 key。
- allkeys-random - 在主鍵空間中,隨機移除某個 key。
- volatile-lru - 在設定了過期時間的鍵空間中,優先移除最近未使用的 key。
- volatile-random - 在設定了過期時間的鍵空間中,隨機移除某個 key。
- volatile-ttl - 在設定了過期時間的鍵空間中,具有更早過期時間的 key 優先移除。
1.8. Redis 和 Memcached 有什麼區別?
兩者都是非關係型記憶體鍵值資料庫。有以下主要不同:
資料型別
- Memcached 僅支援字串型別;
- 而 Redis 支援五種不同種類的資料型別,使得它可以更靈活地解決問題。
資料持久化
- Memcached 不支援持久化;
- Redis 支援兩種持久化策略:RDB 快照和 AOF 日誌。
分散式
- Memcached 不支援分散式,只能通過在客戶端使用像一致性雜湊這樣的分散式演算法來實現分散式儲存,這種方式在儲存和查詢時都需要先在客戶端計算一次資料所在的節點。
- Redis Cluster 實現了分散式的支援。
記憶體管理機制
- Memcached 將記憶體分割成特定長度的塊來儲存資料,以完全解決記憶體碎片的問題,但是這種方式會使得記憶體的利用率不高,例如塊的大小為 128 bytes,只儲存 100 bytes 的資料,那麼剩下的 28 bytes 就浪費掉了。
- 在 Redis 中,並不是所有資料都一直儲存在記憶體中,可以將一些很久沒用的 value 交換到磁碟。而 Memcached 的資料則會一直在記憶體中。
1.9. 為什麼單執行緒的 Redis 效能反而優於多執行緒的 Memcached?
Redis 快速的原因:
- 絕大部分請求是純粹的記憶體操作(非常快速)
- 採用單執行緒,避免了不必要的上下文切換和競爭條件
- 非阻塞 IO
內部實現採用 epoll,採用了 epoll+自己實現的簡單的事件框架。epoll 中的讀、寫、關閉、連線都轉化成了事件,然後利用 epoll 的多路複用特性,絕不在 io 上浪費一點時間。
2. 分散式訊息佇列(MQ)
2.1. 為什麼使用 MQ?
- 非同步處理 - 相比於傳統的序列、並行方式,提高了系統吞吐量。
- 應用解耦 - 系統間通過訊息通訊,不用關心其他系統的處理。
- 流量削鋒 - 可以通過訊息佇列長度控制請求量;可以緩解短時間內的高併發請求。
- 日誌處理 - 解決大量日誌傳輸。
- 訊息通訊 - 訊息佇列一般都內建了高效的通訊機制,因此也可以用在純的訊息通訊。比如實現點對點訊息佇列,或者聊天室等。
2.2. 如何保證 MQ 的高可用?
資料複製
- 將所有 Broker 和待分配的 Partition 排序
- 將第 i 個 Partition 分配到第(i mod n)個 Broker 上
- 將第 i 個 Partition 的第 j 個 Replica 分配到第((i + j) mode n)個 Broker 上
選舉主伺服器
2.3. MQ 有哪些常見問題?如何解決這些問題?
MQ 的常見問題有:
- 訊息的順序問題
- 訊息的重複問題
訊息的順序問題
訊息有序指的是可以按照訊息的傳送順序來消費。
假如生產者產生了 2 條訊息:M1、M2,假定 M1 傳送到 S1,M2 傳送到 S2,如果要保證 M1 先於 M2 被消費,怎麼做?
解決方案:
(1)保證生產者 - MQServer - 消費者是一對一對一的關係
缺陷:
- 並行度就會成為訊息系統的瓶頸(吞吐量不夠)
- 更多的異常處理,比如:只要消費端出現問題,就會導致整個處理流程阻塞,我們不得不花費更多的精力來解決阻塞的問題。
(2)通過合理的設計或者將問題分解來規避。
- 不關注亂序的應用實際大量存在
- 佇列無序並不意味著訊息無序
所以從業務層面來保證訊息的順序而不僅僅是依賴於訊息系統,是一種更合理的方式。
訊息的重複問題
造成訊息重複的根本原因是:網路不可達。
所以解決這個問題的辦法就是繞過這個問題。那麼問題就變成了:如果消費端收到兩條一樣的訊息,應該怎樣處理?
消費端處理訊息的業務邏輯保持冪等性。只要保持冪等性,不管來多少條重複訊息,最後處理的結果都一樣。 保證每條訊息都有唯一編號且保證訊息處理成功與去重表的日誌同時出現。利用一張日誌表來記錄已經處理成功的訊息的 ID,如果新到的訊息 ID 已經在日誌表中,那麼就不再處理這條訊息。
2.4. Kafka, ActiveMQ, RabbitMQ, RocketMQ 各有什麼優缺點?
3. 分散式服務(RPC)
3.1. Dubbo 的實現過程?
節點角色:
節點 | 角色說明 |
---|---|
Provider | 暴露服務的服務提供方 |
Consumer | 呼叫遠端服務的服務消費方 |
Registry | 服務註冊與發現的註冊中心 |
Monitor | 統計服務的呼叫次數和呼叫時間的監控中心 |
Container | 服務執行容器 |
呼叫關係:
- 務容器負責啟動,載入,執行服務提供者。
- 服務提供者在啟動時,向註冊中心註冊自己提供的服務。
- 服務消費者在啟動時,向註冊中心訂閱自己所需的服務。
- 註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連線推送變更資料給消費者。
- 服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一臺提供者進行呼叫,如果呼叫失敗,再選另一臺呼叫。
- 服務消費者和提供者,在記憶體中累計呼叫次數和呼叫時間,定時每分鐘傳送一次統計資料到監控中心。
3.2. Dubbo 負載均衡策略有哪些?
Random
- 隨機,按權重設定隨機概率。
- 在一個截面上碰撞的概率高,但呼叫量越大分佈越均勻,而且按概率使用權重後也比較均勻,有利於動態調整提供者權重。
RoundRobin
- 輪循,按公約後的權重設定輪循比率。
- 存在慢的提供者累積請求的問題,比如:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,久而久之,所有請求都卡在調到第二臺上。
LeastActive
- 最少活躍呼叫數,相同活躍數的隨機,活躍數指呼叫前後計數差。
- 使慢的提供者收到更少請求,因為越慢的提供者的呼叫前後計數差會越大。
ConsistentHash
- 一致性 Hash,相同引數的請求總是發到同一提供者。
- 當某一臺提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
- 預設只對第一個引數 Hash,如果要修改,請配置
<dubbo:parameter key="hash.arguments" value="0,1" />
- 預設用 160 份虛擬節點,如果要修改,請配置
<dubbo:parameter key="hash.nodes" value="320" />
3.3. Dubbo 叢集容錯策略 ?
- Failover - 失敗自動切換,當出現失敗,重試其它伺服器。通常用於讀操作,但重試會帶來更長延遲。可通過 retries="2" 來設定重試次數(不含第一次)。
- Failfast - 快速失敗,只發起一次呼叫,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄。
- Failsafe - 失敗安全,出現異常時,直接忽略。通常用於寫入審計日誌等操作。
- Failback - 失敗自動恢復,後臺記錄失敗請求,定時重發。通常用於訊息通知操作。
- Forking - 並行呼叫多個伺服器,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。可通過 forks="2" 來設定最大並行數。
- Broadcast - 播呼叫所有提供者,逐個呼叫,任意一臺報錯則報錯。通常用於通知所有提供者更新快取或日誌等本地資源資訊。
3.4. 動態代理策略?
Dubbo 作為 RPC 框架,首先要完成的就是跨系統,跨網路的服務呼叫。消費方與提供方遵循統一的介面定義,消費方呼叫介面時,Dubbo 將其轉換成統一格式的資料結構,通過網路傳輸,提供方根據規則找到介面實現,通過反射完成呼叫。也就是說,消費方獲取的是對遠端服務的一個代理(Proxy),而提供方因為要支援不同的介面實現,需要一個包裝層(Wrapper)。呼叫的過程大概是這樣:
消費方的 Proxy 和提供方的 Wrapper 得以讓 Dubbo 構建出複雜、統一的體系。而這種動態代理與包裝也是通過基於 SPI 的外掛方式實現的,它的介面就是ProxyFactory。
@SPI("javassist")
public interface ProxyFactory {
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker) throws RpcException;
@Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
複製程式碼
ProxyFactory 有兩種實現方式,一種是基於 JDK 的代理實現,一種是基於 javassist 的實現。ProxyFactory 介面上定義了@SPI("javassist"),預設為 javassist 的實現。
3.5. Dubbo 支援哪些序列化協議?Hessian?Hessian 的資料結構?
- dubbo 序列化,阿里尚不成熟的 java 序列化實現。
- hessian2 序列化:hessian 是一種跨語言的高效二進位制的序列化方式,但這裡實際不是原生的 hessian2 序列化,而是阿里修改過的 hessian lite,它是 dubbo RPC 預設啟用的序列化方式。
- json 序列化:目前有兩種實現,一種是採用的阿里的 fastjson 庫,另一種是採用 dubbo 中自已實現的簡單 json 庫,一般情況下,json 這種文字序列化效能不如二進位制序列化。
- java 序列化:主要是採用 JDK 自帶的 java 序列化實現,效能很不理想。
- Kryo 和 FST:Kryo 和 FST 的效能依然普遍優於 hessian 和 dubbo 序列化。
Hessian 序列化與 Java 預設的序列化區別?
Hessian 是一個輕量級的 remoting on http 工具,採用的是 Binary RPC 協議,所以它很適合於傳送二進位制資料,同時又具有防火牆穿透能力。
- Hessian 支援跨語言序列
- 比 java 序列化具有更好的效能和易用性
- 支援的語言比較多
3.6. Protoco Buffer 是什麼?
Protocol Buffer 是 Google 出品的一種輕量 & 高效的結構化資料儲存格式,效能比 Json、XML 真的強!太!多!
Protocol Buffer 的序列化 & 反序列化簡單 & 速度快的原因是:
- 編碼 / 解碼 方式簡單(只需要簡單的數學運算 = 位移等等)
- 採用 Protocol Buffer 自身的框架程式碼 和 編譯器 共同完成
Protocol Buffer 的資料壓縮效果好(即序列化後的資料量體積小)的原因是:
- 採用了獨特的編碼方式,如 Varint、Zigzag 編碼方式等等
- 採用 T - L - V 的資料儲存方式:減少了分隔符的使用 & 資料儲存得緊湊
3.7. 註冊中心掛了可以繼續通訊嗎?
可以。Dubbo 消費者在應用啟動時會從註冊中心拉取已註冊的生產者的地址介面,並快取在本地。每次呼叫時,按照本地儲存的地址進行呼叫。
3.8. ZooKeeper 原理是什麼?ZooKeeper 有什麼用?
ZooKeeper 是一個分散式應用協調系統,已經用到了許多分散式專案中,用來完成統一命名服務、狀態同步服務、叢集管理、分散式應用配置項的管理等工作。
- 每個 Server 在記憶體中儲存了一份資料;
- Zookeeper 啟動時,將從例項中選舉一個 leader(Paxos 協議);
- Leader 負責處理資料更新等操作(Zab 協議);
- 一個更新操作成功,當且僅當大多數 Server 在記憶體中成功修改資料。
3.9. Netty 有什麼用?NIO/BIO/AIO 有什麼用?有什麼區別?
Netty 是一個“網路通訊框架”。
Netty 進行事件處理的流程。Channel
是連線的通道,是 ChannelEvent 的產生者,而ChannelPipeline
可以理解為 ChannelHandler 的集合。
IO 的方式通常分為幾種:
- 同步阻塞的 BIO
- 同步非阻塞的 NIO
- 非同步非阻塞的 AIO
在使用同步 I/O 的網路應用中,如果要同時處理多個客戶端請求,或是在客戶端要同時和多個伺服器進行通訊,就必須使用多執行緒來處理。
NIO 基於 Reactor,當 socket 有流可讀或可寫入 socket 時,作業系統會相應的通知引用程式進行處理,應用再將流讀取到緩衝區或寫入作業系統。也就是說,這個時候,已經不是一個連線就要對應一個處理執行緒了,而是有效的請求,對應一個執行緒,當連線沒有資料時,是沒有工作執行緒來處理的。
與 NIO 不同,當進行讀寫操作時,只須直接呼叫 API 的 read 或 write 方法即可。這兩種方法均為非同步的,對於讀操作而言,當有流可讀取時,作業系統會將可讀的流傳入 read 方法的緩衝區,並通知應用程式;對於寫操作而言,當作業系統將 write 方法傳遞的流寫入完畢時,作業系統主動通知應用程式。 即可以理解為,read/write 方法都是非同步的,完成後會主動呼叫回撥函式。
3.10. 為什麼要進行系統拆分?拆分不用 Dubbo 可以嗎?
系統拆分從資源角度分為:應用拆分和資料庫拆分。
從採用的先後順序可分為:水平擴充套件、垂直拆分、業務拆分、水平拆分。
是否使用服務依據實際業務場景來決定。
當垂直應用越來越多,應用之間互動不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提高業務複用及整合的分散式服務框架(RPC)是關鍵。
當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個排程中心基於訪問壓力實時管理叢集容量,提高叢集利用率。此時,用於提高機器利用率的資源排程和治理中心(SOA)是關鍵。
3.11. Dubbo 和 Thrift 有什麼區別?
- Thrift 是跨語言的 RPC 框架。
- Dubbo 支援服務治理,而 Thrift 不支援。
免費Java資料需要自己領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高併發分散式等教程,一共30G。
傳送門: mp.weixin.qq.com/s/JzddfH-7y…