這篇文章主要介紹“Redis分布式技術(shù)面試題有哪些”的相關(guān)知識,小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望這篇“Redis分布式技術(shù)面試題有哪些”文章能幫助大家解決問題。
成都創(chuàng)新互聯(lián)公司是一家專注于成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作與策劃設(shè)計(jì),滎陽網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設(shè)十余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:滎陽等地區(qū)。滎陽做網(wǎng)站價(jià)格咨詢:18982081108數(shù)據(jù)類型 | 可以存儲的值 | 操作 |
---|---|---|
STRING | 字符串、整數(shù)或者浮點(diǎn)數(shù) | 對整個(gè)字符串或者字符串的其中一部分執(zhí)行操作 對整數(shù)和浮點(diǎn)數(shù)執(zhí)行自增或者自減操作 |
LIST | 列表 | 從兩端壓入或者彈出元素 讀取單個(gè)或者多個(gè)元素 進(jìn)行修剪,只保留一個(gè)范圍內(nèi)的元素 |
SET | 無序集合 | 添加、獲取、移除單個(gè)元素 檢查一個(gè)元素是否存在于集合中 計(jì)算交集、并集、差集 從集合里面隨機(jī)獲取元素 |
HASH | 包含鍵值對的無序散列表 | 添加、獲取、移除單個(gè)鍵值對 獲取所有鍵值對 檢查某個(gè)鍵是否存在 |
ZSET | 有序集合 | 添加、獲取、刪除元素 根據(jù)分值范圍或者成員來獲取元素 計(jì)算一個(gè)鍵的排名 |
What Redis data structures look like
從服務(wù)器連接主服務(wù)器,發(fā)送 SYNC 命令;
主服務(wù)器接收到 SYNC 命名后,開始執(zhí)行 BGSAVE 命令生成 RDB 文件并使用緩沖區(qū)記錄此后執(zhí)行的所有寫命令;
主服務(wù)器 BGSAVE 執(zhí)行完后,向所有從服務(wù)器發(fā)送快照文件,并在發(fā)送期間繼續(xù)記錄被執(zhí)行的寫命令;
從服務(wù)器收到快照文件后丟棄所有舊數(shù)據(jù),載入收到的快照;
主服務(wù)器快照發(fā)送完畢后開始向從服務(wù)器發(fā)送緩沖區(qū)中的寫命令;
從服務(wù)器完成對快照的載入,開始接收命令請求,并執(zhí)行來自主服務(wù)器緩沖區(qū)的寫命令;
(1)redis 中的每一個(gè)數(shù)據(jù)庫,都由一個(gè) redisDb 的結(jié)構(gòu)存儲。其中:
redisDb.id 存儲著 redis 數(shù)據(jù)庫以整數(shù)表示的號碼。
redisDb.dict 存儲著該庫所有的鍵值對數(shù)據(jù)。
redisDb.expires 保存著每一個(gè)鍵的過期時(shí)間。
(2)當(dāng) redis 服務(wù)器初始化時(shí),會預(yù)先分配 16 個(gè)數(shù)據(jù)庫(該數(shù)量可以通過配置文件配置),所有數(shù)據(jù)庫保存到結(jié)構(gòu) redisServer 的一個(gè)成員 redisServer.db 數(shù)組中。當(dāng)我們選擇數(shù)據(jù)庫 select number 時(shí),程序直接通過 redisServer.db[number] 來切換數(shù)據(jù)庫。有時(shí)候當(dāng)程序需要知道自己是在哪個(gè)數(shù)據(jù)庫時(shí),直接讀取 redisDb.id 即可。
(3)redis 的字典使用哈希表作為其底層實(shí)現(xiàn)。dict 類型使用的兩個(gè)指向哈希表的指針,其中 0 號哈希表(ht[0])主要用于存儲數(shù)據(jù)庫的所有鍵值,而 1 號哈希表主要用于程序?qū)?0 號哈希表進(jìn)行 rehash 時(shí)使用,rehash 一般是在添加新值時(shí)會觸發(fā),這里不做過多的贅述。所以 redis 中查找一個(gè) key,其實(shí)就是對進(jìn)行該 dict 結(jié)構(gòu)中的 ht[0] 進(jìn)行查找操作。
(4)既然是哈希,那么我們知道就會有哈希碰撞,那么當(dāng)多個(gè)鍵哈希之后為同一個(gè)值怎么辦呢?redis 采取鏈表的方式來存儲多個(gè)哈希碰撞的鍵。也就是說,當(dāng)根據(jù) key 的哈希值找到該列表后,如果列表的長度大于 1,那么我們需要遍歷該鏈表來找到我們所查找的 key。當(dāng)然,一般情況下鏈表長度都為是 1,所以時(shí)間復(fù)雜度可看作 o(1)。
當(dāng)拿到一個(gè) key 后,redis 先判斷當(dāng)前庫的 0 號哈希表是否為空,即:if (dict->ht[0].size == 0)。如果為 true 直接返回 NULL。
判斷該 0 號哈希表是否需要 rehash,因?yàn)槿绻谶M(jìn)行 rehash,那么兩個(gè)表中者有可能存儲該 key。如果正在進(jìn)行 rehash,將調(diào)用一次_dictRehashStep 方法,_dictRehashStep 用于對數(shù)據(jù)庫字典、以及哈希鍵的字典進(jìn)行被動(dòng) rehash,這里不作贅述。
計(jì)算哈希表,根據(jù)當(dāng)前字典與 key 進(jìn)行哈希值的計(jì)算。
根據(jù)哈希值與當(dāng)前字典計(jì)算哈希表的索引值。
根據(jù)索引值在哈希表中取出鏈表,遍歷該鏈表找到 key 的位置。一般情況,該鏈表長度為 1。
當(dāng) ht[0] 查找完了之后,再進(jìn)行了次 rehash 判斷,如果未在 rehashing,則直接結(jié)束,否則對 ht[1]重復(fù) 345 步驟。
Redis Cluster 是 Redis 的分布式解決方案,在 Redis 3.0 版本正式推出的。
Redis Cluster 去中心化,每個(gè)節(jié)點(diǎn)保存數(shù)據(jù)和整個(gè)集群狀態(tài),每個(gè)節(jié)點(diǎn)都和其他所有節(jié)點(diǎn)連接。
Redis Cluster 特點(diǎn):
所有的 redis 節(jié)點(diǎn)彼此互聯(lián)(PING-PONG 機(jī)制),內(nèi)部使用二進(jìn)制協(xié)議優(yōu)化傳輸速度和帶寬。
節(jié)點(diǎn)的 fail 是通過集群中超過半數(shù)的節(jié)點(diǎn)檢測失效時(shí)才生效。
客戶端與 redis 節(jié)點(diǎn)直連,不需要中間 proxy 層??蛻舳瞬恍枰B接集群所有節(jié)點(diǎn),連接集群中任何一個(gè)可用節(jié)點(diǎn)即可。
redis-cluster 把所有的物理節(jié)點(diǎn)映射到[0-16383] 哈希槽 (hash slot)上(不一定是平均分配),cluster 負(fù)責(zé)維護(hù) node、slot、value。
Redis 集群預(yù)分好 16384 個(gè)桶,當(dāng)需要在 Redis 集群中放置一個(gè) key-value 時(shí),根據(jù) CRC16(key) mod 16384 的值,決定將一個(gè) key 放到哪個(gè)桶中。
Redis Cluster 為了保證數(shù)據(jù)的高可用性,加入了主從模式。
一個(gè)主節(jié)點(diǎn)對應(yīng)一個(gè)或多個(gè)從節(jié)點(diǎn),主節(jié)點(diǎn)提供數(shù)據(jù)存取,從節(jié)點(diǎn)則是從主節(jié)點(diǎn)拉取數(shù)據(jù)備份。當(dāng)這個(gè)主節(jié)點(diǎn)掛掉后,就會有這個(gè)從節(jié)點(diǎn)選取一個(gè)來充當(dāng)主節(jié)點(diǎn),從而保證集群不會掛掉。所以,在集群建立的時(shí)候,一定要為每個(gè)主節(jié)點(diǎn)都添加了從節(jié)點(diǎn)。
Redis Sentinel 用于管理多個(gè) Redis 服務(wù)器,它有三個(gè)功能:
監(jiān)控(Monitoring) - Sentinel 會不斷地檢查你的主服務(wù)器和從服務(wù)器是否運(yùn)作正常。
提醒(Notification) - 當(dāng)被監(jiān)控的某個(gè) Redis 服務(wù)器出現(xiàn)問題時(shí), Sentinel 可以通過 API 向管理員或者其他應(yīng)用程序發(fā)送通知。
自動(dòng)故障遷移(Automatic failover) - 當(dāng)一個(gè)主服務(wù)器不能正常工作時(shí), Sentinel 會開始一次自動(dòng)故障遷移操作, 它會將失效主服務(wù)器的其中一個(gè)從服務(wù)器升級為新的主服務(wù)器, 并讓失效主服務(wù)器的其他從服務(wù)器改為復(fù)制新的主服務(wù)器; 當(dāng)客戶端試圖連接失效的主服務(wù)器時(shí), 集群也會向客戶端返回新主服務(wù)器的地址, 使得集群可以使用新主服務(wù)器代替失效服務(wù)器。
Redis 集群中應(yīng)該有奇數(shù)個(gè)節(jié)點(diǎn),所以至少有三個(gè)節(jié)點(diǎn)。
哨兵監(jiān)控集群中的主服務(wù)器出現(xiàn)故障時(shí),需要根據(jù) quorum 選舉出一個(gè)哨兵來執(zhí)行故障轉(zhuǎn)移。選舉需要 majority,即大多數(shù)哨兵是運(yùn)行的(2 個(gè)哨兵的 majority=2,3 個(gè)哨兵的 majority=2,5 個(gè)哨兵的 majority=3,4 個(gè)哨兵的 majority=2)。
假設(shè)集群僅僅部署 2 個(gè)節(jié)點(diǎn)
+----+ +----+| M1 |---------| R1 || S1 | | S2 |+----+ +----+
如果 M1 和 S1 所在服務(wù)器宕機(jī),則哨兵只有 1 個(gè),無法滿足 majority 來進(jìn)行選舉,就不能執(zhí)行故障轉(zhuǎn)移。
分布式鎖的三種實(shí)現(xiàn):
基于數(shù)據(jù)庫實(shí)現(xiàn)分布式鎖;
基于緩存(Redis 等)實(shí)現(xiàn)分布式鎖;
基于 Zookeeper 實(shí)現(xiàn)分布式鎖;
獲取鎖的時(shí)候,使用 setnx 加鎖,并使用 expire 命令為鎖添加一個(gè)超時(shí)時(shí)間,超過該時(shí)間則自動(dòng)釋放鎖,鎖的 value 值為一個(gè)隨機(jī)生成的 UUID,通過此在釋放鎖的時(shí)候進(jìn)行判斷。
獲取鎖的時(shí)候還設(shè)置一個(gè)獲取的超時(shí)時(shí)間,若超過這個(gè)時(shí)間則放棄獲取鎖。
釋放鎖的時(shí)候,通過 UUID 判斷是不是該鎖,若是該鎖,則執(zhí)行 delete 進(jìn)行鎖釋放。
創(chuàng)建一個(gè)目錄 mylock;
線程 A 想獲取鎖就在 mylock 目錄下創(chuàng)建臨時(shí)順序節(jié)點(diǎn);
獲取 mylock 目錄下所有的子節(jié)點(diǎn),然后獲取比自己小的兄弟節(jié)點(diǎn),如果不存在,則說明當(dāng)前線程順序號最小,獲得鎖;
線程 B 獲取所有節(jié)點(diǎn),判斷自己不是最小節(jié)點(diǎn),設(shè)置監(jiān)聽比自己次小的節(jié)點(diǎn);
線程 A 處理完,刪除自己的節(jié)點(diǎn),線程 B 監(jiān)聽到變更事件,判斷自己是不是最小的節(jié)點(diǎn),如果是則獲得鎖。
ZooKeeper 具備高可用、可重入、阻塞鎖特性,可解決失效死鎖問題。 但 ZooKeeper 因?yàn)樾枰l繁的創(chuàng)建和刪除節(jié)點(diǎn),性能上不如 Redis 方式。
將存在于某一時(shí)刻的所有數(shù)據(jù)都寫入到硬盤中。
快照的原理
在默認(rèn)情況下,Redis 將數(shù)據(jù)庫快照保存在名字為 dump.rdb 的二進(jìn)制文件中。你可以對 Redis 進(jìn)行設(shè)置, 讓它在“N 秒內(nèi)數(shù)據(jù)集至少有 M 個(gè)改動(dòng)”這一條件被滿足時(shí), 自動(dòng)保存一次數(shù)據(jù)集。你也可以通過調(diào)用 SAVE 或者 BGSAVE,手動(dòng)讓 Redis 進(jìn)行數(shù)據(jù)集保存操作。這種持久化方式被稱為快照。
當(dāng) Redis 需要保存 dump.rdb 文件時(shí), 服務(wù)器執(zhí)行以下操作:
Redis 創(chuàng)建一個(gè)子進(jìn)程。
子進(jìn)程將數(shù)據(jù)集寫入到一個(gè)臨時(shí)快照文件中。
當(dāng)子進(jìn)程完成對新快照文件的寫入時(shí),Redis 用新快照文件替換原來的快照文件,并刪除舊的快照文件。
這種工作方式使得 Redis 可以從寫時(shí)復(fù)制(copy-on-write)機(jī)制中獲益。
快照的優(yōu)點(diǎn)
它保存了某個(gè)時(shí)間點(diǎn)的數(shù)據(jù)集,非常適用于數(shù)據(jù)集的備份。
很方便傳送到另一個(gè)遠(yuǎn)端數(shù)據(jù)中心或者亞馬遜的 S3(可能加密),非常適用于災(zāi)難恢復(fù)。
快照在保存 RDB 文件時(shí)父進(jìn)程唯一需要做的就是 fork 出一個(gè)子進(jìn)程,接下來的工作全部由子進(jìn)程來做,父進(jìn)程不需要再做其他 IO 操作,所以快照持久化方式可以大化 redis 的性能。
與 AOF 相比,在恢復(fù)大的數(shù)據(jù)集的時(shí)候,DB 方式會更快一些。
快照的缺點(diǎn)
如果你希望在 redis 意外停止工作(例如電源中斷)的情況下丟失的數(shù)據(jù)最少的話,那么快照不適合你。
快照需要經(jīng)常 fork 子進(jìn)程來保存數(shù)據(jù)集到硬盤上。當(dāng)數(shù)據(jù)集比較大的時(shí)候,fork 的過程是非常耗時(shí)的,可能會導(dǎo)致 Redis 在一些毫秒級內(nèi)不能響應(yīng)客戶端的請求。
AOF 持久化方式記錄每次對服務(wù)器執(zhí)行的寫操作。當(dāng)服務(wù)器重啟的時(shí)候會重新執(zhí)行這些命令來恢復(fù)原始的數(shù)據(jù)。
Redis 創(chuàng)建一個(gè)子進(jìn)程。
子進(jìn)程開始將新 AOF 文件的內(nèi)容寫入到臨時(shí)文件。
對于所有新執(zhí)行的寫入命令,父進(jìn)程一邊將它們累積到一個(gè)內(nèi)存緩存中,一邊將這些改動(dòng)追加到現(xiàn)有 AOF 文件的末尾,這樣樣即使在重寫的中途發(fā)生停機(jī),現(xiàn)有的 AOF 文件也還是安全的。
當(dāng)子進(jìn)程完成重寫工作時(shí),它給父進(jìn)程發(fā)送一個(gè)信號,父進(jìn)程在接收到信號之后,將內(nèi)存緩存中的所有數(shù)據(jù)追加到新 AOF 文件的末尾。
搞定!現(xiàn)在 Redis 原子地用新文件替換舊文件,之后所有命令都會直接追加到新 AOF 文件的末尾。
使用默認(rèn)的每秒 fsync 策略,Redis 的性能依然很好(fsync 是由后臺線程進(jìn)行處理的,主線程會盡力處理客戶端請求),一旦出現(xiàn)故障,使用 AOF ,你最多丟失 1 秒的數(shù)據(jù)。
AOF 文件是一個(gè)只進(jìn)行追加的日志文件,所以不需要寫入 seek,即使由于某些原因(磁盤空間已滿,寫的過程中宕機(jī)等等)未執(zhí)行完整的寫入命令,你也也可使用 redis-check-aof 工具修復(fù)這些問題。
Redis 可以在 AOF 文件體積變得過大時(shí),自動(dòng)地在后臺對 AOF 進(jìn)行重寫:重寫后的新 AOF 文件包含了恢復(fù)當(dāng)前數(shù)據(jù)集所需的最小命令集合。整個(gè)重寫操作是絕對安全的。
AOF 文件有序地保存了對數(shù)據(jù)庫執(zhí)行的所有寫入操作,這些寫入操作以 Redis 協(xié)議的格式保存。因此 AOF 文件的內(nèi)容非常容易被人讀懂,對文件進(jìn)行分析(parse)也很輕松。
對于相同的數(shù)據(jù)集來說,AOF 文件的體積通常要大于 RDB 文件的體積。
根據(jù)所使用的 fsync 策略,AOF 的速度可能會慢于快照。在一般情況下,每秒 fsync 的性能依然非常高,而關(guān)閉 fsync 可以讓 AOF 的速度和快照一樣快,即使在高負(fù)荷之下也是如此。不過在處理巨大的寫入載入時(shí),快照可以提供更有保證的大延遲時(shí)間(latency)。
noeviction - 當(dāng)內(nèi)存使用達(dá)到閾值的時(shí)候,所有引起申請內(nèi)存的命令會報(bào)錯(cuò)。
allkeys-lru - 在主鍵空間中,優(yōu)先移除最近未使用的 key。
allkeys-random - 在主鍵空間中,隨機(jī)移除某個(gè) key。
volatile-lru - 在設(shè)置了過期時(shí)間的鍵空間中,優(yōu)先移除最近未使用的 key。
volatile-random - 在設(shè)置了過期時(shí)間的鍵空間中,隨機(jī)移除某個(gè) key。
volatile-ttl - 在設(shè)置了過期時(shí)間的鍵空間中,具有更早過期時(shí)間的 key 優(yōu)先移除。
兩者都是非關(guān)系型內(nèi)存鍵值數(shù)據(jù)庫。有以下主要不同:
數(shù)據(jù)類型
Memcached 僅支持字符串類型;
而 Redis 支持五種不同種類的數(shù)據(jù)類型,使得它可以更靈活地解決問題。
數(shù)據(jù)持久化
Memcached 不支持持久化;
Redis 支持兩種持久化策略:RDB 快照和 AOF 日志。
分布式
Memcached 不支持分布式,只能通過在客戶端使用像一致性哈希這樣的分布式算法來實(shí)現(xiàn)分布式存儲,這種方式在存儲和查詢時(shí)都需要先在客戶端計(jì)算一次數(shù)據(jù)所在的節(jié)點(diǎn)。
Redis Cluster 實(shí)現(xiàn)了分布式的支持。
內(nèi)存管理機(jī)制
Memcached 將內(nèi)存分割成特定長度的塊來存儲數(shù)據(jù),以完全解決內(nèi)存碎片的問題,但是這種方式會使得內(nèi)存的利用率不高,例如塊的大小為 128 bytes,只存儲 100 bytes 的數(shù)據(jù),那么剩下的 28 bytes 就浪費(fèi)掉了。
在 Redis 中,并不是所有數(shù)據(jù)都一直存儲在內(nèi)存中,可以將一些很久沒用的 value 交換到磁盤。而 Memcached 的數(shù)據(jù)則會一直在內(nèi)存中。
Redis 快速的原因:
絕大部分請求是純粹的內(nèi)存操作(非常快速)
采用單線程,避免了不必要的上下文切換和競爭條件
非阻塞 IO
內(nèi)部實(shí)現(xiàn)采用 epoll,采用了 epoll+自己實(shí)現(xiàn)的簡單的事件框架。epoll 中的讀、寫、關(guān)閉、連接都轉(zhuǎn)化成了事件,然后利用 epoll 的多路復(fù)用特性,絕不在 io 上浪費(fèi)一點(diǎn)時(shí)間。
異步處理 - 相比于傳統(tǒng)的串行、并行方式,提高了系統(tǒng)吞吐量。
應(yīng)用解耦 - 系統(tǒng)間通過消息通信,不用關(guān)心其他系統(tǒng)的處理。
流量削鋒 - 可以通過消息隊(duì)列長度控制請求量;可以緩解短時(shí)間內(nèi)的高并發(fā)請求。
日志處理 - 解決大量日志傳輸。
消息通訊 - 消息隊(duì)列一般都內(nèi)置了高效的通信機(jī)制,因此也可以用在純的消息通訊。比如實(shí)現(xiàn)點(diǎn)對點(diǎn)消息隊(duì)列,或者聊天室等。
將所有 Broker 和待分配的 Partition 排序
將第 i 個(gè) Partition 分配到第(i mod n)個(gè) Broker 上
將第 i 個(gè) Partition 的第 j 個(gè) Replica 分配到第((i + j) mode n)個(gè) Broker 上
MQ 的常見問題有:
消息的順序問題
消息的重復(fù)問題
消息有序指的是可以按照消息的發(fā)送順序來消費(fèi)。
假如生產(chǎn)者產(chǎn)生了 2 條消息:M1、M2,假定 M1 發(fā)送到 S1,M2 發(fā)送到 S2,如果要保證 M1 先于 M2 被消費(fèi),怎么做?
解決方案:
(1)保證生產(chǎn)者 - MQServer - 消費(fèi)者是一對一對一的關(guān)系
缺陷:
并行度就會成為消息系統(tǒng)的瓶頸(吞吐量不夠)
更多的異常處理,比如:只要消費(fèi)端出現(xiàn)問題,就會導(dǎo)致整個(gè)處理流程阻塞,我們不得不花費(fèi)更多的精力來解決阻塞的問題。
(2)通過合理的設(shè)計(jì)或者將問題分解來規(guī)避。
不關(guān)注亂序的應(yīng)用實(shí)際大量存在
隊(duì)列無序并不意味著消息無序
所以從業(yè)務(wù)層面來保證消息的順序而不僅僅是依賴于消息系統(tǒng),是一種更合理的方式。
造成消息重復(fù)的根本原因是:網(wǎng)絡(luò)不可達(dá)。
所以解決這個(gè)問題的辦法就是繞過這個(gè)問題。那么問題就變成了:如果消費(fèi)端收到兩條一樣的消息,應(yīng)該怎樣處理?
消費(fèi)端處理消息的業(yè)務(wù)邏輯保持冪等性。只要保持冪等性,不管來多少條重復(fù)消息,最后處理的結(jié)果都一樣。 保證每條消息都有唯一編號且保證消息處理成功與去重表的日志同時(shí)出現(xiàn)。利用一張日志表來記錄已經(jīng)處理成功的消息的 ID,如果新到的消息 ID 已經(jīng)在日志表中,那么就不再處理這條消息。
節(jié)點(diǎn)角色:
節(jié)點(diǎn) | 角色說明 |
---|---|
Provider | 暴露服務(wù)的服務(wù)提供方 |
Consumer | 調(diào)用遠(yuǎn)程服務(wù)的服務(wù)消費(fèi)方 |
Registry | 服務(wù)注冊與發(fā)現(xiàn)的注冊中心 |
Monitor | 統(tǒng)計(jì)服務(wù)的調(diào)用次數(shù)和調(diào)用時(shí)間的監(jiān)控中心 |
Container | 服務(wù)運(yùn)行容器 |
調(diào)用關(guān)系:
務(wù)容器負(fù)責(zé)啟動(dòng),加載,運(yùn)行服務(wù)提供者。
服務(wù)提供者在啟動(dòng)時(shí),向注冊中心注冊自己提供的服務(wù)。
服務(wù)消費(fèi)者在啟動(dòng)時(shí),向注冊中心訂閱自己所需的服務(wù)。
注冊中心返回服務(wù)提供者地址列表給消費(fèi)者,如果有變更,注冊中心將基于長連接推送變更數(shù)據(jù)給消費(fèi)者。
服務(wù)消費(fèi)者,從提供者地址列表中,基于軟負(fù)載均衡算法,選一臺提供者進(jìn)行調(diào)用,如果調(diào)用失敗,再選另一臺調(diào)用。
服務(wù)消費(fèi)者和提供者,在內(nèi)存中累計(jì)調(diào)用次數(shù)和調(diào)用時(shí)間,定時(shí)每分鐘發(fā)送一次統(tǒng)計(jì)數(shù)據(jù)到監(jiān)控中心。
Random
隨機(jī),按權(quán)重設(shè)置隨機(jī)概率。
在一個(gè)截面上碰撞的概率高,但調(diào)用量越大分布越均勻,而且按概率使用權(quán)重后也比較均勻,有利于動(dòng)態(tài)調(diào)整提供者權(quán)重。
RoundRobin
輪循,按公約后的權(quán)重設(shè)置輪循比率。
存在慢的提供者累積請求的問題,比如:第二臺機(jī)器很慢,但沒掛,當(dāng)請求調(diào)到第二臺時(shí)就卡在那,久而久之,所有請求都卡在調(diào)到第二臺上。
LeastActive
最少活躍調(diào)用數(shù),相同活躍數(shù)的隨機(jī),活躍數(shù)指調(diào)用前后計(jì)數(shù)差。
使慢的提供者收到更少請求,因?yàn)樵铰奶峁┱叩恼{(diào)用前后計(jì)數(shù)差會越大。
ConsistentHash
一致性 Hash,相同參數(shù)的請求總是發(fā)到同一提供者。
當(dāng)某一臺提供者掛時(shí),原本發(fā)往該提供者的請求,基于虛擬節(jié)點(diǎn),平攤到其它提供者,不會引起劇烈變動(dòng)。
缺省只對第一個(gè)參數(shù) Hash,如果要修改,請配置
缺省用 160 份虛擬節(jié)點(diǎn),如果要修改,請配置
Failover - 失敗自動(dòng)切換,當(dāng)出現(xiàn)失敗,重試其它服務(wù)器。通常用于讀操作,但重試會帶來更長延遲??赏ㄟ^ retries="2" 來設(shè)置重試次數(shù)(不含第一次)。
Failfast - 快速失敗,只發(fā)起一次調(diào)用,失敗立即報(bào)錯(cuò)。通常用于非冪等性的寫操作,比如新增記錄。
Failsafe - 失敗安全,出現(xiàn)異常時(shí),直接忽略。通常用于寫入審計(jì)日志等操作。
Failback - 失敗自動(dòng)恢復(fù),后臺記錄失敗請求,定時(shí)重發(fā)。通常用于消息通知操作。
Forking - 并行調(diào)用多個(gè)服務(wù)器,只要一個(gè)成功即返回。通常用于實(shí)時(shí)性要求較高的讀操作,但需要浪費(fèi)更多服務(wù)資源。可通過 forks="2" 來設(shè)置大并行數(shù)。
Broadcast - 播調(diào)用所有提供者,逐個(gè)調(diào)用,任意一臺報(bào)錯(cuò)則報(bào)錯(cuò)。通常用于通知所有提供者更新緩存或日志等本地資源信息。
Dubbo 作為 RPC 框架,首先要完成的就是跨系統(tǒng),跨網(wǎng)絡(luò)的服務(wù)調(diào)用。消費(fèi)方與提供方遵循統(tǒng)一的接口定義,消費(fèi)方調(diào)用接口時(shí),Dubbo 將其轉(zhuǎn)換成統(tǒng)一格式的數(shù)據(jù)結(jié)構(gòu),通過網(wǎng)絡(luò)傳輸,提供方根據(jù)規(guī)則找到接口實(shí)現(xiàn),通過反射完成調(diào)用。也就是說,消費(fèi)方獲取的是對遠(yuǎn)程服務(wù)的一個(gè)代理(Proxy),而提供方因?yàn)橐С植煌慕涌趯?shí)現(xiàn),需要一個(gè)包裝層(Wrapper)。調(diào)用的過程大概是這樣:
消費(fèi)方的 Proxy 和提供方的 Wrapper 得以讓 Dubbo 構(gòu)建出復(fù)雜、統(tǒng)一的體系。而這種動(dòng)態(tài)代理與包裝也是通過基于 SPI 的插件方式實(shí)現(xiàn)的,它的接口就是ProxyFactory。
@SPI("javassist")public interface ProxyFactory { @Adaptive({Constants.PROXY_KEY})T getProxy(Invoker invoker) throws RpcException; @Adaptive({Constants.PROXY_KEY}) Invoker getInvoker(T proxy, Class type, URL url) throws RpcException; }
ProxyFactory 有兩種實(shí)現(xiàn)方式,一種是基于 JDK 的代理實(shí)現(xiàn),一種是基于 javassist 的實(shí)現(xiàn)。ProxyFactory 接口上定義了@SPI("javassist"),默認(rèn)為 javassist 的實(shí)現(xiàn)。
dubbo 序列化,阿里尚不成熟的 java 序列化實(shí)現(xiàn)。
hessian2 序列化:hessian 是一種跨語言的高效二進(jìn)制的序列化方式,但這里實(shí)際不是原生的 hessian2 序列化,而是阿里修改過的 hessian lite,它是 dubbo RPC 默認(rèn)啟用的序列化方式。
json 序列化:目前有兩種實(shí)現(xiàn),一種是采用的阿里的 fastjson 庫,另一種是采用 dubbo 中自已實(shí)現(xiàn)的簡單 json 庫,一般情況下,json 這種文本序列化性能不如二進(jìn)制序列化。
java 序列化:主要是采用 JDK 自帶的 java 序列化實(shí)現(xiàn),性能很不理想。
Kryo 和 FST:Kryo 和 FST 的性能依然普遍優(yōu)于 hessian 和 dubbo 序列化。
Hessian 序列化與 Java 默認(rèn)的序列化區(qū)別?
Hessian 是一個(gè)輕量級的 remoting on http 工具,采用的是 Binary RPC 協(xié)議,所以它很適合于發(fā)送二進(jìn)制數(shù)據(jù),同時(shí)又具有防火墻穿透能力。
Hessian 支持跨語言串行
比 java 序列化具有更好的性能和易用性
支持的語言比較多
Protocol Buffer 是 Google 出品的一種輕量 & 高效的結(jié)構(gòu)化數(shù)據(jù)存儲格式,性能比 Json、XML 真的強(qiáng)!太!多!
Protocol Buffer 的序列化 & 反序列化簡單 & 速度快的原因是:
編碼 / 解碼 方式簡單(只需要簡單的數(shù)學(xué)運(yùn)算 = 位移等等)
采用 Protocol Buffer 自身的框架代碼 和 編譯器 共同完成
Protocol Buffer 的數(shù)據(jù)壓縮效果好(即序列化后的數(shù)據(jù)量體積?。┑脑蚴牵?/p>
采用了獨(dú)特的編碼方式,如 Varint、Zigzag 編碼方式等等
采用 T - L - V 的數(shù)據(jù)存儲方式:減少了分隔符的使用 & 數(shù)據(jù)存儲得緊湊
可以。Dubbo 消費(fèi)者在應(yīng)用啟動(dòng)時(shí)會從注冊中心拉取已注冊的生產(chǎn)者的地址接口,并緩存在本地。每次調(diào)用時(shí),按照本地存儲的地址進(jìn)行調(diào)用。
ZooKeeper 是一個(gè)分布式應(yīng)用協(xié)調(diào)系統(tǒng),已經(jīng)用到了許多分布式項(xiàng)目中,用來完成統(tǒng)一命名服務(wù)、狀態(tài)同步服務(wù)、集群管理、分布式應(yīng)用配置項(xiàng)的管理等工作。
每個(gè) Server 在內(nèi)存中存儲了一份數(shù)據(jù);
Zookeeper 啟動(dòng)時(shí),將從實(shí)例中選舉一個(gè) leader(Paxos 協(xié)議);
Leader 負(fù)責(zé)處理數(shù)據(jù)更新等操作(Zab 協(xié)議);
一個(gè)更新操作成功,當(dāng)且僅當(dāng)大多數(shù) Server 在內(nèi)存中成功修改數(shù)據(jù)。
Netty 是一個(gè)“網(wǎng)絡(luò)通訊框架”。
Netty 進(jìn)行事件處理的流程。Channel
是連接的通道,是 ChannelEvent 的產(chǎn)生者,而ChannelPipeline
可以理解為 ChannelHandler 的集合。
IO 的方式通常分為幾種:
同步阻塞的 BIO
同步非阻塞的 NIO
異步非阻塞的 AIO
在使用同步 I/O 的網(wǎng)絡(luò)應(yīng)用中,如果要同時(shí)處理多個(gè)客戶端請求,或是在客戶端要同時(shí)和多個(gè)服務(wù)器進(jìn)行通訊,就必須使用多線程來處理。
NIO 基于 Reactor,當(dāng) socket 有流可讀或可寫入 socket 時(shí),操作系統(tǒng)會相應(yīng)的通知引用程序進(jìn)行處理,應(yīng)用再將流讀取到緩沖區(qū)或?qū)懭氩僮飨到y(tǒng)。也就是說,這個(gè)時(shí)候,已經(jīng)不是一個(gè)連接就要對應(yīng)一個(gè)處理線程了,而是有效的請求,對應(yīng)一個(gè)線程,當(dāng)連接沒有數(shù)據(jù)時(shí),是沒有工作線程來處理的。
與 NIO 不同,當(dāng)進(jìn)行讀寫操作時(shí),只須直接調(diào)用 API 的 read 或 write 方法即可。這兩種方法均為異步的,對于讀操作而言,當(dāng)有流可讀取時(shí),操作系統(tǒng)會將可讀的流傳入 read 方法的緩沖區(qū),并通知應(yīng)用程序;對于寫操作而言,當(dāng)操作系統(tǒng)將 write 方法傳遞的流寫入完畢時(shí),操作系統(tǒng)主動(dòng)通知應(yīng)用程序。 即可以理解為,read/write 方法都是異步的,完成后會主動(dòng)調(diào)用回調(diào)函數(shù)。
系統(tǒng)拆分從資源角度分為:應(yīng)用拆分和數(shù)據(jù)庫拆分。
從采用的先后順序可分為:水平擴(kuò)展、垂直拆分、業(yè)務(wù)拆分、水平拆分。
是否使用服務(wù)依據(jù)實(shí)際業(yè)務(wù)場景來決定。
當(dāng)垂直應(yīng)用越來越多,應(yīng)用之間交互不可避免,將核心業(yè)務(wù)抽取出來,作為獨(dú)立的服務(wù),逐漸形成穩(wěn)定的服務(wù)中心,使前端應(yīng)用能更快速的響應(yīng)多變的市場需求。此時(shí),用于提高業(yè)務(wù)復(fù)用及整合的分布式服務(wù)框架(RPC)是關(guān)鍵。
當(dāng)服務(wù)越來越多,容量的評估,小服務(wù)資源的浪費(fèi)等問題逐漸顯現(xiàn),此時(shí)需增加一個(gè)調(diào)度中心基于訪問壓力實(shí)時(shí)管理集群容量,提高集群利用率。此時(shí),用于提高機(jī)器利用率的資源調(diào)度和治理中心(SOA)是關(guān)鍵。
Thrift 是跨語言的 RPC 框架。
Dubbo 支持服務(wù)治理,而 Thrift 不支持。
關(guān)于“Redis分布式技術(shù)面試題有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點(diǎn)。