头条是这批次面试中的一个理想公司,基础架构部。从两轮面试的状况来看,面试官的素质很是高,面试经验也比较丰富。一方面提问抓的准,不会在你明确表示准备不足的方面硬扣;一方面深度广度、代码风格均有涉及。不过总共只让我写了两道代码题,但愿不是放水。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 国际许可协议发布,欢迎转载,演绎或用于商业目的,可是必须保留本文的署名及连接。