當前版本6.0.5(2020-6-28)
叢集架構中,Redis支援主從,哨兵,以及最新的無主叢集模式(Redis-Cluster)
Redis主節點正常啓動,從節點使用slaveof命令設定爲從節點。
哨兵節點最好設定爲叢集,這樣能夠在單個哨兵節點網路故障時不會出現誤判,多數哨兵節點共同判斷節點失效可信度更高。當主伺服器失效的時候,見識這個主伺服器的所有Sentinel就會基於彼此共有的資訊選出一個Sentinel,並從現有的從伺服器當中選出一個新的主伺服器。當被選中的從伺服器轉換爲主伺服器之後,那個被選中的Sentinel就會讓剩餘的其他伺服器去複製這個新的主伺服器(預設Sentinel會一個一個遷移從伺服器,可以通過設定選項進行修改)
S_DOWN:subjectively down,直接翻譯的爲"主觀"失效,即當前sentinel範例認爲某個redis服務爲"不可用"狀態.
O_DOWN:objectively down,直接翻譯爲"客觀"失效,即多個sentinel範例都認爲master處於"SDOWN"狀態,那麼此時master將處於ODOWN,ODOWN可以簡單理解爲master已經被叢集確定爲"不可用",將會開啓failover.
一、使用如下條件篩選備選node:
1、slave節點狀態處於S_DOWN,O_DOWN,DISCONNECTED的除外
2、最近一次ping應答時間不超過5倍ping的間隔(假如ping的間隔爲1秒,則最近一次應答延遲不應超過5秒,redis sentinel預設爲1秒)
3、info_refresh應答不超過3倍info_refresh的間隔(原理同2,redis sentinel預設爲10秒)
4、slave節點與master節點失去聯繫的時間不能超過( (now - master->s_down_since_time) + (master->down_after_period * 10))。總體意思是說,slave節點與master同步太不及時的(比如新啓動的節點),不應該參與被選舉。
5、Slave priority不等於0(這個是在組態檔中指定,預設設定爲100)。
二、從備選node中,按照如下順序選擇新的master
1、較低的slave_priority(這個是在組態檔中指定,預設設定爲100)
2、較大的replication offset(每個slave在與master同步後offset自動增加)
3、較小的runid(每個redis範例,都會有一個runid,通常是一個40位的隨機字串,在redis啓動時設定,重複概率非常小)
4、如果以上條件都不足以區別出唯一的節點,則會看哪個slave節點處理之前master發送的command多,就選誰。
在3.x提出cluster叢集模式。Redis-Cluster採用無中心結構,每個節點儲存數據和整個叢集狀態,每個節點都和其他所有節點連線。
key 分佈模式,key空間分佈被劃分爲16384個slot,所以一個叢集,主節點的個數最大爲16384(一般建議master最大節點數爲1000)
Cluster bus,每個節點有一個額外的TCP埠,這個埠用來和其他節點交換資訊。這個埠一般是在與用戶端鏈接埠上面加10000,比如用戶端埠爲6379,那麼cluster bus的埠爲16379.
cluster 拓撲,Redis cluster 是一個網狀的,每一個節點通過tcp與其他每個節點連線。假如n個節點的叢集,每個節點有n-1個出的鏈接,n-1個進的鏈接。這些鏈接會一直存活。假如一個節點發送了一個ping,很就沒收到pong,但還沒到時間把這個節點設爲 unreachable,就會通過重連重新整理鏈接。
Nodes handshake,如果一個節點發送MEET資訊(METT 類似ping,但是強迫接受者,把它作爲叢集一員)。一個節點發送MEET資訊,只有管理員通過命令列,執行如下命令CLUSTER MEET ip port。如果這個節點已經被一個節點信任,那麼也會被其他節點信任。比如A 知道B,B知道C,B會發送gossip資訊給A關於C的資訊。A就會認爲C是叢集一員,並與其建立連線。
失敗檢測,叢集失效檢測就是,當某個master或者slave不能被大多數nodes可達時,用於故障遷移並將合適的slave提升爲master。當slave提升未能有效實施時,叢集將處於error狀態且停止接收Client端查詢。
叢集中的每個節點都會定期地向叢集中的其他節點發送PING訊息,以此交換各個節點狀態資訊,檢測各個節點狀態:線上狀態、疑似下線狀態PFAIL、已下線狀態FAIL。當主節點A通過訊息得知主節點B認爲主節點D進入了疑似下線(PFAIL)狀態時,主節點A會在自己的clusterState.nodes字典中找到主節點D所對應的clusterNode結構,並將主節點B的下線報告(failure report)新增到clusterNode結構的fail_reports鏈表中
如果叢集裏面,半數以上的主節點都將主節點D報告爲疑似下線,那麼主節點D將被標記爲已下線(FAIL)狀態,將主節點D標記爲已下線的節點會向叢集廣播主節點D的FAIL訊息,
所有收到FAIL訊息的節點都會立即更新nodes裏面主節點D狀態標記爲已下線。
將node標記爲FAIL需要滿足以下兩個條件:
1.有半數以上的主節點將node標記爲PFAIL狀態。
2.當前節點也將node標記爲PFAIL狀態。
選新主的過程基於Raft協定選舉方式來實現的
1)當從節點發現自己的主節點進行已下線狀態時,從節點會廣播一條
CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST訊息,要求所有收到這條訊息,並且具有投票權的主節點向這個從節點投票
2)如果一個主節點具有投票權,並且這個主節點尚未投票給其他從節點,那麼主節點將向要求投票的從節點返回一條,CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK訊息,表示這個主節點支援從節點成爲新的主節點
3)每個參與選舉的從節點都會接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK訊息,並根據自己收到了多少條這種訊息來統計自己獲得了多少主節點的支援
4)如果叢集裡有N個具有投票權的主節點,那麼當一個從節點收集到大於等於叢集N/2+1張支援票時,這個從節點就成爲新的主節點
5)如果在一個設定紀元沒有從能夠收集到足夠的支援票數,那麼叢集進入一個新的設定紀元,並再次進行選主,直到選出新的主節點爲止
用戶端請求獲得指定鍵的值時只需要向任意叢集中的節點發送命令,該節點會判斷這個鍵是否在本節點中,在的話直接獲取返回,如果不在則返回給用戶端一個重定向命令,並附帶儲存此鍵對應的節點的ip、埠、槽位。用戶端重新發送請求即可。也可以設定令節點自動轉發請求到指定節點處理再返回給用戶端。當然這樣在一定程度上會影響效能,可以由用戶端設定快取插槽和節點的資訊來解決。
Codis是豌豆莢團隊開源的使用Go語言編寫的Redis分佈式解決方案,它是作爲中介軟體,以代理的身份接收請求,底層再將請求轉發到指定的節點中,Codis的優勢在於可以不停機動態增加或者刪除節點,並提供了影象化的管理介面!
當前版本2.5.0(2020-6-28)
KafkaController作爲Kafka叢集控制管理模組。由於Zookeeper上儲存了Kafka機器的元數據資訊,因爲KafkaController通過3在不同目錄註冊不同的回撥函數來達到監測叢集狀態的目的,以及響應叢集狀態的變化
叢集中主要的模式是Partition-Replica,Kafka的核心概念在於 每個Topic下分爲多個Partition,每個Partition都有一個或多個Replica。其中一個Replica爲Leader,其他都爲Follower,Leader處理Partition的所有讀寫請求,Follower定期同步Leader上的數據。
Leader和Follower的選舉是基於Zookeeper實現的,嘗試在Zookeeper的相同路徑上建立瞬時節點(Ephemeral Node),只有一個KafkaController會建立成功。其中負責狀態管理的類爲ZookeeperLeaderElector。
Topic分割區的Leader Replica在不同場景下的選舉策略是不一樣的,不同選舉策略都基礎PartitionLeaderSelector。其根據Topic、Partition、當前Leader、當前的ISR選舉出新的Leader,新的ISR和新的AR(線上狀態),共有5種不同的策略:
消費的時候,只會從leader去讀,但是隻有當一個訊息已經被所有follower都同步成功並返回ack的時候,這個訊息才能 纔能夠被消費者讀到。所以可以說讀取是沒有負載均衡地。複製集僅爲了高可用。
Partition的AR列表的第一個Replica稱爲「Preferred Replica」,並均勻分佈在整個Kafka叢集中。由於每個Partition只有Leader Replica對外提供讀寫服務,並且Partition建立的時候預設的Leader Replica位於Preferred Replica之上,此時Kafka叢集的負載是均衡的,如果Kafka叢集長時間執行,Broker Server中途由於異常而發生重新啓動,此時Partition的Leader Replica會發生遷移,這樣會導致其Partition的Leader Replica在叢集中不再均衡了。
kafka-reassign-partitions.sh提供來重新分配分割區副本的能力。該工具可以促進Kafka叢集的負載均衡。因爲Follower Replica需要從Leader Replica Fetch數據以保持與與Leader Replica同步,僅保持Leader Replica分佈的平衡對整個叢集的負載均衡時不夠的。另外當Kafka叢集擴容後,該工具可以將已有Topic的Partition遷移到新加入的Broker上。
當前版本3.8.5(2020-6-28)
RabbitMQ用戶端中與事務機制 機製相關的方法有三個:channel.txSelect(設定爲事務模式)、channel.txCommit(提交事務)和channel.txRollback(回滾事務)。事務會很大影響RabbitMQ的訊息吞吐量。
輕量級的方式:發送方確認機制 機製。生產者將通道設定成confirm(確認)模式,一旦通道進入confirm模式,所有該通道上面發佈的訊息都會被指派一個唯一的ID(從1開始),一旦訊息被投遞到所有匹配佇列之後,RabbitMQ就發送一個確認(Basic.Ack)給生產者(包含訊息的唯一ID),這就使得生產者知曉訊息已經正確的到達了目的地。如果訊息和佇列是可持久化的,那麼確認訊息就會在訊息寫入磁碟後發出。RabbitMQ回傳給生產者的確認訊息中的deliveryTag包含了待確認訊息的序號,此外RabbitMQ也可以設定channel.basicAck方法中的multiple參數,表示這個序號之前的所有訊息都已經得到了處理。
RabbitMQ叢集中的所有節點都會備份所有的元數據資訊,包括:
在RabbitMQ叢集中建立佇列,叢集只會在單個節點而不是在所有節點上建立佇列的進程幷包含完整的佇列(元數據、狀態、內容)。這樣只有佇列的宿主節點(所有者)節點知道佇列的所有資訊,所有其他非所有者只知道佇列的元數據和指向該佇列存在的那個節點的指針。節點崩潰時,該節點的佇列進程和關聯的系結都會消失,附加在那些佇列上的消費者也會丟失其所訂閱的資訊,並且任何匹配該佇列系結資訊的新訊息也會丟失。
不同於佇列那樣有自己的進程,交換器實際上只是一個名稱和系結列表。當訊息發佈到交換器時,實際上是由所連線的通道將訊息上的路由鍵同交換器的系結列表進行比較,然後再路由訊息。當建立一個新的交換器時,RabbitMQ所要做的就是將系結列表新增到叢集中的所有節點上。這樣每個節點上的每個通道都可以存取到新的交換器。
多機多節點是指每台物理機器都安裝了RabbitMQ,應當只在區域網內使用,廣域網應當使用Federation或者Shovel。
命令列主要使用 rabbitmqctl join_cluster {nodename} 加入叢集節點
rabbitmqctl forget_cluester_node {nodename}
RabbitMQ要求叢集中至少有一個磁碟節點,其他節點可以是記憶體節點。當節點加入或者離開叢集的時候,它們必須將變更通知到至少一個磁碟節點。如果唯一一個磁碟節點崩潰,叢集可以繼續收發訊息,但是不能執行建立佇列、交換器、系結關係、使用者,以及更改許可權、新增和刪除叢集節點的操作。所以叢集應該保障有兩個或者多個磁碟節點的存在。
Federation外掛可以讓多個交換器或者多個佇列進行聯邦,一個聯邦交換器(federated exchange)或者一個聯邦佇列(federated queue)接收上遊(upstream)的訊息,這裏的上遊時指位於其他Broker上的交換器或者佇列聯邦交換器能夠將原本發送給上遊交換器(upstream exchange)的訊息路由到原生的某個佇列中;聯邦佇列則允許一個本地消費者接收到來自上遊佇列(upstream queue)的訊息。
與Federation具備的數據轉發功能類似,Shovel能夠可靠、持續的從一個Broker的佇列拉取數據並轉發只另一個Broker的交換器。實際上是基於AMQP協定的轉發器。
Shovel可以部署在源端也可以部署在目的端。有兩種方式可以部署Shovel:靜態方式(static)和動態方式(dynamic)。靜態方式是指在RabbitMQ.config組態檔中設定,動態方式只指通過Runtime Parameter設定。
當叢集訊息堆積嚴重時,可以通過Shovel將佇列中的訊息移交給另一個叢集,這是一備一的情況。如果需要一備多,可以採用映象佇列或者引入Federation。
Shovel工作在Federation的更低一層。監獄Federation從一個交換器中轉發訊息到另一個交換器(如果有必要可以確認訊息是否被轉發),Shovel只是簡單地從某個Broker上的佇列消費訊息,然後轉發訊息到另一個Broker上的交換器而已。Shovel也可以再一臺單獨的伺服器上去轉發訊息,例如將一個佇列中的數據移動到另一個佇列中。
引入映象佇列(Mirror Queue)的機制 機製,可以將佇列映象到叢集中的其他Broker節點之上,如果叢集中的一個節點失效了,佇列能自動地切換到映象的另一個節點上以保證服務的可用性。每一個設定映象的佇列都包含一個主節點(master)和若幹個從節點(slave),如果master失效,slave加入時間最長的會提升爲master。發送到映象佇列的所有訊息會被同時發往master和其他所有的slave。除了發送訊息(Basic.Publish)外所有動作都只會想master發送,然後由master將命令執行的結果廣播給各個slave。
消費者與slave建立連線消費時實質上都是從master上獲取訊息,只不過看似從slave上消費而已。例如消費者與slave建立了TCP連線後執行Basic.Get操作,由slave轉發給master,再由master準備好數據返回給slave,投遞給消費者。這裏的master和slave針對佇列而言,佇列可以均勻地散落在叢集的各個Broker節點中以達到負載均衡地目的,真正的負載還是針對實際的物理機器而言,而不是記憶體中駐留的佇列進程。
網路分割區的恢復
首先選一個最信任的partition,Mnesia使用該partition中的狀態,其他partitions中發生的變化都將丟失。
停止其他partitions中的所有nodes,之後重新啓動這些nodes。當這些nodes重新加入cluster後將從信任的partition恢復狀態。
最後還需重新啓動信任的partition中的所有nodes以清除network partition的警告資訊
Rabbitmq自動處理網路分割區的3種模式
RabbitMQ提供了3種自動處理network partitions的方式:預設爲ignore模式,也即需要手工處理
pause-minority mode:暫停少數模式;
pause-if-all-down mode:暫停-如果全部停止模式
autoheal mode:自動癒合模式
pause-minority mode:暫停少數模式
在pause-minority模式下,察覺其他nodes down掉後,RabbitMQ將自動暫停認爲自己是少數派的 nodes(例如小於或等於總nodes數的一半),network partition一旦發生,「少數派」的nodes將立刻暫停,直至partition結束後重新恢復。這可以保證在network partition發生時,至多隻有一個partition中的nodes繼續執行。(犧牲可用性保證一致性)
若所有分割區的nodes個數都小於總nodes個數一半,則意味着所有分割區的nodes都會認爲自己是少數派,即所有nodes都將暫停
pause-if-all-down mode:暫停-如果全部停止模式
http://www.rabbitmq.com/partitions.html
autoheal模式
在autoheal模式下一旦發生了partition,RabbitMQ將自動確定一個優勝partition,然後重新啓動所有不在優勝partition中的nodes。
獲勝的partition爲擁有最多用戶端連線的partition(若連線相同則爲節點最多的partition)。
關於自動處理partitions的設定在組態檔的cluster_partition_handling參數中進行。
各自的適用場景
network partitions自動處理並不能保證cluster不出任何問題。
一般來說可作如下選擇:
ignore:若網路非常可靠。所有nodes在同一機架,通過交換機連線,該交換機也是通往外部網路的出口。在cluster的某一部分故障時不希望其餘部分受影響。或者cluster只有兩個node。
pause_minority:網路較不可靠。cluster處於EC2的3個AZ中,假定每次至多隻有其中一個AZ故障,想要剩餘的AZ繼續提供服務而故障的AZ中的nodes在AZ恢復後重新自動加入到cluster。
autoheal:網路很不可靠。與數據完整性相比更關注服務的持續性。cluster只有兩個node。
當前版本4.7.0 release(2020-6-28)
RocketMQ是一個訊息與流處理平臺,具有低延時、高效能、高可靠、萬億級訊息儲存以及可延伸性靈活等特性。主要由4個核心部分組成:name servers, brokers, producers and consumers,每一部分都能叢集部署避免單點故障。
Name Server是一個幾乎無狀態節點,可叢集部署,節點之間無任何資訊同步。提供輕量級的服務發現和路由。 每個 NameServer 記錄完整的路由資訊,提供等效的讀寫服務,並支援快速儲存擴充套件。
Broker 通過提供輕量級的 Topic 和 Queue 機制 機製來處理訊息儲存,同時支援推(push)和拉(pull)模式以及主從結構的容錯機制 機製。
Broker部署相對複雜,Broker分爲Master與Slave,一個Master可以對應多個Slave,但是一個Slave只能對應一個Master,Master與Slave的對應關係通過指定相同的Broker Name,不同的Broker Id來定義,BrokerId爲0表示Master,非0表示Slave。Master也可以部署多個。
每個Broker與Name Server叢集中的所有節點建立長連線,定時(每隔30s)註冊Topic資訊到所有Name Server。Name Server定時(每隔10s)掃描所有存活broker的連線,如果Name Server超過2分鐘沒有收到心跳,則Name Server斷開與Broker的連線。
Producer生產者,產生訊息的範例,擁有相同 Producer Group 的 Producer 組成一個叢集。
Producer與Name Server叢集中的其中一個節點(隨機選擇)建立長連線,定期從Name Server取Topic路由資訊,並向提供Topic服務的Master建立長連線,且定時向Master發送心跳。Producer完全無狀態,可叢集部署。
Producer每隔30s(由ClientConfig的pollNameServerInterval)從Name server獲取所有topic佇列的最新情況,這意味着如果Broker不可用,Producer最多30s能夠感知,在此期間內發往Broker的所有訊息都會失敗。
Producer每隔30s(由ClientConfig中heartbeatBrokerInterval決定)向所有關聯的broker發送心跳,Broker每隔10s中掃描所有存活的連線,如果Broker在2分鐘內沒有收到心跳數據,則關閉與Producer的連線。
Consumer消費者,接收訊息進行消費的範例,擁有相同 Consumer Group 的
Consumer 組成一個叢集。
Consumer與Name Server叢集中的其中一個節點(隨機選擇)建立長連線,定期從Name Server取Topic路由資訊,並向提供Topic服務的Master、Slave建立長連線,且定時向Master、Slave發送心跳。Consumer既可以從Master訂閱訊息,也可以從Slave訂閱訊息,訂閱規則由Broker設定決定。
Consumer每隔30s從Name server獲取topic的最新佇列情況,這意味着Broker不可用時,Consumer最多最需要30s才能 纔能感知。
Consumer每隔30s(由ClientConfig中heartbeatBrokerInterval決定)向所有關聯的broker發送心跳,Broker每隔10s掃描所有存活的連線,若某個連線2分鐘內沒有發送心跳數據,則關閉連線;並向該Consumer Group的所有Consumer發出通知,Group內的Consumer重新分配佇列,然後繼續消費。
當Consumer得到master宕機通知後,轉向slave消費,slave不能保證master的訊息100%都同步過來了,因此會有少量的訊息丟失。但是一旦master恢復,未同步過去的訊息會被最終消費掉。
當前版本7.8.0(2020-6-28)
綠色——最健康的狀態,代表所有的主分片和副本分片都可用;
黃色——所有的主分片可用,但是部分副本分片不可用;
紅色——部分主分片不可用。此時執行查詢部分數據仍然可以查到
查詢不是簡單單步驟的操作,一般分爲兩個階段:分散階段(scatter phase)和合併階段(gather phase)。在分散階段將查詢分發到包含相關文件的多個分片中執行查詢,合併階段從衆多分片中收集返回結果,對它們進行合併、排序、進行後續處理並返回用戶端。
預設情況下每個索引由5個主要分片組成,每個主要分片有一個副本。一個分片是一個目錄中的檔案,Lucene用這些檔案儲存索引數據,分片也是Elasticsearch將數據從一個節點遷移到另一個節點的最小單位。
多個節點使用同樣的叢集名稱(cluster.name)啓動,可以連線叢集中的任一節點存取完整數據集。
預設情況下,索引一篇文件時系統首先根據文件ID的雜湊值選擇一個主分片,並將文件發送到該主分片,然後發送給該主分片的所有副本分片進行索引。搜尋時就可以在主副分片之間進行負載均衡。
索引的每篇文件都有一個ID,ID經過了雜湊處理。索引的每個分片有一個雜湊的取值範圍。索引的文件會分發到雜湊範圍包含該文件ID雜湊值的分片。雜湊的ID被稱爲路由值(routing value),將文件分配到某個分配的過程稱爲路由(routing)。
一個分片是Lucene的索引:一個包含倒排索引的檔案目錄。
Elasticsearch可以使用廣播(broadcast)或者單播(unicast)來發現另一個節點。發現節點後會確認主節點,負責管理叢集的狀態(當前的設定和叢集中分片、索引以及節點的狀態)。會建立內部ping機制 機製來確保每個節點在叢集中保持活躍和健康,被成爲錯誤識別。discovery.zen.minimum_master_nodes最好是節點數除以2加1,能夠防止腦裂。
Bully演算法
Leader選舉的基本演算法之一。 它假定所有節點都有一個惟一的ID,該ID對節點進行排序。 任何時候的當前Leader都是參與叢集的最高id節點。 該演算法的優點是易於實現,但是,當擁有最大 id 的節點處於不穩定狀態的場景下會有問題,例如 Master 負載過重而假死,叢集擁有第二大id 的節點被選爲 新主,這時原來的 Master 恢復,再次被選爲新主,然後又假死…
elasticsearch 通過推遲選舉直到當前的 Master 失效來解決上述問題,但是容易產生腦裂,再通過 法定得票人數過半 解決腦裂
選主流程
只有一個 Leader將當前版本的全域性叢集狀態推播到每個節點。 ZenDiscovery(預設)過程就是這樣的:
每個節點計算最低的已知節點ID,並向該節點發送領導投票
如果一個節點收到足夠多的票數,並且該節點也爲自己投票,那麼它將扮演領導者的角色,開始發佈叢集狀態。
所有節點都會參數選舉,並參與投票,但是,只有有資格成爲 master 的節點的投票纔有效.
有多少選票贏得選舉的定義就是所謂的法定人數。 在彈性搜尋中,法定大小是一個可設定的參數。 (一般設定成:可以成爲master節點數n/2+1)
當前版本4.2.8(2020-6-28)
預設數據目錄是/data/db,它負責儲存所有的MongoDB的數據檔案。在MongoDB內部每個數據庫都包含一個.ns檔案(儲存了每張表和每個索引的名稱空間元數據)和一些數據檔案(foo.0 foo.1 foo.2 遞增)。
MongoDB內部有預分配空間的機制 機製,每個預分配的檔案都用0填充,這樣就能始終保持額外的空間和空餘的數據檔案。隨着表中數據增加,數據檔案每新分配一次,大小就會是上一個數據檔案的2倍,最大2G。
MongoDB支援在多個機器中通過非同步複製達到故障轉移和實現冗餘。多機器中同一時刻只有一臺用於寫操作。擔當Primary角色的機器能把讀操作分發給slave。
高可用分爲兩種:
需要建立主從key檔案,用於標識叢集的私鑰完整路徑,各個範例的key file內部不一致程式不能正常使用。
Replica Set通過一個日誌來儲存寫操作,這個日誌就是oplog。oplog.rs時一個固定長度的capped collection,存在local數據庫中。Oplog 其實就像 MySQL 的 Binlog 一樣,記錄着主節點上執行的每一個操作,而 Secondary 通過複製 Oplog 並應用的方式來進行數據同步。
選擇同步源節點
Replica Sets中的節點從距離它「最近」的節點同步數據,這個「最近」是通過ping的時間來判斷的。在節點之間的心跳檢測中,會記錄ping某個節點和收到響應的時間,通過這個時間的長短,來確定距離的遠近,時間越長視爲距離越遠。知道了和節點之間的距離健康度來確定同步的源節點。
當我們在MongoDB中執行一個寫操作時,預設情況下,寫操作指令發送後,就認爲寫操作執行成功了。爲了保證系統可用性和數據安全性,我們可以更改設定,當寫操作在n個節點(n包括primary,如果n=1,那就是在primary執行成功後返回)都執行成功後,才返回成功。
我們可以通過執行db.adminCommand({replSetGetStatus:1})命令來檢視當前的節點狀況,在secondary上執行這個命令的時候,能夠看到syncingTo這個欄位,這個欄位的值就表示secondary節點同步數據的源節點。有2個secondary或者更多,鏈式同步
Replica Sets角色
pv0: 基於priority 和 optime 選舉新主,依賴clock synchronization。
有選舉權的節點,每一輪選舉最多投一票,在30s內,不能重複投票。
pv1:基於Raft協定,每個成員都有 對候選主列表成員投贊成或者反對票,不是單方面否決選舉,沒有節點投反對票,且獲得贊成票數超過有權投票節點總數的1/2,則能成爲Primary。否則進入下一輪選舉。
因使用了Raft協定,加快 back-to-back選主,減少整個選舉新主所需花費的總時間,相應的會增加WriteConcern(w:1)rollback的可能性。
Raft將時間分爲多個term,term以連續的整數來標識,每個term以一次election開始,如果有server被選爲leader,則該term的剩餘時間該server都是leader。
3.2之前(只支援pv0 協定),>= 3.2開始:支援pv0 協定、pv1協定,預設是pv1協定。
將海量的數據水平擴充套件的數據庫集羣系統,數據分表儲存在sharding的各個節點上。
MongoDB的數據分塊稱爲chunk,每個chunk都是collection中一段連續的數據記錄,通常最大尺寸是200MB,超出則生成新的數據塊
一個MongoDB Sharding Cluster,需要三種角色:
MongoDB Auto-Sharding 解決來海量儲存和動態擴容的問題,「Replica Sets+Sharding」的方案:
Consensus一致性這個概念,它是指多個伺服器在狀態達成一致,但是在一個分佈式系統中,因爲各種意外可能,有的伺服器可能會崩潰或變得不可靠,它就不能和其他伺服器達成一致狀態。這樣就需要一種Consensus協定,一致性協定是爲了確保容錯性,也就是即使系統中有一兩個伺服器當機,也不會影響其處理過程。
爲了以容錯方式達成一致,我們不可能要求所有伺服器100%都達成一致狀態,只要超過半數的大多數伺服器達成一致就可以了,假設有N台伺服器,N/2 +1 就超過半數,代表大多數了。
Paxos和Raft都是爲了實現Consensus一致性這個目標,這個過程如同選舉一樣,參選者需要說服大多數選民(伺服器)投票給他,一旦選定後就跟隨其操作。Paxos和Raft的區別在於選舉的具體過程不同。
Paxos演算法是什麼?
Paxos演算法執行在允許宕機故障的非同步系統中,不要求可靠的訊息傳遞,可容忍訊息丟失、延遲、亂序以及重複。它利用大多數 (Majority) 機制 機製保證了2F+1的容錯能力,即2F+1個節點的系統最多允許F個節點同時出現故障。
一個或多個提議進程 (Proposer) 可以發起提案 (Proposal),Paxos演算法使所有提案中的某一個提案,在所有進程中達成一致。系統中的多數派同時認可該提案,即達成了一致。最多隻針對一個確定的提案達成一致。
Proposer: 提出提案 (Proposal)。Proposal資訊包括提案編號 (Proposal ID) 和提議的值 (Value)。
Acceptor:參與決策,迴應Proposers的提案。收到Proposal後可以接受提案,若Proposal獲得多數Acceptors的接受,則稱該Proposal被批準。
Learner:不參與決策,從Proposers/Acceptors學習最新達成一致的提案(Value)
Raft將系統中的角色分爲領導者(Leader)、跟從者(Follower)和候選人(Candidate):
Leader:接受用戶端請求,並向Follower同步請求日誌,當紀錄檔同步到大多數節點上後告訴Follower提交日誌。
Follower:接受並持久化Leader同步的日誌,在Leader告之日誌可以提交之後,提交日誌。
Candidate:Leader選舉過程中的臨時角色。
Raft 使用心跳(heartbeat)觸發Leader選舉。當伺服器啓動時,初始化爲Follower。Leader向所有Followers週期性發送heartbeat。如果Follower在選舉超時時間內沒有收到Leader的heartbeat,就會等待一段隨機的時間後發起一次Leader選舉。
Follower將其當前term加一然後轉換爲Candidate。它首先給自己投票並且給叢集中的其他伺服器發送 RequestVote RPC (RPC細節參見八、Raft演算法總結)。結果有以下三種情況:
Nacos中的服務註冊數據被設計爲五層結構,包括Namespace、Group、Service、Cluster、Instance。
NACOS選舉機制 機製的底層原理是RAFT共識演算法,NACOS沒有依賴諸如zookeeper之類的第三方庫,而是自實現了一套RAFT演算法。
相較於大名鼎鼎的Paxos演算法,RAFT演算法最突出的優勢就是易於理解,學習起來很輕鬆。
在RAFT演算法領域中,有三種基本的狀態(角色):follower、candidate、leader。
處於follower狀態的server不會發起任何的request,只是被動的響應leader和candidate。
處於leader狀態的server會主動的發送心跳包給各個follower,並且接收client所有的request。
而candidate是一種過渡狀態,只有整個cluster在進行新的選舉的時候,纔會出現此種狀態的server。
C是所有節點在同一時間看到的數據是一致的;
A的定義是所有請求都會收到響應。
1 eureka AP
eureka 保證了可用性,實現最終一致性。
Eureka各個節點都是平等的,幾個節點掛掉不會影響正常節點的工作,剩餘的節點依然可以提供註冊和查詢服務。而Eureka的用戶端在向某個Eureka註冊或時如果發現連線失敗,則會自動切換至其它節點,只要有一臺Eureka還在,就能保證註冊服務可用(保證可用性),只不過查到的資訊可能不是最新的(不保證強一致性),其中說明了,eureka是不滿足強一致性,但還是會保證最終一致性
2 zookeeper CP
zookeeper在選舉leader時,會停止服務,直到選舉成功之後纔會再次對外提供服務,這個時候就說明了服務不可用,但是在選舉成功之後,因爲一主多從的結構,zookeeper在這時還是一個高可用註冊中心,只是在優先保證一致性的前提下,zookeeper纔會顧及到可用性
選型依據:
在粗粒度分佈式鎖,分佈式選主,主備高可用切換等不需要高 TPS 支援的場景下有不可替代的作用,而這些需求往往多集中在大數據、離線任務等相關的業務領域,因爲大數據領域,講究分割數據集,並且大部分時間分任務多進程 / 執行緒並行處理這些數據集,但是總是有一些點上需要將這些任務和進程統一協調,這時候就是 ZooKeeper 發揮巨大作用的用武之地。
但是在交易場景交易鏈路上,在主業務數據存取,大規模服務發現、大規模健康監測等方面有天然的短板,應該竭力避免在這些場景下引入 ZooKeeper,在阿裡巴巴的生產實踐中,應用對 ZooKeeper 申請使用的時候要進行嚴格的場景、容量、SLA 需求的評估。
所以可以使用 ZooKeeper,但是大數據請向左,而交易則向右,分佈式協調向左,服務發現向右。
簡單總結一下剛剛分析的整個過程。
所以總的來說,Nacos採用推+拉的形式,來解決最開始關於長輪訓時間間隔的問題。當然,30s這個時間是可以設定的,而之所以定30s,應該是一個經驗值。
服務註冊到註冊中心後,服務的消費者就可以進行服務發現的流程了,消費者可以直接向註冊中心發送獲取某個服務範例的請求,這種情況下註冊中心將返回所有可用的服務範例給消費者,但是一般不推薦這種情況。另一種方法就是服務的消費者向註冊中心訂閱某個服務,並提交一個監聽器,當註冊中心中服務發生變更時,監聽器會收到通知,這時消費者更新原生的服務範例列表,以保證所有的服務均是可用的。
Nacos 用戶端進行服務註冊有兩個部分組成,一個是將服務資訊註冊到伺服器端,另一個是像伺服器端發送心跳包,這兩個操作都是通過 NamingProxy 和伺服器端進行數據互動的。
Nacos 用戶端進行服務訂閱時也有兩部分組成,一個是不斷從伺服器端查詢可用服務範例的定時任務,另一個是不斷從已變服務佇列中取出服務並通知 EventListener 持有者的定時任務。
HostReactor#getServiceInfo
維護了一個serviceInfoMap,顧名思義,維護了serverList的資訊,key值是serverName,value是ServiceInfo;類中還有一個定時任務ScheduledExecutorService
getServiceInfo()方法主要邏輯是:
1、先從已經存在的serviceInfoMap中通過serverName獲取一個ServiceInfo,如果已經有了,需要再判斷,另一個updatingMap是否存在這個key,如果存在,在wait 5秒,這個時間是寫死的。在返回ServiceInfo前,呼叫scheduleUpdateIfAbsent()方法更新。
2、如果上面第一步serviceInfoMap不存在,則將傳來的參數(erviceName, clusters, env)構建一個ServiceInfo,同時維護到serviceInfoMap和updatingMap中,同時根據allIPs這個參數的不同(我斷點時爲false)呼叫不同的介面去Nacos伺服器端拉取數據,通過方法updateService4AllIPNow 和 updateServiceNow,最後與上一步一樣,呼叫scheduleUpdateIfAbsent方法。
3、scheduleUpdateIfAbsent方法,維護另一個map --futureMap
1.用戶端組裝自己的服務資訊,然後向伺服器端發起註冊請求
2.伺服器端接收到註冊請求後,將服務資訊加入到本地快取,並且加入定時,不斷的檢測服務是否健康,更新或刪除註冊的服務
3.用戶端會在啓動時,spring建立feign bean的時候會去從Nacos伺服器端獲取service資訊,並且加入定時,不斷的拉取feign對應模組的服務資訊
4.用戶端在通過feign呼叫的時候,會通過負載均衡演算法,從本地快取中選擇一個服務進行呼叫
5.伺服器端接收到發現請求時,會根據條件從伺服器端本地快取中獲取對應的範例,封裝好返回給用戶端