網(wǎng)站服務(wù)器搭建的步驟采集站seo課程
1、講一講Kafka與RocketMQ中存儲設(shè)計的異同?
Kafka 中文件的布局是以 Topic/partition ,每一個分區(qū)一個物理文件夾,在分區(qū)文件級別實現(xiàn)文件順序?qū)?#xff0c;如果一個Kafka集群中擁有成百上千個主題,每一個主題擁有上百個分區(qū),消息在高并發(fā)寫入時,其IO操作就會顯得零散(消息分散的落盤策略會導(dǎo)致磁盤IO競爭激烈成為瓶頸),其操作相當(dāng)于隨機IO,即 Kafka 在消息寫入時的IO性能會隨著 topic 、分區(qū)數(shù)量的增長,其寫入性能會先上升,然后下降。
而RocketMQ在消息寫入時追求極致的順序?qū)?#xff0c;所有的消息不分主題一律順序?qū)懭?commitlog 文件,并不會隨著 topic 和 分區(qū)數(shù)量的增加而影響其順序性。
在消息發(fā)送端,消費端共存的場景下,隨著Topic數(shù)的增加Kafka吞吐量會急劇下降,而RocketMQ則表現(xiàn)穩(wěn)定。因此Kafka適合Topic和消費端都比較少的業(yè)務(wù)場景,而RocketMQ更適合多Topic,多消費端的業(yè)務(wù)場景。
2、講一講Kafka與RocketMQ中零拷貝技術(shù)的運用
什么是零拷貝?
零拷貝(英語: Zero-copy) 技術(shù)是指計算機執(zhí)行操作時,CPU不需要先將數(shù)據(jù)從某處內(nèi)存復(fù)制到另一個特定區(qū)域。這種技術(shù)通常用于通過網(wǎng)絡(luò)傳輸文件時節(jié)省CPU周期和內(nèi)存帶寬。
?零拷貝技術(shù)可以減少數(shù)據(jù)拷貝和共享總線操作的次數(shù),消除傳輸數(shù)據(jù)在存儲器之間不必要的中間拷貝次數(shù),從而有效地提高數(shù)據(jù)傳輸效率
?零拷貝技術(shù)減少了用戶進程地址空間和內(nèi)核地址空間之間因為上:下文切換而帶來的開銷
可以看出沒有說不需要拷貝,只是說減少冗余[不必要]的拷貝。
下面這些組件、框架中均使用了零拷貝技術(shù):Kafka、Netty、Rocketmq、Nginx、Apache。
傳統(tǒng)數(shù)據(jù)傳送機制
比如:讀取文件,再用socket發(fā)送出去,實際經(jīng)過四次copy。
偽碼實現(xiàn)如下:
buffer = File.read()
Socket.send(buffer)
1、第一次:將磁盤文件,讀取到操作系統(tǒng)內(nèi)核緩沖區(qū);
2、第二次:將內(nèi)核緩沖區(qū)的數(shù)據(jù),copy到應(yīng)用程序的buffer;
3、第三步:將application應(yīng)用程序buffer中的數(shù)據(jù),copy到socket網(wǎng)絡(luò)發(fā)送緩沖區(qū)(屬于操作系統(tǒng)內(nèi)核的緩沖區(qū));
4、第四次:將socket buffer的數(shù)據(jù),copy到網(wǎng)卡,由網(wǎng)卡進行網(wǎng)絡(luò)傳輸。
分析上述的過程,雖然引入DMA來接管CPU的中斷請求,但四次copy是存在“不必要的拷貝”的。實際上并不需要第二個和第三個數(shù)據(jù)副本。應(yīng)用程序除了緩存數(shù)據(jù)并將其傳輸回套接字緩沖區(qū)之外什么都不做。相反,數(shù)據(jù)可以直接從讀緩沖區(qū)傳輸?shù)教捉幼志彌_區(qū)。
顯然,第二次和第三次數(shù)據(jù)copy 其實在這種場景下沒有什么幫助反而帶來開銷(DMA拷貝速度一般比CPU拷貝速度快一個數(shù)量級),這也正是零拷貝出現(xiàn)的背景和意義。
打個比喻:200M的數(shù)據(jù),讀取文件,再用socket發(fā)送出去,實際經(jīng)過四次copy(2次cpu拷貝每次100ms ,2次DMS拷貝每次10ms)
傳統(tǒng)網(wǎng)絡(luò)傳輸?shù)脑?#xff1a;合計耗時將有220ms
mmap內(nèi)存映射(RocketMQ使用的)
硬盤上文件的位置和應(yīng)用程序緩沖區(qū)(application buffers)進行映射(建立一種一一對應(yīng)關(guān)系),由于mmap()將文件直接映射到用戶空間,所以實際文件讀取時根據(jù)這個映射關(guān)系,直接將文件從硬盤拷貝到用戶空間,只進行了一次數(shù)據(jù)拷貝,不再有文件內(nèi)容從硬盤拷貝到內(nèi)核空間的一個緩沖區(qū)。
mmap內(nèi)存映射將會經(jīng)歷:3次拷貝: 1次cpu copy,2次DMA copy;
打個比喻:200M的數(shù)據(jù),讀取文件,再用socket發(fā)送出去,如果是使用MMAP實際經(jīng)過三次copy(1次cpu拷貝每次100ms ,2次DMS拷貝每次10ms)合計只需要120ms
從數(shù)據(jù)拷貝的角度上來看,就比傳統(tǒng)的網(wǎng)絡(luò)傳輸,性能提升了近一倍。
RocketMQ源碼中的MMAP運用
RocketMQ源碼中,使用MappedFile這個類類進行MMAP的映射
Kafka中的零拷貝
Kafka兩個重要過程都使用了零拷貝技術(shù),且都是操作系統(tǒng)層面的狹義零拷貝,一是Producer生產(chǎn)的數(shù)據(jù)存到broker,二是 Consumer從broker讀取數(shù)據(jù)。
Producer生產(chǎn)的數(shù)據(jù)持久化到broker,采用mmap文件映射,實現(xiàn)順序的快速寫入;
Customer從broker讀取數(shù)據(jù),采用sendfile,將磁盤文件讀到OS內(nèi)核緩沖區(qū)后,直接轉(zhuǎn)到socket buffer進行網(wǎng)絡(luò)發(fā)送。
sendfile
linux 2.1支持的sendfile
當(dāng)調(diào)用sendfile()時,DMA將磁盤數(shù)據(jù)復(fù)制到kernel buffer,然后將內(nèi)核中的kernel buffer直接拷貝到socket buffer。在硬件支持的情況下,甚至數(shù)據(jù)都并不需要被真正復(fù)制到socket關(guān)聯(lián)的緩沖區(qū)內(nèi)。取而代之的是,只有記錄數(shù)據(jù)位置和長度的描述符被加入到socket緩沖區(qū)中,DMA模塊將數(shù)據(jù)直接從內(nèi)核緩沖區(qū)傳遞給協(xié)議引擎,從而消除了遺留的最后一次復(fù)制。
一旦數(shù)據(jù)全都拷貝到socket buffer,sendfile()系統(tǒng)調(diào)用將會return、代表數(shù)據(jù)轉(zhuǎn)化的完成。socket buffer里的數(shù)據(jù)就能在網(wǎng)絡(luò)傳輸了。
sendfile會經(jīng)歷:3次拷貝,1次CPU copy ,2次DMA copy;硬件支持的情況下,則是2次拷貝,0次CPU copy, 2次DMA copy。
3、有沒有讀過RocketMQ源碼,分享一下?
RocketMQ的源碼是非常的多,我們沒有必要把RocketMQ所有的源碼都讀完,所以我們把核心、重點的源碼進行解讀,RocketMQ核心流程如下:
- 啟動流程RocketMQ服務(wù)端由兩部分組成NameServer和Broker,NameServer是服務(wù)的注冊中心,Broker會把自己的地址注冊到NameServer,生產(chǎn)者和消費者啟動的時候會先從NameServer獲取Broker的地址,再去從Broker發(fā)送和接受消息。
- 消息生產(chǎn)流程Producer將消息寫入到RocketMQ集群中Broker中具體的Queue。
- 消息消費流程Comsumer從RocketMQ集群中拉取對應(yīng)的消息并進行消費確認(rèn)。
NameServer設(shè)計亮點
存儲基于內(nèi)存
NameServer存儲以下信息:
topicQueueTable:Topic消息隊列路由信息,消息發(fā)送時根據(jù)路由表進行負(fù)載均衡
brokerAddrTable:Broker基礎(chǔ)信息,包括brokerName、所屬集群名稱、主備Broker地址
clusterAddrTable:Broker集群信息,存儲集群中所有Broker名稱
brokerLiveTable:Broker狀態(tài)信息,NameServer每次收到心跳包是會替換該信息
filterServerTable:Broker上的FilterServer列表,用于類模式消息過濾。
NameServer的實現(xiàn)基于內(nèi)存,NameServer并不會持久化路由信息,持久化的重任是交給Broker來完成。這樣設(shè)計可以提高NameServer的處理能力。
消息寫入流程
RocketMQ使用Netty處理網(wǎng)絡(luò),broker收到消息寫入的請求就會進入SendMessageProcessor類中processRequest方法。
最終進入DefaultMessageStore類中asyncPutMessage方法進行消息的存儲
然后消息進入commitlog類中的asyncPutMessage方法進行消息的存儲
整個存儲設(shè)計層次非常清晰,大致的層次如下圖:
業(yè)務(wù)層:也可以稱之為網(wǎng)絡(luò)層,就是收到消息之后,一般交給SendMessageProcessor來分配(交給哪個業(yè)務(wù)來處理)。DefaultMessageStore,這個是存儲層最核心的入口。
存儲邏輯層:主要負(fù)責(zé)各種存儲的邏輯,里面有很多跟存儲同名的類。
存儲I/O層:主要負(fù)責(zé)存儲的具體的消息與I/O處理。
Commitlog寫入時使用可重入鎖還是自旋鎖?
RocketMQ在寫入消息到CommitLog中時,使用了鎖機制,即同一時刻只有一個線程可以寫CommitLog文件。CommitLog 中使用了兩種鎖,一個是自旋鎖,另一個是重入鎖。源碼如下:
這里注意lock鎖的標(biāo)準(zhǔn)用法是try-finally處理(防止死鎖問題)
另外這里鎖的類型可以自主配置。
RocketMQ 官方文檔優(yōu)化建議:異步刷盤建議使用自旋鎖,同步刷盤建議使用重入鎖,調(diào)整Broker配置項useReentrantLockWhenPutMessage,默認(rèn)為false;
同步刷盤時,鎖競爭激烈,會有較多的線程處于等待阻塞等待鎖的狀態(tài),如果采用自旋鎖會浪費很多的CPU時間,所以“同步刷盤建議使用重入鎖”。
異步刷盤是間隔一定的時間刷一次盤,鎖競爭不激烈,不會存在大量阻塞等待鎖的線程,偶爾鎖等待就自旋等待一下很短的時間,不要進行上下文切換了,所以采用自旋鎖更合適。
Commitlog寫入時使用可重入鎖還是自旋鎖?
RocketMQ在寫入消息到CommitLog中時,使用了鎖機制,即同一時刻只有一個線程可以寫CommitLog文件。CommitLog 中使用了兩種鎖,一個是自旋鎖,另一個是重入鎖。源碼如下:
這里注意lock鎖的標(biāo)準(zhǔn)用法是try-finally處理(防止死鎖問題)
另外這里鎖的類型可以自主配置。
RocketMQ 官方文檔優(yōu)化建議:異步刷盤建議使用自旋鎖,同步刷盤建議使用重入鎖,調(diào)整Broker配置項useReentrantLockWhenPutMessage,默認(rèn)為false;
同步刷盤時,鎖競爭激烈,會有較多的線程處于等待阻塞等待鎖的狀態(tài),如果采用自旋鎖會浪費很多的CPU時間,所以“同步刷盤建議使用重入鎖”。
異步刷盤是間隔一定的時間刷一次盤,鎖競爭不激烈,不會存在大量阻塞等待鎖的線程,偶爾鎖等待就自旋等待一下很短的時間,不要進行上下文切換了,所以采用自旋鎖更合適。