不少朋友在剛開始搭建和使用 YARN 集羣的時候,很容易就被紛繁複雜的配置參數搞暈了:參數名稱相近、新老命名摻雜、文檔說明模糊 。特別是那幾個關於內存的配置參數,即便看好幾遍文檔也不能徹底弄懂含義不說,配置時一不當心就會張冠李戴,犯錯誤。java
若是你一樣遇到了上面的問題,沒有關係,在這篇文章中,我就爲你們梳理一下 YARN 的幾個不易理解的內存配置參數,並結合源碼闡述它們的做用和原理,讓你們完全清楚這些參數的含義。node
1、YARN 的基本架構數組
介紹 YARN 框架的介紹文章網上隨處均可以找到,我這裏就不作詳細闡述了。下面附上一張 YARN 框架圖,方便引入咱們的後續內容:服務器
yarn架構圖架構
2、內存相關參數框架
YARN中關於內存配置的參數呢,主要也就是那麼幾個,我已經整理出來列在了下表中。你們先看一下,對於表中各列的意義,我會在本節後面詳細說明;而對於每一個參數的意義,我會放在下節進行詳細解釋。spa
內存參數列表線程
3、參數說明設計
下面分別來說解每一個參數的功能和意義。對象
mapreduce.map.java.opts 和 mapreduce.map.memory.mb
我反覆斟酌了一下,以爲這兩個參數仍是要放在一塊兒講才容易讓你們理解,不然割裂開會讓你們困惑更大。這兩個參數的功能以下:
mapreduce.map.java.opts: 運行 Map 任務的 JVM 參數,例如 -Xmx 指定最大內存大小;
mapreduce.map.memory.mb: Container 這個進程的最大可用內存大小。
這兩個參數是怎樣一種聯繫呢?首先你們要了解 Container 是一個什麼樣的進程。簡單地說,Container 其實就是在執行一個腳本文件(launch_container.sh),而腳本文件中,會執行一個 Java 的子進程,這個子進程就是真正的 Map Task。
Container 和 Map Task 的關係圖
理解了這一點你們就明白了,mapreduce.map.java.opts 其實就是啓動 JVM 虛擬機時,傳遞給虛擬機的啓動參數,而默認值 -Xmx200m 表示這個 Java 程序可使用的最大堆內存數,一旦超過這個大小,JVM 就會拋出 Out of Memory 異常,並終止進程。而 mapreduce.map.memory.mb 設置的是 Container 的內存上限,這個參數由 NodeManager 讀取並進行控制,當 Container 的內存大小超過了這個參數值,NodeManager 會負責 kill 掉 Container。在後面分析 yarn.nodemanager.vmem-pmem-ratio 這個參數的時候,會講解 NodeManager 監控 Container 內存(包括虛擬內存和物理內存)及 kill 掉 Container 的過程。
緊接着,一些深刻思考的讀者可能就會提出這些問題了:
Q: 上面說過,Container 只是一個簡單的腳本程序,且裏面僅運行了一個 JVM 程序,那麼爲什麼還須要分別設置這兩個參數,而不能簡單的設置 JVM 的內存大小就是 Container的大小?
A: YARN 做爲一個通用的計算平臺,設計之初就考慮了各類語言的程序運行於這個平臺之上,而非僅適用 Java 及 JVM。因此 Container 被設計成一個抽象的計算單元,因而它就有了本身的內存配置參數。
Q: JVM 是做爲 Container 的獨立子進程運行的,與 Container 是兩個不一樣的進程。那麼 JVM 使用的內存大小是否受限於 Container 的內存大小限制?也就是說,mapreduce.map.java.opts 參數值是否能夠大於 mapreduce.map.memory.mb 的參數值?
A: 這就須要瞭解 NodeManager 是如何管理 Container 內存的了。NodeManager 專門有一個 monitor 線程,時刻監控全部 Container 的物理內存和虛擬內存的使用狀況,看每一個 Container 是否超過了其預設的內存大小。而計算 Container 內存大小的方式,是計算 Container 的全部子進程所用內存的和。上面說過了,JVM 是 Container 的子進程,那麼 JVM 進程使用的內存大小,固然就算到了 Container 的使用內存量之中。一旦某個 Container 使用的內存量超過了其預設的內存量,則 NodeManager 就會無情地 kill 掉它。
mapreduce.reduce.java.opts 和 mapred.job.reduce.memory.mb
和上面介紹的參數相似,區別就是這兩個參數是針對 Reducer 的。
mapred.child.java.opts
這個參數也已是一箇舊的參數了。在老版本的 MR 中,Map Task 和 Reduce Task 的 JVM 內存配置參數不是分開的,由這個參數統一指定。也就是說,這個參數其實已經分紅了 mapreduce.map.java.opts 和 mapreduce.reduce.java.opts 兩個,分別控制 Map Task 和 Reduce Task
yarn.nodemanager.resource.memory-mb
從這個參數開始,咱們來看 NodeManager 的配置項。
這個參數實際上是設置 NodeManager 預備從本機申請多少內存量的,用於全部 Container 的分配及計算。這個參數至關於一個閾值,限制了 NodeManager 可以使用的服務器的最大內存量,以防止 NodeManager 過分消耗系統內存,致使最終服務器宕機。這個值能夠根據實際服務器的配置及使用,適度調整大小。例如咱們的服務器是 96GB 的內存配置,上面部署了 NodeManager 和 HBase,咱們爲 NodeManager 分配了 52GB 的內存。
yarn.nodemanager.vmem-pmem-ratio 和 yarn.nodemanager.vmem-check-enabled
yarn.nodemanager.vmem-pmem-ratio 這個參數估計是最讓人困惑的了。下面給你們解釋一下這個參數到底在控制什麼。
首先,NodeManager 接收到 AppMaster 傳遞過來的 Container 後,會用 Container 的物理內存大小 (pmem) * yarn.nodemanager.vmem-pmem-ratio 獲得 Container 的虛擬內存大小的限制,即爲 vmemLimit。
而後,NodeManager 在 monitor 線程中監控 Container 的 pmem(物理內存)和 vmem(虛擬內存)的使用狀況。若是當前 vmem 大於 vmemLimit 的限制,則 kill 掉進程。
上述控制是針對虛擬內存的,針對物理內存的使用 YARN 也有相似的監控。yarn.nodemanager.vmem-check-enabled 參數則十分簡單,就是上述監控的開關。
上面的介紹提到了 vmemLimit,也許你們會有個疑問:這裏的 vmem 到底是否是 OS 層面的虛擬內存概念呢?咱們來看一下源碼是怎麼作的。
ContainerMontor 就是上述所說的 NodeManager 中監控每一個 Container 內存使用狀況的 monitor,它是一個獨立線程。ContainerMonitor 得到單個 Container 內存(包括物理內存和虛擬內存)使用狀況的邏輯以下:
Monitor 每隔 3 秒鐘就更新一次每一個 Container 的使用狀況;更新的方式是:
查看 /proc/pid/stat 目錄下的全部文件,從中得到每一個進程的全部信息;
根據當前 Container 的 pid 找出其全部的子進程,並返回這個 Container 爲根節點,子進程爲葉節點的進程樹;在 Linux 系統下,這個進程樹保存在 ProcfsBasedProcessTree 類對象中;
而後從 ProcfsBasedProcessTree 類對象中得到當前進程 (Container) 總虛擬內存量和物理內存量。
由此你們應該立馬知道了,內存量是經過 /proc/pid/stat 文件得到的,且得到的是該進程及其全部子進程的內存量。因此,這裏的 vmem 就是 OS 層面的虛擬內存概念。
內存參數組合示意圖