Docker原理解讀
926
2025-04-01
在一個系統中,模型和接口是相對穩定的部分。
但同樣的模型和接口,若采用不同實現,穩定性、可擴展性和性能等諸多方面相差極大。只有熟悉實現,才有改代碼寫新需求的基礎。
“看實現”的確是個大難題,因有無數細節怪在等你。所以,團隊的新人都需要幾個月試用期去熟悉代碼細節。
你不可能記住項目所有細節,但這不妨礙你工作。但若你心中沒有一份關于項目實現的地圖,你就一定會迷失。
新人一般用幾個月熟悉代碼,就是在通過代碼一點點展開地圖,但是,這不僅極其浪費時間,也很難形成整體認知。
推薦你應該直接把地圖展開。怎么展開?
要找到兩個關鍵點:軟件的結構和關鍵的技術。
以Kafka為例,了解一個軟件設計三步走:“模型、接口、實現”。
先看Kafka的模型和接口。
MQ的模型與接口
Kafka自我介紹是個分布式流平臺,這是它現在的發展方向,但更多人覺得它是個MQ。
MQ是Kafka這個軟件的核心模型,而流平臺顯然是這個核心模型存在之后的擴展。所以,要先把焦點放在Kafka的核心模型——MQ。
MQ(Messaging Queue)是一種進程間通信方式,發消息的一方(即生產者)將消息發給MQ,收消息的一方(即消費者)將隊列中的消息取出并處理。
看模型,MQ是很簡單的,不就是生產者發消息,消費者消費消息,還有個topic,區分發給不同目標的消息。
基本接口也很簡單:
生產者發消息:
消費者收消息:
看完模型和接口,你會感覺MQ本身并不難。
但MQ實現有很多,Kafka只是其中一種,為什么會有這么多不同MQ實現呢?因為每個MQ實現有所側重,有其適用場景。
MQ還提供一定的消息存儲能力。當
Pro發消息速度>Con處理消息速度
1
MQ可起到緩沖作用。所以MQ還能“削峰填谷”:在消息量特別大時,先把消息收下來,慢慢處理,以減小系統壓力。
Kafka之所以突出于一大堆MQ實現,關鍵在于它針對消息寫入做優化,它的生產者寫入特快,即吞吐力特強。
顯然,接口和模型不足以將Kafka與其他MQ實現區分。所以,必須開始了解它的實現。
看軟件實現時的關鍵:
軟件的結構
關鍵的技術
模型是個抽象概念,被抽象的對象可以是某個聚合實體(訂單中心中的訂單),也可以是某個流程或功能(Java內存模型中的主存與緩存同步的規則)。
分層對模型來說是實現層面的東西,是一種水平方向的拆分,是一個實現上的規范;
模型的細粒度拆分(父模型、子模型),應該是一種垂直維度的拆分,子模型的功能要高內聚,其復雜性不該發散到外部。
軟件結構
軟件結構也是軟件模型,只不過,它不是整體上的模型,而是展開實現細節之后的模型。模型是分層的。
對每個軟件,當你從整體去了解它時,它是完整的一塊。但當你打開它的時候,就成了多模塊組合,這也是“分層”意義。上一層只要使用下一層提供給它的接口。
所以,當打開一個層次,了解其實現時,先從大處著手。最好找到一張結構圖,準確了解它的結構。
如果你能夠找到這樣一張圖,你還是很幸運的。因為在真實的項目中,你可能會碰到各種可能性:
結構圖混亂:你找到一張圖,上面包含了各種內容。比如,有的是模塊設計,有的是具體實現,更有甚者,還包括了一些流程
結構圖復雜:一個比較成熟的項目,圖上畫了太多的內容。確實,隨著項目的發展,軟件解決的問題越來越多,它必然包含了更多的模塊。但對于初次接觸這個項目的我們而言,它就過于復雜了
無結構圖
想辦法畫一張
先了解模型和接口,因為它們永遠是你的主線。
假設:現在你有了一張結構圖,你打算做什么?
了解它的結構?是,但不夠。不僅要知道一個設計的結果,最好還要推斷出設計原因。
所以,一種更好的做法:帶問題上路。
假設自己就是這個軟件設計者,問問自己要怎么做。再去對比別人的設計,你就會發現,自己的想法和別人想法的相同或不同。
讓你來設計MQ,你會怎么做?
Kafka網上能搜到各種架構圖,看個 最簡單的架構圖,因為最貼近MQ基礎模型:
你能看到什么?
Kafka的生產者一端將消息發給Kafka集群
消費者一端將消息取出來進行處理
這樣的結構和你想的是不是一樣?
進一步設計,會干啥?
生產者端封裝出一個 SDK,負責消息的發送
消費者端封裝出一個 SDK,負責消息的接收
設計一個集群系統,作為生產者和消費者之間的連接
可以問自己更多的問題:
生產端如果出現網絡抖動,消息沒有成功發送,怎么重試?
消費端處理完的消息,如何保證集群不重復發送?
為什么要設計一個集群呢?要防止出現單點的故障,而一旦有了集群,就會牽扯到下一個問題,集群內的節點如何保證消息的同步呢?
消息在集群里是怎么存儲的?
生產端也好,消費端也罷,如果一個節點徹底掉線,集群該怎么處理呢?
……
有了更多問題,你就會在代碼里更深入探索。你可根據需要,打開對應模塊,進一步了解實現:
比如消息重發問題,可看生產端是如何解決的。當問題細化到具體實現時,就可以打開源碼,去尋找答案。
結構上,Kafka不是一個特復雜系統。所以,若你的項目更復雜,層次更多,推薦把各層次逐一展開,先把整體結構放在心中,再做細節探索。
核心技術
就是能夠讓這個軟件的“實現”與眾不同的地方。
了解關鍵技術可保證我們對代碼的調整不會使項目出現明顯劣化。
大多數項目都愿意把自己的關鍵技術講出來,所以,找到這些信息不難。
Kafka
對寫入做了專門優化,使其整體吞吐能力很強。
咋做到的?
MQ實現消息存儲的方式通常是把它寫入磁盤,而Kafka不同之處在于,它利用磁盤順序讀寫特性。
普通機械硬盤:
隨機寫,需按機械硬盤方式尋址,然后磁頭做機械運動,寫入很慢
順序寫,會大幅減少磁頭運動
可以這樣實現,也是充分利用MQ本身特性:有序,技術實現與需求完美結合的產物。還可進一步優化:利用內存映射文件減少用戶空間到內核空間復制的開銷。
若站在了解實現的角度,你會覺得這都很自然。
但要想從設計角度學到更多,還是應帶著問題上路,多問自己,為什么其它MQ不這么做?
這的確值得深思。Kafka這個實現到底是哪里不容易想到呢?
軟硬結合。
其它MQ實現也會把消息寫入文件,但文件對于它們只是個通用接口。開發者并沒有想過利用硬件的特性做開發。而Kafka開發者突破此限制,把硬件特性利用起來,取得更好結果。
LMAX Disruptor
最強勁的線程通信庫。經典代碼片段:
要理解這段代碼,必須理解CPU緩存行,這也是軟硬結合的設計。
Disruptor緩存行填充中的填充字段。Disruptor中的一個元素是個volatile long類型,占用8字節。
一但一個元素被修改,則與其在同一緩存行的所有元素的緩存都會失效。這就導致變更索引位1的元素,會導致索引位0的元素緩存也失效(操作時需重新從主內存加載)。
所以,Disruptor做了一個緩存行填充的優化,在目標元素的前后都加7個類型字段,兩邊都占據掉56個字節。故而保證每個元素都獨占緩存行。是一種用空間換時間的思想。
總結
理解一個實現,是以對模型和接口的理解為前提。
如果想了解一個系統的實現,應從軟件結構和關鍵技術兩個方面著手。無論是軟件結構,還是關鍵技術,我們都需要帶著自己的問題入手,而問題的出發點就是我們對模型和接口的理解。
了解軟件的結構,其實,就是把分層的模型展開,看下一層模型:
要知道這個層次給你提供了怎樣的模型
要帶著自己的問題去了解這些模型為什么要這么設計
Kafka的實現主要是針對機械硬盤做的優化,現在的SSD硬盤越來越多,成本越來越低,這個立意的出發點已經不像以前那樣穩固了。
軟件的結構和核心技術應該分開,kafka之所以是:
MQ,看的是對MQ這個模型結構的實現
就沒必看存儲的實現,應該看路由信息管理、消息生產、消息消費等核心實現及其旁支功能的選擇(限制消息大小、故障節點延后、延遲消費)
kafka,看的是其消息存儲核心技術實現
如果想知道kafka為什么在 MQ如此突出,那就得了解其核心技術實現,即這里的軟硬結合的存儲設計。
理解實現,帶著自己的問題,了解軟件的結構和關鍵的技術。
Kafka
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。