我建議你閱讀《Hadoop中海量資料的分散式處理方法》系列中的講座《Hadoop.ZooKeeper》的文字記錄
什麼是 ZooKeeper,它在 Hadoop 生態系統中的地位。關於分散式計算的謊言。標準分散式系統圖。協調分散式系統的困難。典型的協調問題。 ZooKeeper 設計背後的原則。 ZooKeeper 資料模型。 znode 標誌。會議。客戶端API。原語(配置、組成員身分、簡單鎖、領導者選舉、無羊群效應的鎖定)。動物園管理員架構。動物園管理員資料庫。札布。請求處理程序。
今天我們來談談ZooKeeper。這個東西非常有用。與任何 Apache Hadoop 產品一樣,它有一個標誌。它描繪了一個男人。
在此之前,我們主要討論瞭如何在那裡處理數據,如何儲存它,即如何以某種方式使用它並以某種方式使用它。今天我想談談建立分散式應用程式。 ZooKeeper 是可以讓您簡化此事的工具之一。這是一種服務,旨在對分散式系統、分散式應用程式中的進程互動進行某種協調。
對此類應用程式的需求每天都在變得越來越多,這就是我們課程的目的。一方面,MapReduce 和這個現成的框架可讓您消除這種複雜性,並使程式設計師無需編寫流程互動和協調等原語。但另一方面,沒有人保證無論如何都不必這麼做。 MapReduce或其他現成的框架並不總是能完全取代一些無法使用它實現的情況。包括MapReduce本身以及一堆其他Apache專案;它們實際上也是分散式應用程式。為了使編寫更容易,他們編寫了 ZooKeeper。
與所有 Hadoop 相關應用程式一樣,它是由 Yahoo! 開發的。現在它也是一個官方的 Apache 應用程式。它不像 HBase 那樣積極開發。如果你使用 JIRA HBase,那麼每天都會有一堆錯誤報告,一堆優化某些東西的建議,也就是說,專案的生命不斷地持續。而ZooKeeper一方面是比較簡單的產品,另一方面這也保證了它的可靠性。而且它非常易於使用,這就是為什麼它已成為 Hadoop 生態系統內應用程式的標準。因此,我認為回顧它以了解它的工作原理以及如何使用它會很有用。
這是我們某次講座中的圖片。我們可以說它與我們迄今為止所考慮的一切都是正交的。這裡指出的所有內容,在某種程度上,都可以與 ZooKeeper 配合使用,即,它是一項使用所有這些產品的服務。 HDFS 和 MapReduce 都沒有編寫自己專門為它們工作的類似服務。因此,使用了ZooKeeper。這簡化了開發以及一些與錯誤相關的事情。
這一切從何而來?看起來我們在不同的電腦上並行啟動了兩個應用程序,用繩子或網格將它們連接起來,一切正常。但問題是網路不可靠,如果您嗅探流量或查看低級別發生的情況,客戶端如何在網路上交互,您經常會看到一些資料包遺失或重新發送。 TCP 協定的發明並非毫無意義,它允許您建立特定的會話並保證訊息的傳遞。但無論如何,即使是 TCP 也不能總是拯救你。一切都有一個超時時間。網路可能會暫時中斷。它可能只是眨眼。這一切都導致您不能依賴網路的可靠性。這是與編寫在一台電腦或一台超級電腦上運行的並行應用程式的主要區別,在一台電腦或一台超級電腦上沒有網絡,但記憶體中有更可靠的資料交換總線。這是一個根本的區別。
除此之外,在使用網路時,總是存在著一定的延遲。磁碟也有,但網路有更多。延遲是一段延遲時間,可以很小,也可以非常大。
網路拓撲正在改變。什麼是拓樸——這是我們網路設備的佈局。有資料中心,有架子,還有蠟燭。所有這些都可以重新連接、移動等。這一切也需要考慮。 IP 名稱發生變化,流量傳輸的路由也會發生變化。這也需要考慮。
網路在設備方面也可能發生變化。從實踐來看,我可以說我們的網路工程師非常喜歡定期更新蠟燭上的東西。突然出了一個新的固件,他們對一些Hadoop叢集並不是特別感興趣。他們有自己的工作。對他們來說,最重要的是網路正常運作。因此,他們想在那裡重新上傳一些東西,在他們的硬體上進行刷新,硬體也會定期更改。所有這些都需要以某種方式考慮在內。所有這些都會影響我們的分散式應用程式。
通常,由於某種原因開始處理大量資料的人認為網路是無限的。如果那裡有一個幾 TB 的文件,那麼您可以將其帶到您的伺服器或電腦並使用 貓 並觀看。另一個錯誤是在 VIM 查看日誌。永遠不要這樣做,因為這很糟糕。因為 Vim 嘗試緩衝所有內容,將所有內容載入到記憶體中,尤其是當我們開始瀏覽此日誌並尋找某些內容時。這些都是被遺忘的事情,但值得考慮。
編寫一個在一台具有一個處理器的電腦上運行的程式會更容易。
當我們的系統成長時,我們希望將其全部並行化,並且不僅在電腦上並行化,而且在叢集上並行化。那麼問題來了:這件事如何協調?我們的應用程式甚至可能不會相互交互,但我們在多台伺服器上並行運行多個進程。如何監控他們一切順利?例如,他們透過網路發送一些東西。他們必須在某處寫入其狀態,例如在某種資料庫或日誌中,然後聚合該日誌並在某處對其進行分析。另外,我們需要考慮到該進程正在運行並且正在運行,突然出現一些錯誤或崩潰,那麼我們要多快才能發現它?
顯然,這一切都可以快速監控。這也很好,但是監控是一個有限的東西,它可以讓你在最高層級監控一些東西。
當我們希望我們的進程開始相互互動時,例如,向彼此發送一些數據,那麼問題也就出現了——這將如何發生?是否會出現某種競爭條件,它們是否會相互覆蓋,資料是否會正確到達,沿途是否會遺失任何東西?我們需要開發某種協議等。
所有這些過程的協調並不是一件小事。它迫使開發人員下降到更低的水平,要么從頭開始編寫系統,要么不完全從頭開始,但這並不是那麼簡單。
如果您想出了一種加密演算法,甚至實現了它,請立即扔掉它,因為它很可能不適合您。它很可能包含一堆您忘記提供的錯誤。切勿將其用於任何嚴重的事情,因為它很可能不穩定。因為所有現有的演算法都經過了很長時間的時間檢驗。它受到社區的干擾。這是一個單獨的主題。這裡也是一樣。如果您自己不可能實現某種進程同步,那麼最好不要這樣做,因為它非常複雜,並且會導致您走上不斷尋找錯誤的不穩定道路。
今天我們談論的是ZooKeeper。一方面,它是一個框架,另一方面,它是一種服務,可以讓開發人員的生活變得更輕鬆,並盡可能簡化邏輯的實現和流程的協調。
讓我們記住標準的分散式系統可能會是什麼樣子。這就是我們談論的——HDFS、HBase。有一個Master進程來管理worker和slave進程。他負責協調和分配任務、重啟worker、啟動新worker以及分配負載。
更進階的是協調服務,即將協調任務本身移到單獨的進程中,再加上並行運行某種備份或備用Master,因為Master可能會失敗。如果Master倒下了,那麼我們的系統就無法運作。我們正在運行備份。有些人指出主伺服器需要複製以進行備份。這也可以委託協調服務。但在這張圖中,Master 本身負責協調 Worker;這裡的服務正在協調資料複製活動。
更高級的選擇是所有協調都由我們的服務處理,就像通常所做的那樣。他負責確保一切順利。如果出現問題,我們會找出原因並嘗試解決這種情況。無論如何,我們留下了一個主站,他以某種方式與從站進行交互,並可以透過某些服務發送資料、訊息、訊息等。
還有一個更高階的方案,當我們沒有Master時,所有節點都是主從,行為不同。但他們仍然需要相互交互,因此仍然需要一些服務來協調這些操作。或許,以這個原理工作的 Cassandra 就適合這個方案。
很難說這些方案中哪一個效果比較好。每個都有自己的優點和缺點。
對師父來說,沒有必要害怕一些事情,因為,正如實踐所顯示的,他不太容易受到不斷服務的影響。這裡最主要的是選擇正確的解決方案來將該服務託管在一個單獨的強大節點上,以便它擁有足夠的資源,這樣如果可能的話,用戶就無法訪問那裡,這樣他們就不會意外殺死這個進程。但同時,在這樣的方案中,從Master流程管理worker要容易得多,也就是從實作的角度來看,這個方案更簡單。
這個方案(上圖)可能更複雜,但更可靠。
主要問題是部分失敗。例如,當我們透過網路發送訊息時,發生某種意外,發送訊息的人將不知道他的訊息是否被接收以及接收方發生了什麼,將不知道訊息是否被正確處理,即他不會收到任何確認。
因此,我們必須處理這種情況。最簡單的事情就是重新發送此訊息並等待我們收到回應。在這種情況下,不考慮接收器的狀態是否有變化。我們可能會發送一條訊息並添加相同的數據兩次。
ZooKeeper 提供了處理此類拒絕的方法,這也讓我們的生活變得更輕鬆。
如前面所提到的,這與編寫多執行緒程式類似,但主要區別在於,在我們建立在不同機器上的分散式應用程式中,唯一的通訊方式是網路。本質上,這是一個無共享架構。在一台機器上運行的每個進程或服務都有自己的記憶體、自己的磁碟、自己的處理器,並且不與任何人共享。
如果我們在一台電腦上編寫多線程程序,那麼我們可以使用共享記憶體來交換資料。我們在那裡有一個上下文切換,進程可以切換。這會影響效能。一方面是叢集上的程式沒有這個問題,但是網路有問題。
因此,編寫分散式系統時出現的主要問題是配置。我們正在編寫某種應用程式。如果很簡單,那麼我們在程式碼中硬編碼各種數字,但這很不方便,因為如果我們決定不希望超時半秒,而希望超時為一秒,那麼我們需要重新編譯應用程式並再次推出所有內容。當它在一台機器上時,你可以重新啟動它,這是一回事,但當我們有很多機器時,我們必須不斷複製所有內容。我們必須嘗試使應用程式可配置。
這裡我們討論的是系統行程的靜態配置。這不完全是,也許從作業系統的角度來看,它可能是我們進程的靜態配置,也就是說,這是一個不能簡單地採取和更新的配置。
還有一個動態配置。這些是我們想要動態變更的參數,以便在那裡擷取它們。
這裡有什麼問題?我們更新了配置,推出了它,那又怎樣?問題可能是,一方面我們推出了配置,但忘記了新事物,配置仍然存在。其次,在我們推出時,配置在某些地方進行了更新,但在其他地方卻沒有更新。我們的應用程式在一台電腦上運行的一些進程使用新配置重新啟動,並在某處使用舊配置重新啟動。從配置的角度來看,這可能會導致我們的分散式應用程式不一致。這個問題很常見。對於動態配置,它更相關,因為它意味著它可以動態更改。
另一個問題是團體成員資格。我們總是有一些工人,我們總是想知道他們中哪些人還活著,哪些人死了。如果有Master,那麼他必須了解哪些worker可以被重新導向到客戶端,以便他們執行計算或處理數據,哪些不能。不斷出現的一個問題是我們需要知道誰在我們的集群中工作。
另一個典型的問題是領導人選舉,當我們想知道誰負責時。一個例子是複製,當我們有一些進程接收寫入操作,然後在其他進程之間複製它們。他將是領導者,其他人都會服從他,都會跟隨他。有必要選擇一個對每個人來說都明確的過程,這樣就不會出現選擇兩個領導者的情況。
還存在互斥訪問。這裡的問題更加複雜。當您編寫多執行緒程式並希望對某些資源(例如記憶體單元)的存取受到限制並僅由一個執行緒執行時,就會有互斥體這樣的東西。這裡的資源可能是更抽象的東西。來自我們網路不同節點的不同應用程式應該只接收對給定資源的獨佔存取權,而不是讓每個人都可以更改它或在那裡寫入內容。這些就是所謂的鎖。
ZooKeeper 可以讓您在一定程度上解決所有這些問題。我將透過範例展示它如何讓您做到這一點。
不存在阻塞原語。當我們開始使用某些東西時,這個原語將不會等待任何事件發生。最有可能的是,這個東西將非同步工作,從而允許進程在等待某些東西時不會掛起。這是一個非常有用的東西。
所有客戶端請求都按照通用佇列的順序進行處理。
在客戶端看到更改的資料之前,客戶端有機會收到有關某些狀態變更、資料變更的通知。
ZooKeeper 可以以兩種模式工作。第一個是獨立的,位於一個節點上。這樣方便測試。它還可以在任意數量的伺服器上以叢集模式運行。如果我們有一個包含 100 台機器的集群,那麼它就沒有必要在 100 台機器上運行。選擇幾台可以運行 ZooKeeper 的機器就足夠了。它體現了高可用性的原則。在每個正在運行的實例上,ZooKeeper 儲存資料的完整副本。稍後我會告訴你他是如何做到的。它不會對資料進行分片或分區。一方面,我們不能儲存太多,這是一個缺點,另一方面,沒有必要這樣做。這不是它的設計目的,它不是資料庫。
資料可以緩存在客戶端。這是一個標準原則,這樣我們就不會中斷服務,也不會用相同的請求來載入它。聰明的客戶端通常知道這一點並緩存它。
例如,我們的某些事情發生了變化。有某種應用程式。選舉了一位新的領導者,他負責處理寫入操作等。我們想要複製資料。一種解決方案是將其放入循環中。我們不斷質疑我們的服務 - 有什麼改變嗎?第二種選擇更為優化。這是一種監視機制,可讓您通知客戶端某些內容發生了變化。就資源而言,這是一種更便宜的方法,對客戶來說更方便。
客戶端是使用ZooKeeper的使用者。
Server 是 ZooKeeper 進程本身。
Znode是ZooKeeper中的關鍵。所有的znode都被ZooKeeper儲存在記憶體中,並以層次圖的形式組織起來,以樹的形式。
有兩種類型的操作。第一個是更新/寫入,當某些操作改變樹的狀態。這棵樹很常見。
並且有可能客戶端沒有完成一次請求而斷開連接,但可以建立一個會話,透過該會話與 ZooKeeper 進行互動。
ZooKeeper 的資料模型類似於檔案系統。有一個標準根目錄,然後我們就好像瀏覽從根目錄開始的目錄一樣。然後是第一級、第二級的目錄。這是所有 znode。
每個znode可以儲存一些數據,通常不會很大,例如10KB。每個 znode 可以有一定數量的子節點。
Znode 有多種類型。它們是可以被創造的。並且在創建 znode 時,我們指定它應該屬於的類型。
有兩種類型。第一個是臨時旗幟。 Znode 存在於會話中。例如,客戶端已經建立了會話。只要這個會話還存在,它就會存在。為了不產生不必要的東西,這是必要的。這也適用於我們在會話中儲存資料原語非常重要的時刻。
第二種是順序標誌。它在到達 znode 的途中增加計數器。例如,我們有一個包含應用程式 1_5 的目錄。當我們建立第一個節點時,它接收到 p_1,第二個節點接收到 p_2。而且當我們每次呼叫這個方法時,我們傳遞的是完整路徑,僅表示路徑的一部分,並且這個數字會自動遞增,因為我們表示節點類型 - 順序的。
常規 znode。她將永遠活著,並擁有我們告訴她的名字。
另一個有用的東西是手錶標誌。如果我們安裝它,那麼客戶端就可以訂閱特定節點的一些事件。稍後我將透過一個範例向您展示這是如何完成的。 ZooKeeper 本身會通知客戶端節點上的資料發生了變化。但是,通知並不能保證某些新資料已到達。他們只是說有些東西發生了變化,所以您仍然需要稍後透過單獨的呼叫來比較數據。
正如我已經說過的,資料的順序由千位元組決定。那裡不需要儲存大量文字數據,因為它不是資料庫,而是動作協調伺服器。
我會告訴你一些關於會議的資訊。如果我們有多個伺服器,那麼我們可以使用會話標識符透明地在伺服器之間移動。相當方便。
每個會話都有某種超時。會話是根據客戶端在該會話期間是否向伺服器發送任何內容來定義的。如果他在逾時期間沒有傳輸任何內容,會話就會中斷,或者用戶端可以自行關閉它。
它沒有那麼多功能,但您可以使用此 API 執行不同的操作。我們看到 create 呼叫建立了一個 znode 並採用三個參數。這是 znode 的路徑,必須從根開始完整指定。這也是我們想要傳輸到那裡的一些數據。以及標誌的類型。創建後,它會返回 znode 的路徑。
其次,你可以刪除它。這裡的技巧是,第二個參數除了 znode 的路徑之外,還可以指定版本。因此,如果我們傳輸的 znode 版本與實際存在的版本相同,則該 znode 將被刪除。
如果我們不想檢查這個版本,那麼我們只需傳遞“-1”參數即可。
第三,它檢查 znode 是否存在。如果節點存在則傳回 true,否則傳回 false。
然後出現flag watch,可以讓你監控這個節點。
即使在不存在的節點上,您也可以設定此標誌,並在它出現時收到通知。這也很有用。
還有一些挑戰是 獲取數據。很明顯我們可以透過znode接收資料。您也可以使用旗幟手錶。在這種情況下,如果沒有節點,則不會安裝。因此,你需要了解它的存在,然後接收資料。
還有 設定數據。這裡我們傳遞版本。而如果我們把這個傳遞下去,某個版本的znode上的資料就會被更新。
您也可以指定“-1”來排除此檢查。
另一種有用的方法是 獲取孩子。我們還可以獲得屬於它的所有 znode 的列表。我們可以透過設定標誌監視來監控這一點。
及方法 同步 允許一次發送所有更改,從而確保它們被保存並且所有資料都已完全更改。
如果我們與傳統程式設計進行類比,那麼當您使用 write 等方法將某些內容寫入磁碟時,在它回傳回應給您之後,並不能保證您已將資料寫入磁碟。即使作業系統確信所有內容都已寫入,磁碟本身也有一些機制,進程會經過多層緩衝區,只有在此之後,資料才會被放置在磁碟上。
大多數情況下使用非同步呼叫。這允許客戶端並行處理不同的請求。您可以使用同步方法,但效率較低。
我們討論的兩個操作是更新/寫入,這會更改資料。它們是建立、設定資料、同步、刪除。而read是存在的,getData,getChildren。
現在有一些範例說明如何製作在分散式系統中工作的原語。例如,與某些東西的配置相關。一名新工人出現了。我們添加了機器並開始了這個過程。還有以下三個問題。它如何查詢 ZooKeeper 的配置?而如果我們想要改變配置,該如何改變呢?那我們改變之後,我們的那些工人怎麼得到它呢?
ZooKeeper 讓這變得相對容易。例如,我們的 znode 樹。這裡有一個用於我們的應用程式的節點,我們在其中建立一個附加節點,其中包含來自配置的資料。這些可能是也可能不是單獨的參數。由於尺寸較小,配置尺寸通常也較小,因此很可能將其儲存在這裡。
您正在使用該方法 獲取數據 從節點獲取工作線程的配置。設定為真。如果由於某種原因該節點不存在,當它出現或更改時我們會收到通知。如果我們想知道某些事情發生了變化,那麼我們將其設為 true。如果該節點中的資料發生變化,我們就會知道。
設定數據。我們設定數據,設定“-1”,即我們不檢查版本,我們假設我們總是有一種配置,我們不需要儲存很多配置。如果您需要儲存很多,則需要新增另一個層級。這裡我們認為只有一個,所以只更新最新的,所以不要檢查版本。此時,所有先前訂閱的用戶端都會收到該節點變更的通知。他們收到資料後,還必須再次要求資料。通知是他們不接收資料本身,而僅接收更改通知。此後他們必須要求新數據。
使用原語的第二個選項是 團體會員。我們有一個分散式應用程序,有一群工作人員,我們想了解他們都已就位。因此,他們必須註冊自己在我們的應用程式中工作。我們也想從主進程或其他地方了解我們目前擁有的所有活躍工作人員。
我們如何做到這一點?對於該應用程序,我們建立一個工作節點並使用 create 方法在其中新增一個子層級。我的幻燈片有錯誤。這裡你需要 順序 指定,那麼所有的worker都會被一一創建。請求有關該節點子節點的所有資料的應用程式接收所有存在的活動工作線程。
對於如何在 Java 程式碼中完成此操作來說,這是一個非常糟糕的實作。讓我們從最後的 main 方法開始。這是我們的類,讓我們創建它的方法。作為第一個參數,我們使用主機,我們連接的地方,即我們將其設定為參數。第二個參數是群組的名稱。
連接是如何發生的?這是所使用的 API 的一個簡單範例。這裡一切都比較簡單。有一個標準類 ZooKeeper。我們將主機傳遞給它。並設定超時時間,例如5秒。我們有一個名為connectedSignal 的成員。本質上,我們沿著傳輸路徑建立一個群組。儘管可以寫入一些內容,但我們不會在那裡寫入資料。而這裡的節點是持久的類型。本質上,這是一個一直存在的普通常規節點。這是創建會話的地方。這是客戶端本身的實作。我們的客戶端將定期發送訊息,表明會話處於活動狀態。當我們結束會話時,我們呼叫 close,就這樣,會話就結束了。這是為了防止我們出現問題,ZooKeeper 會發現並中斷會話。
如何鎖定資源?這裡一切都有點複雜。我們有一組工作人員,有一些我們想要鎖定的資源。為此,我們建立一個單獨的節點,例如,名為 lock1。如果我們能夠創建它,那麼我們就在這裡獲得了一把鎖。如果我們無法創建它,那麼工作線程會嘗試從這裡獲取 getData,並且由於節點已經創建,那麼我們在這裡放置一個觀察者,一旦該節點的狀態發生變化,我們就會知道它。我們可以嘗試有時間重新創建它。如果我們取得了這個節點,取得了這個鎖,那麼當我們不再需要鎖時,我們將放棄它,因為節點只存在於會話中。相應地,它將消失。而另一個會話框架內的另一個客戶端將能夠鎖定該節點,或者更確切地說,他將收到通知,表明某些內容已發生變化,並且他可以嘗試及時執行此操作。
如何選擇主要領導者的另一個例子。這個稍微複雜一點,但也比較簡單。這裡發生了什麼事?有一個主節點聚合所有工作人員。我們正在嘗試獲取有關領導者的數據。如果成功發生,也就是我們收到了一些數據,那麼我們的工作人員就開始跟隨這個領導者。他相信已經有一個領導者了。
如果領導者因為某些原因死亡,例如跌倒,那麼我們會試著創造一個新的領導者。如果我們成功了,那麼我們的工人就會成為領導者。如果此時有人設法創造了一位新的領導者,那麼我們會試著了解祂是誰,然後跟隨祂。
這裡就出現了所謂的羊群效應,即羊群效應,因為當一個領導者去世時,最先出現的人就會成為領導者。
在擷取資源時,您可以嘗試使用稍微不同的方法,如下所示。例如,我們想要得到一把鎖,但沒有赫特效應。它包括這樣一個事實:我們的應用程式請求一個已存在的帶有鎖的節點的所有節點 ID 的清單。如果在此之前我們為其建立鎖的節點是我們收到的集合中最小的,那麼這意味著我們已經捕獲了鎖。我們檢查是否收到了鎖。作為檢查,會有一個條件,即我們在建立新鎖時收到的 id 是最小的。如果我們收到了,我們就會進一步工作。
如果有某個 id 小於我們的鎖,那麼我們會在該事件上放置觀察者並等待通知,直到發生變化。也就是我們收到了這個鎖。而直到它脫落之前,我們不會成為最小的id,也不會收到最小的鎖,這樣我們就能夠登入了。如果不滿足這個條件,那麼我們立即去這裡嘗試再次取得這個鎖,因為在這段時間裡可能發生了一些變化。
ZooKeeper 由什麼組成?主要有4件事。這是處理過程—請求。還有 ZooKeeper 原子廣播。有一個提交日誌,其中記錄了所有操作。以及記憶體複製資料庫本身,即儲存整個樹的資料庫本身。
值得注意的是,所有的寫入操作都經過請求處理器。讀取操作直接進入記憶體資料庫。
資料庫本身是完全複製的。 ZooKeeper 的所有實例都儲存資料的完整副本。
為了在崩潰後恢復資料庫,有一個提交日誌。標準做法是,在資料進入記憶體之前,將其寫入內存,以便在崩潰時可以回放此日誌並恢復系統狀態。並且也使用資料庫的定期快照。
ZooKeeper 原子廣播是用來維護複製資料的東西。
ZAB 在內部從 ZooKeeper 節點的角度選擇領導者。其他節點成為她的追隨者並期望她採取一些行動。如果他們收到條目,他們會將它們全部轉發給領導者。他首先執行寫入操作,然後向他的追蹤者發送有關更改內容的訊息。事實上,這必須是原子地執行的,即整個事情的記錄和廣播操作必須是原子地執行的,從而確保資料的一致性。
它只處理寫入請求。它的主要任務是將操作轉換為事務性更新。這是一個特別產生的請求。
而這裡值得注意的是,保證了同一操作的更新的冪等性。這是什麼?這個東西如果執行兩次,將具有相同的狀態,即請求本身不會改變。需要這樣做,以便在發生崩潰的情況下,您可以重新啟動操作,從而回滾此刻已經失效的更改。在這種情況下,系統的狀態將變得相同,即不應該出現一系列相同的情況,例如更新過程,導致系統的最終狀態不同。
來源: www.habr.com