頭條是這批次面試中的一個理想公司,基礎架構部。從兩輪面試的狀況來看,面試官的素質很是高,面試經驗也比較豐富。一方面提問抓的準,不會在你明確表示準備不足的方面硬扣;一方面深度廣度、代碼風格均有涉及。不過總共只讓我寫了兩道代碼題,但願不是放水。java
一面應該還問了其餘內容,可是兩次面試問的太多,想不起來了。git
兩個項目都是簡單介紹,沒有深刻問。github
書到用時方恨少,題到問時才知淺。併發複習的太少,這部分徹底忘卻了。面試
題目很熟悉了,卻是也寫過。算法
我有點記不清二叉搜索樹(BST)的定義,因而先跟面試官簡單確認了下。接下來,向面試官明確題目:架構
l < root < r
向面試官表示用分治法,而後開始寫代碼。寫到一半,面試官又跟我說能夠考慮二叉樹的先序、中序、後續遍歷這些。我反應過來能夠直接中序遍歷,遍歷的同時判斷序列是否遞增。說了下思路,詢問面試官是否能夠先寫我原來的解法,這樣就不用換思路了,省的出錯。面試官統一後我才繼續寫:併發
private class Result {
public boolean isBST;
public int min;
public int max;
public Result(boolean isBST, int min, int max) {
this.isBST = isBST;
this.min = min;
this.max = max;
}
}
public isBST(TreeNode root) {
if (root == null) {
return true;
}
Result result = dc(root);
return result.isBST;
}
private Result dc(TreeNode root) {
if (root == null) {
return null;
}
if (root.left == null && root.right == null) {
return new Result(true, root.val, root.val);
}
Result lResult = dc(root.left);
Result rResult = dc(root.right);
if (lResult != null && (!lResult.isBST || lResult.max >= root.val) {
return new Result(false, 0, 0);
}
if (rResult != null && (!rResult.isBST || rResult.min >= root.val)) {
return new Result(false, 0, 0);
}
// 忘記了這裏的check,no face, no bug free
int min = root.val;
int max = root.val;
if (lResult != null) {
min = lResult.min;
}
if (rResult != null) {
max = rResult.max;
}
return new Result(true, min, max);
}複製代碼
分治法代碼寫出來有點長,不過多長也得堅持編碼風格和bug free。app
but,我第一次寫的仍是有bug,剛把紙交給面試官我就想起來了——最後一句return沒有檢查空指針,,,媽的真是蠢。還好面試官沒有介意,感謝感謝。負載均衡
中序遍歷的話,簡單作能夠徹底保存下序列,再去check,可是浪費空間;複雜作就得一邊遍歷一邊檢查,我還沒想清楚足夠簡潔的寫法。(待補充)分佈式
多是我講的愈來愈能抓住key point了?這是我惟一一次把vulture中的兩個難點都講了。特別是異常恢復,把壓縮過程抽象成狀態機仍是有點講頭的。
我表示也考慮過這種方案,但當時的需求是儘可能簡單的搞定壓縮,若是跟HDFS整合的話,可能須要將冷熱溫的生存期、溫度等都寫進FileStatus,而改動這種基礎類的影響太大了。
表示項目自己很簡單,難在如何肯定所監控指標的準確含義。
話說一半,意在引導面試官提問指標相關的內容,秀源碼。
我把ContainerExitStatus=137時的case講了一下。
客戶端掛載,如何映射 balabala。
Zookeeper保障惟一時刻只有至多一個active節點;惟一的active向journalNode寫數據,其餘standby從journalNode讀數據。
開始覺得跟SecondaryNameNode機制同樣,後來仔細一想:standby既然能從journalNode讀數據,也就不須要像SecondaryNameNode那樣從active拉取editlog,天然也不須要在stanby上合併日誌並同步回active。
因此,坦白本身沒看過這一塊內容,也沒什麼想法。
狀態機、事件調度
說的比較糙:
先講三個資源等級,假設最簡單的場景,剛說到ResourceLocalizer狀態機,面試官就表示,「好,不用說了,我知道你看過這塊」。
只想出來這兩點。待完善。
我不瞭解這方面的內容,嘗試以客戶端掛載方案爲baseline進行改進。
Federation的核心是目錄到NameNode的映射關係。而客戶端掛載的本質缺點在於將映射關係保存在客戶端,所以全部功能都依賴於客戶端完成。因此優化的第一步是,將映射關係保存在服務端。下面給出兩種方案:
我回答的不徹底是面試官想要的——面試官問我看過Hdaoop 3.0的源碼嗎,我表示徹底沒看過,面試官表示沒看過的話應該答不上來。
1w個節點,每一個節點都是
40核+256G
,所有跑MR,每一個container1核+2G
。估計Proxy須要承受的qps。
假設就跑了一個AM,它申請了集羣全部的container,所有跑mapper,可忽略該AM。假設每一個mapper在1min(60s)內須要訪問5次NameNode,則至少須要訪問5次Proxy(獲取映射)。則:
qps = (10E4 * 40 / 1) * 5次/60s = 3.67 * 10E4 次/s複製代碼
面試官問我這麼大的規模單機扛得住嗎,,,我表示如今的技術扛萬級qps不是很輕鬆嗎。恩,,可能我仍是沒有get到考點吧。
我一面中併發答的不好,就主動跟二面面試官表示併發這塊複習的很差。面試官心照不宣,也沒有出很難的題目。萬謝萬謝。
synchronized、ReentrantLock、用AQS裸寫一個鎖。
這裏我原本也寫了Condition、CountDownLatch那些併發工具,後來以爲這更屬於同步,因此又把它們刪了。這種屬於偏概念的套路題,須要刷面經才能知道套路答案。
我一開始以爲先不用說重入次數,就只回答了須要在鎖內部記錄owner。結果面試官提醒多重入的場景我才說還要記錄重入次數,搞得好像面試官提醒我纔想到。
因此說下次不能耍小聰明,能想到的點儘可能說出來。若是面試官不攔着你,你就由簡到難,一直說下去,說到一個完整且你說不下去的地方爲止。
對比講了HashTable和ConcurrentHashMap。
估計面試官問我這個問題只是想看看我是否是併發一點都不會,畢竟我前面併發答的那麼差。
而後就說不上來了。
現補充以下:
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
public final native Class<?> getClass();
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
protected native Object clone() throws CloneNotSupportedException;
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {...}
public final void wait() throws InterruptedException {
wait(0);
}
protected void finalize() throws Throwable { }
}複製代碼
主要漏答的是clone, getClass。finalize, registerNatives也儘可能記住。
主要是須要判斷相等的時候,好比HashMap的get方法,對hashCode和equals的調用順序有要求:
if (table[pos].key.hashCode() == key.hashCode
&& (table[pos].key == key || table[pos].equals(key))) {
return table[pos];
}複製代碼
只有hashCode相等的時候,equals纔有可能相等,即,「覆寫後,若是equals相等,那麼hashCode也必須相等」。
同時,對於HashMap還要保證插入先後,key的hashCode相等。
總結以下:
咱們知道NameNode中保存了BlockId到BlockInfo的映射,以知足快速查找的目的。若是直接用HashMap實現(固然,實際用的也不是HashMap,用的Google的LightWeightGSet),因爲HashMap內部用拉鍊發實現,每一個節點都有最有兩個指針,假設每一個指針佔2字節,那麼1億個Block最少要佔用4億字節,假設形成很大的空間浪費。你須要實現一個新的hash表,接口定義:
interface SimpleMap<K, V> { K get(K key); V put(K key, V value); V remove(K key); }複製代碼
要求:
- 儘可能提升空間利用率
- 能夠犧牲部分性能
立即想到了線性探測、rehash等方法,以爲so easy,確承認以用線性探測後,就開始打草稿了,而後謄抄。須要注意的是del方法,我採起的思路是刪除後將tail節點填充到被刪除的位置。啪啪啪一頓寫,結果交了答案——我又意識到了大bug,思路就錯了;還發現了一個put方法中的一個死循環。不過這兩點都屬於我基本思路上的問題,就不秀錯誤代碼了。
回來本身想,如今補充正確代碼,未實現的非核心代碼參照HashMap:
public class LinearProbingHashMap<K, V> implements SimpleMap<K, V> {
@Override
public V get(Object key) {
if (key == null) {
return null;
}
int pos = indexFor(hash(key));
for (int i = 0;
i < table.length && table[pos] != null;
i++, pos = (pos + 1) % table.length) {
if (table[pos] == Entry.DELETED_ENTRY) {
continue;
}
if (table[pos].keyHash == key.hashCode()
&& (table[pos].key == key || table[pos].key.equals(key))) {
return table[pos].value;
}
}
return null;
}
@Override
public V put(K key, V value) {
if (key == null) {
return null;
}
int pos = indexFor(hash(key));
int lastEmptyPos = -1;
for (int i = 0;
i < table.length && table[pos] != null;
i++, pos = (pos + 1) % table.length) {
if (table[pos] == Entry.DELETED_ENTRY) {
lastEmptyPos = pos;
}
if (table[pos].keyHash == key.hashCode()
&& (table[pos].key == key || table[pos].key.equals(key))) {
V oldValee = table[pos].value;
table[pos].value = value;
return oldValee;
}
}
if (size + 1 < threshold) {
if (lastEmptyPos == -1) {
// assert table[pos] == null;
lastEmptyPos = pos;
}
table[lastEmptyPos] = new Entry<>(key, value);
size++;
return null;
}
LinearProbingHashMap<K, V> newMap = new LinearProbingHashMap<>(table.length * 2);
for (Entry<K, V> entry : table) {
// TODO: 2017/8/31 remove extra cost on copy entry
if (entry != null && entry != Entry.DELETED_ENTRY) {
newMap.put(entry.key, entry.value);
}
}
init(newMap); // update table, size, loadFactor, threshold, and etc.
put(key, value);
return null;
}
@Override
public V remove(Object key) {
if (key == null) {
return null;
}
int pos = indexFor(hash(key));
for (int i = 0;
i < table.length && table[pos] != null;
i++, pos = (pos + 1) % table.length) {
if (table[pos] == Entry.DELETED_ENTRY) {
continue;
}
if (table[pos].keyHash == key.hashCode()
&& (table[pos].key == key || table[pos].key.equals(key))) {
V oldValue = table[pos].value;
table[pos] = (Entry<K, V>) Entry.DELETED_ENTRY;
size--;
return oldValue;
}
}
return null;
}
}複製代碼
恩,我覺得到提問環節就是最後一輪技術面呢,問了問題1,面試官表示這是三面纔會聊的。總共有三輪面試,一面二面水平差很少,三面是技術leader。
正面評價:
負面評價:
等了三面的面試官好久都沒有來,我實在憋不住就寫了個紙條,把門開着去上廁所了。。。結果回來——握草女面試官!再看正臉,握草化妝了!怎麼看都不像是技術leader,一問才知道是HR。HR表示,技術leader在開會,剛纔跟他碰了一下,他說看前面兩面面試官評價都不錯,他不須要面了,因此直接跳到了HR面。
給我整的分不清是好消息仍是壞消息。據說這個leader是90年的,天大本科畢業就工做了,帶着一羣80、90後,很是屌,原本想借着面試瞻仰一下,真是惋惜。後面就是正常的HR面,不過感受頭條的HR問的真多啊,各類興趣愛好,評價,家庭什麼的。第一次經歷這陣仗,學到了學到了。
最後問我有沒有拿到其餘offer,老實回答了。我我的以爲,面試是個相互選擇的過程,但願公司和麪試者都能坦誠相待。
面試完,又分別跟大學同窗(頭條暑期實習)和濤神聊了聊,兩我的都反應,頭條的壓力很是大,可是同時也主動表示食堂好。看來只要公司願意給高價,什麼加班什麼壓力大均可以放一邊。學到了學到了。
上次阿里面試官簡歷的面試三原則很是管用,此次溝通明顯順暢了許多。頭條面試有一個好處,要不要你都會給通知,,,but,拒信和offer都要等好幾天啊,,,HR姐姐說盡可能本週五以前給通知,但願一切順利!!
給本身的建議:
本文連接:【面經】頭條-2017年8月30日,散招實習生
做者:猴子007
出處:monkeysayhi.github.io
本文基於 知識共享署名-相同方式共享 4.0 國際許可協議發佈,歡迎轉載,演繹或用於商業目的,可是必須保留本文的署名及連接。