這篇文章主要介紹HDFS短路讀的示例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
成都服務(wù)器托管,創(chuàng)新互聯(lián)提供包括服務(wù)器租用、服務(wù)器托管、帶寬租用、云主機(jī)、機(jī)柜租用、主機(jī)租用托管、CDN網(wǎng)站加速、域名申請(qǐng)等業(yè)務(wù)的一體化完整服務(wù)。電話(huà)咨詢(xún):18980820575
Hadoop的一個(gè)重要思想就是移動(dòng)計(jì)算,而不是移動(dòng)數(shù)據(jù)。我們更愿意盡可能將計(jì)算移動(dòng)到數(shù)據(jù)所在節(jié)點(diǎn)。因此,HDFS中經(jīng)常出現(xiàn)客戶(hù)端和數(shù)據(jù)在一個(gè)節(jié)點(diǎn)上,當(dāng)客戶(hù)端讀取一個(gè)數(shù)據(jù)塊時(shí),就會(huì)出現(xiàn)本地讀取。例如HBase場(chǎng)景,ResionServer寫(xiě)數(shù)據(jù)一般在HDFS中都會(huì)存儲(chǔ)三備份副本并且肯定會(huì)往本地節(jié)點(diǎn)寫(xiě)一備份,當(dāng)ResionServer讀取該數(shù)據(jù)時(shí)也會(huì)優(yōu)先選擇同一節(jié)點(diǎn)的數(shù)據(jù)進(jìn)行讀取。
1、網(wǎng)絡(luò)讀
最初,HDFS中本地讀取的處理方式和遠(yuǎn)程讀取相同,也是通過(guò)網(wǎng)絡(luò)讀來(lái)實(shí)現(xiàn)??蛻?hù)端通過(guò)TCP套接字連接到DataNode,并通過(guò)DataTransferProtocol協(xié)議傳輸數(shù)據(jù),如下圖:
這種方式簡(jiǎn)單,但有明顯的問(wèn)題:
DataNode必須為每個(gè)正在讀取數(shù)據(jù)塊的客戶(hù)端保留一個(gè)線(xiàn)程和一個(gè)TCP套接字。內(nèi)核中會(huì)有TCP協(xié)議的開(kāi)銷(xiāo),以及DataTransferProtocol協(xié)議的開(kāi)銷(xiāo),因此這里有很大的優(yōu)化空間。
2、HDFS-2246 不安全短路讀
短路讀關(guān)鍵思想是因?yàn)榭蛻?hù)端和數(shù)據(jù)塊在同一節(jié)點(diǎn)上,所以DataNode不需要出現(xiàn)在讀取數(shù)據(jù)路徑中。而客戶(hù)端本身可以直接從本地磁盤(pán)讀取數(shù)據(jù)。這樣會(huì)使讀取性能得到很大的提高。在HDFS-2246中實(shí)現(xiàn)的短路讀是DataNode將所有數(shù)據(jù)塊路徑的權(quán)限開(kāi)放給客戶(hù)端,客戶(hù)端直接通過(guò)本地磁盤(pán)路徑來(lái)讀取數(shù)據(jù),見(jiàn)下圖:
但這種方式引入了很多問(wèn)題:
(1)系統(tǒng)管理員必須更改DataNode數(shù)據(jù)目錄的權(quán)限,以允許客戶(hù)端打開(kāi)相關(guān)文件。將能夠使用短路讀的用戶(hù)專(zhuān)門(mén)列入白名單,不允許其他用戶(hù)使用。通常,這些用戶(hù)也必須放在特殊的Unix組中。
(2)這些權(quán)限更改會(huì)引入了一個(gè)安全漏洞,具有讀取DataNode節(jié)點(diǎn)上數(shù)據(jù)塊文件權(quán)限的用戶(hù)可以任意讀取路徑上所有數(shù)據(jù)塊,而不僅僅是他們所需訪(fǎng)問(wèn)的數(shù)據(jù)塊,這好像讓用戶(hù)變成了超級(jí)用戶(hù),對(duì)于少數(shù)用戶(hù)來(lái)說(shuō)可能是可以接受的,例如HBase用戶(hù)。但總的來(lái)說(shuō),它會(huì)帶來(lái)很大安全隱患。
3、HDFS-347 安全短路讀
HDFS-2246的主要問(wèn)題是向客戶(hù)端打開(kāi)了DataNode的數(shù)據(jù)目錄,而我們真正需要讀取的只是一部分?jǐn)?shù)據(jù)塊文件。Unix有一種機(jī)制是可以做到這一點(diǎn),稱(chēng)為文件描述符傳遞。HDFS-347使用這種機(jī)制來(lái)實(shí)現(xiàn)安全短路讀。DataNode不是將目錄傳遞給客戶(hù)端,而是打開(kāi)塊文件和元數(shù)據(jù)文件,并將它們的文件描述符通過(guò)domain socket傳遞給客戶(hù)端,如下圖:
基于以下兩方面,安全短路讀解決了HDFS-2246存在的安全性問(wèn)題。
(1)文件描述符是只讀的,因此客戶(hù)端無(wú)法修改傳遞描述符的文件。
(2)客戶(hù)端無(wú)法訪(fǎng)問(wèn)數(shù)據(jù)塊目錄本身,所以也無(wú)法讀取它不應(yīng)該訪(fǎng)問(wèn)的任何其他數(shù)據(jù)塊文件。
1、短路讀共享內(nèi)存
了解了HDFS短路讀的演進(jìn),我們來(lái)看下HDFS是如何實(shí)現(xiàn)安全短路讀的。DataNode將短路讀副本的文件描述符傳給DFSClient,DFSClient緩存副本文件描述符。由于副本的狀態(tài)可能隨時(shí)發(fā)生改變,所以需要DFSClient和DataNode實(shí)時(shí)同步副本狀態(tài)。同時(shí),DFSClient和DataNode在同一臺(tái)機(jī)器上,共享內(nèi)存可以通過(guò)POSIX提供的 mmap接口實(shí)現(xiàn)將文件映射到內(nèi)存,并且映射數(shù)據(jù)是實(shí)時(shí)同步的(如下圖),所以共享內(nèi)存可以維護(hù)所有短路讀副本的狀態(tài),使得DFSClient和DataNode通過(guò)共享內(nèi)存來(lái)實(shí)時(shí)同步副本信息。
共享內(nèi)存會(huì)有很多槽位,每個(gè)槽位對(duì)應(yīng)一個(gè)短路讀副本的信息。共享內(nèi)存保存了所有槽位的二進(jìn)制信息。但是映射數(shù)據(jù)中的二進(jìn)制槽位信息不便于管理,所以定義了Slot對(duì)象操作映射數(shù)據(jù)中的一個(gè)槽位,如下圖:
Slot槽位大小是64字節(jié),槽位數(shù)據(jù)格式,前4字節(jié)是Slot標(biāo)志位,5到8字節(jié)是錨計(jì)數(shù)位,剩余字節(jié)保留將來(lái)使用,例如統(tǒng)計(jì)信息等。
兩個(gè)標(biāo)志位:
(1)VALID_FLAG:表示槽位是否有效。
DFSClient在共享內(nèi)存中分配新的槽位時(shí)設(shè)置此標(biāo)志位。當(dāng)與此槽位關(guān)聯(lián)的副本不再有效時(shí),DataNode將會(huì)消除此標(biāo)志位。DFSClient本身也會(huì)消除此槽位,認(rèn)為DataNode不再使用此槽位進(jìn)行通信。
(2)ANCHORABLE_FLAG:表示槽位對(duì)應(yīng)的副本是否已經(jīng)緩存。
DataNode將槽位對(duì)應(yīng)的副本通過(guò)POSIX提供的mlock接口緩存時(shí)會(huì)設(shè)置該標(biāo)志位。當(dāng)標(biāo)志位已設(shè)置,DFSClient短路讀取該副本時(shí)不再需要進(jìn)行校驗(yàn),因?yàn)楦北揪彺鏁r(shí)已經(jīng)做了檢驗(yàn)操作,并且這種副本還支持零拷貝讀取。DFSClient對(duì)這樣的副本進(jìn)行讀取時(shí),需要在對(duì)應(yīng)的槽位錨計(jì)數(shù)加1,只有當(dāng)槽位的錨計(jì)數(shù)為0時(shí),DataNode才可以從緩存中刪除此副本。
共享內(nèi)存段的最大是8192字節(jié),當(dāng)DFSClient進(jìn)行大量短路讀時(shí), DFSClient和DataNode之間可能會(huì)有多段共享內(nèi)存。HDFS中DFSClient定義了DFSClientShm類(lèi)抽象了DFSClient端一段共享內(nèi)存,DFSClientShmManager類(lèi)管理所有的DFSClientShm,而DataNode端定義了RegisteredShm類(lèi)抽象DataNode端的一段共享內(nèi)存,ShortCircuitRegistry類(lèi)管理所有DataNode端的共享內(nèi)存,如下圖所示:
在安全短路讀中,DFSClient和DataNode是通過(guò)domain socket來(lái)同步共享內(nèi)存槽位信息的。
DFSClient申請(qǐng)一段共享內(nèi)存保存短路讀副本的狀態(tài)。DataNode會(huì)創(chuàng)建共享內(nèi)存,并將共享內(nèi)存文件映射到DataNode內(nèi)存中,并創(chuàng)建RegisteredShm管理這段共享內(nèi)存,之后會(huì)將共享內(nèi)存文件的文件描述符通過(guò)domain socket返回給DFSClient。
DFSClient根據(jù)文件描述符打開(kāi)共享內(nèi)存文件,將該文件映射到DFSClient的內(nèi)存中,并創(chuàng)建DfsClientShm對(duì)象管理這段共享內(nèi)存。
DFSClient通過(guò)domain socket向DataNode申請(qǐng)數(shù)據(jù)塊文件以及元數(shù)據(jù)文件的文件描述符,并且同步共享內(nèi)存中slot槽位的狀態(tài)。DFSClient會(huì)在DfsClientShm管理的共享內(nèi)存中為數(shù)據(jù)塊申請(qǐng)一個(gè)slot槽位,之后通過(guò)domain socket向DataNode同步信息,DataNode會(huì)在RegisteredShm管理的共享內(nèi)存中創(chuàng)建相應(yīng)的slot槽位,然后獲取數(shù)據(jù)塊文件以及元數(shù)據(jù)文件的文件描述符,并通過(guò)domain socket發(fā)送給DFSClient,如下圖:
共享內(nèi)存機(jī)制
2、短路讀流程
當(dāng)客戶(hù)端執(zhí)行數(shù)據(jù)塊副本短路讀時(shí),DFSClient與DataNode的交互過(guò)程如下:
短路讀簡(jiǎn)化流程圖
(1)DFSClient通過(guò)requestShortCircuitShm()接口向DataNode請(qǐng)求創(chuàng)建共享內(nèi)存,DataNode創(chuàng)建共享內(nèi)存文件并將共享內(nèi)存文件描述符返回給DFSClient。
(2)DFSClient通過(guò)allocShmSlot()接口申請(qǐng)共享內(nèi)存中的槽位,并通過(guò)requestShortCircuitFds()接口向DataNode請(qǐng)求要讀取的副本文件描述符,DataNode打開(kāi)副本文件并將數(shù)據(jù)塊文件和元數(shù)據(jù)文件的文件描述符返回給DFSClient。
(3)DFSClient讀取完副本后,異步通過(guò)releaseShortCircuitFds()接口向DataNode請(qǐng)求釋放文件描述符及相應(yīng)槽位。
1、Slot槽位釋放緩慢
幾次壓力場(chǎng)景中,我們發(fā)現(xiàn)Hbase ResionServer多個(gè)短路讀線(xiàn)程經(jīng)常會(huì)阻塞在domain socket的讀寫(xiě)上。從DataNode 的dump中發(fā)現(xiàn)大量的用于短路讀的ShortCircuitShm。于是我們通過(guò)YCSB模擬線(xiàn)上的情況,發(fā)現(xiàn)短路讀請(qǐng)求量較大時(shí),BlockReaderLocal分配的QPS很高,并且BlockReaderLocal的分配依賴(lài)于同步讀取塊的ShortCircuitShm和slot的分配。
YCSB GET QPS
YCSB GET LATENCY
通過(guò)統(tǒng)計(jì)slot分配和釋放的QPS,我們發(fā)現(xiàn)slot分配的QPS能達(dá)到3000+,而釋放的QPS只能達(dá)到1000+,并且在YCSB測(cè)試經(jīng)過(guò)約1小時(shí),DataNode出現(xiàn)FULL GC。由此可看出,DataNode中積累的,來(lái)不及釋放的slot,是導(dǎo)致GC的主要有原因。
現(xiàn)在的短路讀實(shí)現(xiàn)中,每次釋放slot,都會(huì)新建一個(gè)domain socket連接。而DataNode對(duì)于每個(gè)新建立的domain socket 連接,都會(huì)重新初始化一個(gè)DataXceiver去處理這個(gè)請(qǐng)求。通過(guò)profile DataNode發(fā)現(xiàn),SlotReleaser線(xiàn)程花了大量的時(shí)間在建立和清理這些連接上。
于是,我們對(duì)SlotReleaser的domain socket連接進(jìn)行了復(fù)用。通過(guò)復(fù)用domain socket,在同樣的測(cè)試集上,slot 釋放的QPS能和分配的QPS達(dá)到一致。從而消除了過(guò)期slot在DataNode中的擠壓。同時(shí),由于DataNode Young GC減少,YCSB的GET的QPS也提升了約20%左右。
2、共享內(nèi)存分配效率低
在profile HBase短路讀過(guò)程中,我們還發(fā)現(xiàn)另外一個(gè)問(wèn)題,就是每隔一段時(shí)間,會(huì)有一批讀會(huì)有約200ms左右的延遲而且這些延遲幾乎同時(shí)出現(xiàn)。開(kāi)始我們懷疑 Hbase ResionServer的Minor GC導(dǎo)致。但通過(guò)比對(duì)ResionServer的GC日志,發(fā)現(xiàn)時(shí)間并不完全匹配。有一部分卻和DataNode Minor GC時(shí)間吻合。數(shù)據(jù)塊副本的真正讀取操作,是完全不通過(guò)DataNode的,如果是DataNode的影響,那問(wèn)題只能出在ResionServer和DataNode建立短路讀時(shí)的交互上。通過(guò)進(jìn)一步在短路讀過(guò)程中加trace log,我們發(fā)現(xiàn)這些延遲,是由于DataNode Minor GC導(dǎo)致ShortCircuitShm分配請(qǐng)求被阻塞。當(dāng)分配一個(gè)ShortCircuitShm時(shí),會(huì)導(dǎo)致很多slot的分配阻塞。slot的分配延遲,又會(huì)引起B(yǎng)lockReaderLocal的延遲,從而導(dǎo)致短路讀的延遲。這就是之前發(fā)現(xiàn)有一批讀,總是同時(shí)報(bào)相近的延遲。 為了解決這個(gè)問(wèn)題,我們對(duì)ShortCircuitShm進(jìn)行了預(yù)分配,以減輕DataNode Minor GC對(duì)短路度影響,使得延遲更為平滑。
3、短禁止正在構(gòu)建塊的短路讀
禁止了正在構(gòu)建塊進(jìn)行短路讀,也就是最后一個(gè)塊禁止短路讀。這個(gè)問(wèn)題由于HBase的讀寫(xiě)模式 ,對(duì)其影響不是很大,但對(duì)基于HDFS流式服務(wù)影響很大。我們正在做的優(yōu)化工作主要是通過(guò)保證短路讀只發(fā)生在flush操作之后,同時(shí)在讀取過(guò)程中檢查塊的有效性,處理讀異常,如果確實(shí)失敗,轉(zhuǎn)成遠(yuǎn)程讀的方式。
附錄
文件描述符在形式上是一個(gè)非負(fù)整數(shù)。實(shí)際上,它是一個(gè)索引值,指向內(nèi)核為每一個(gè)進(jìn)程所維護(hù)的該進(jìn)程打開(kāi)文件的記錄表。當(dāng)程序打開(kāi)一個(gè)現(xiàn)有文件或者創(chuàng)建一個(gè)新文件時(shí),內(nèi)核向進(jìn)程返回一個(gè)文件描述符。
Unix和Windows系統(tǒng)都允許在進(jìn)程間傳遞文件描述符。一個(gè)進(jìn)程打開(kāi)一個(gè)文件,然后把該文件的文件描述符傳遞給另一個(gè)進(jìn)程,另一個(gè)進(jìn)程可以訪(fǎng)問(wèn)該文件。文件描述符傳遞對(duì)于安全性是非常有用的,因?yàn)樗藢?duì)第二個(gè)進(jìn)程需要擁有足夠訪(fǎng)問(wèn)權(quán)限來(lái)打開(kāi)文件限制,同時(shí)文件描述符是只讀的,所以該方式還可以防止有問(wèn)題的程序或者惡意的客戶(hù)端損壞文件。在Unix系統(tǒng)上,文件描述符傳遞只能通過(guò)Unix domain socket完成。
Unix domain socket是用于在同一臺(tái)主機(jī)操作系統(tǒng)上執(zhí)行的進(jìn)程間交換數(shù)據(jù)的通信端點(diǎn)。有效的Unix domain socket類(lèi)型是SOCK_STREAM(用于面向流的套接字)和SOCK_DGRAM(用于保留消息邊界的面向數(shù)據(jù)報(bào)的套接字),與大多數(shù)Unix實(shí)現(xiàn)一樣,Unix domain datagram socket始終可靠且不重新排序的數(shù)據(jù)報(bào)。Unix domain socket是POSIX操作系統(tǒng)的標(biāo)準(zhǔn)組件。
Unix domain socket的API類(lèi)似于網(wǎng)絡(luò)socket,但是不使用底層網(wǎng)絡(luò)協(xié)議,所有通信都完全在操作系統(tǒng)內(nèi)核中進(jìn)行。Unix domain socket使用文件系統(tǒng)作為其地址名稱(chēng)空間。進(jìn)程引用Unix domain socket作為文件系統(tǒng)inode,因此兩個(gè)進(jìn)程可以通過(guò)打開(kāi)相同的socket進(jìn)行通信。 除了發(fā)送數(shù)據(jù)外,進(jìn)程還可以使用sendmsg()和recvmsg()系統(tǒng)調(diào)用在Unix domain socket連接上發(fā)送文件描述符。并且只有發(fā)送進(jìn)程授權(quán)給接收進(jìn)程,接收進(jìn)程才可以訪(fǎng)問(wèn)文件描述符的權(quán)限。
共享內(nèi)存是進(jìn)程間通信的方法,即在同時(shí)運(yùn)行的程序之間交換數(shù)據(jù)的方法。一個(gè)進(jìn)程將在RAM中創(chuàng)建一個(gè)其他進(jìn)程可以訪(fǎng)問(wèn)的區(qū)域。由于兩個(gè)進(jìn)程可以像訪(fǎng)問(wèn)自身內(nèi)存一樣訪(fǎng)問(wèn)共享內(nèi)存區(qū)域,因此是一種非??焖俚耐ㄐ欧绞健5撬臄U(kuò)展性較差,例如通信必須在同一臺(tái)機(jī)器上運(yùn)行。而且必須要避免如果共享內(nèi)存的進(jìn)程在不同的CPU上運(yùn)行,并且底層架構(gòu)不是緩存一致的。
POSIX提供了使用共享內(nèi)存的POSIX標(biāo)準(zhǔn)化API。使用sys/mman.h中的函數(shù)shm_open。POSIX進(jìn)程間通信包含共享函數(shù)shmat,shmctl,shmdt和shmget。shm_open創(chuàng)建的共享內(nèi)存是持久化的。它一直保留在系統(tǒng)中,直到被進(jìn)程明確刪除。這有一個(gè)缺點(diǎn),如果進(jìn)程崩潰并且無(wú)法清理共享內(nèi)存,它將一直保持到系統(tǒng)關(guān)閉。POSIX還提供了用于將文件映射到內(nèi)存的mmap API,可以共享映射,允許將文件的內(nèi)容用作共享內(nèi)存。
以上是“HDFS短路讀的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!