JPPF平行計算框架類載入機制研究
不算什麼深入的研究,主要是瞭解下JPPF中類的載入和隔離機制。
JPPF中類的載入採用的是分散式類載入技術。這樣既可在Node節點執行在node上並不存在的類。也就是類可以僅在使用者的Client端存在。
如圖,JPPF的class loader大致分三層。
System class loader是由JVM控制的載入器,用於啟動node節點。在大多數JVM中是,該loader是java.net.URLClassloader的例項。
Server class loader是類AbstractJPPFClassloader的實現,提供了遠端訪問server端classpath中的類和其他資源的功能。該loader在node連線到server端的時候建立,斷開連線的時候銷燬。其父loader為System class loader。
Client class loader
JPPF的node節點僅持有一個與Server的連線,該連線被所有的loader共享,這樣可以避免併發時潛在的的一致性,同步和衝突等問題。
Java的classloader是先從parent loader開始載入的餓,下圖展示了node節點載入類的過程
node節點先從server節點請求class。如果server節點存在class,則直接載入返回。
如果class僅在client端存在,則會先訪問server,server無,再通過server定位client,訪問client端的class。
在JPPF框架裡,每個client都有一個唯一的UUID,用於唯一標識該client。Node節點便可根據此UUID識別出當前classloader讀取的是哪個client的class
每個Server也有唯一的UUID標識,這樣就可以實現鏈式類載入:
需要注意的是,在這種情況下,node節點仍然只持有一個server classloader,僅連線跟它直接關聯的server。
UUID與classloader
使用client端UUID,即可實現不同的client端在執行同一個任務的時候的classloader隔離,因為uuid不同,會使用不同的classloader載入類。不過,如果同一個UUID提交了兩個不同版本的任務class,則有可能出現衝突和錯誤。
你也可以手動指定client端的UUID,這樣classloader即可重用。哪怕你重連client也可以重用同一個classloader。不過,此時隨之而來的問題就是在客戶端修改的版本可能不會在node端自動生效,不同的版本還可能造成衝突。這時,你可以重啟node或者更換UUID使node重新載入class。
任務預部署
對於大任務,如果擔心每次走網路效率低,根據上面介紹的classloader機制,自然想到可以預先將任務檔案部署到server或者node節點。不過此時需要自己管理好部署的版本,所謂各有利弊。不過,如果任務版本穩定的話,建議採用此種方式。
classloader快取池
為了避免載入過多的類造成記憶體溢位,JPPF在node端增加了classloader快取池大小的設定,如果超過上限,則會銷燬最老的classloader。
jppf.classloader.cache.size = n
本地快取資原始檔
當呼叫getResourceAsStream(), getResource(),getResources() or getMultipleResources()方法的時候,class loader會快取非類定義的檔案至記憶體或者本地檔案系統中。這樣會避免再次請求該檔案時候的網路訪問,提高效率。你可以指定快取在記憶體中還是檔案系統中:
# either “file” (the default) or “memory“
jppf.resource.cache.storage = file
檔案的儲存路徑當然也是可以配置的,預設是系統臨時資料夾System.getProperty("java.io.tmpdir").:
jppf.resource.cache.dir = some_directory
JPPF server中的類快取
JPPF的服務端會在記憶體中快取被classloader載入進來的類和其他資。這樣就可以避免與客戶端的頻繁的網路訪問,從而提高執行速度。你也無需擔心記憶體溢位的問題,因為這種快取採用的是軟引用,這樣快取的類就可以在必要的時候被JVM的垃圾回收機制回收。在一般情況,該快取還是可以顯著的提升效率的。
Class loader代理模型
JPPF預設採用的是”父優先“的類載入模型,也就是上面我們介紹的模型。上述模型雖然好用,但是類的載入順序就相對固定,也就不能最大化的優化載入效率。因此,JPPF通過繼承URLClassLoader,複寫並開放addURL介面(jdk裡是protected,JPPF為public),使得使用者的自己開發任務可以直接呼叫該方法,形成對classloader的一種擴充套件。使用者可呼叫addURL方法,指定自己想要載入類的地址。此時JPPF classloader的載入順序會出現變化,從大的步驟看,會先查詢指定的URL classpath:
* 從clientclassloader入手:代理到server的classloader
* server的classloader: 先僅查詢使用者指定的URL classpath
* 如果找到類,則結束查詢
* 否則,回到client的classloader,也僅查詢URL的classpath
* 如果找到,結束查詢。
此書,如果仍未找到,則回到前面介紹的通過網路的查詢順序。
概括來說,就是如果URL優先的代理模型啟用,則node會先從本地層級中查詢URL指定的classpath,然後在網路上通過server查詢。有三種指定代理模型的方式,最簡單的就是直接配置在node節點的配置檔案中:
# possible values: parent | url, defaults to parent
jppf.classloader.delegation = parent
下載檔案
如我們之前的介紹的,我們可以通過addURL(URL)方法,動態指定載入類的URL地址,那麼自然想到,我們可以通過先將檔案下載到本地,然後指定URL地址,從而進行本地載入,以提到效率。在AbstractJPPFClassLoader類中,有方法:
public URL[] getMultipleResources(final String...names)
可以幫你同時下載多個檔案到本地。
以上只是我瞭解到的JPPF中關於類載入部分的設計和實現。從已有掌握來看,JPPF考慮的還是非常全面周到的,隔離、效率、一致性都有考慮,應該說對平行計算來說,很有保障。