头条终面:写个消息中间件
這種設(shè)計(jì)類問(wèn)題想必大家都不陌生,面試時(shí)或多或少都能碰到。
比如如何寫(xiě)一個(gè)線程池?如何寫(xiě)一個(gè) HashMap ?如何寫(xiě)一個(gè) RPC 框架等等,當(dāng)然這里的寫(xiě)不是真的叫你用代碼寫(xiě)出來(lái),只是說(shuō)說(shuō)設(shè)計(jì)理念,整體架構(gòu)。
這個(gè)面試題來(lái)自于一個(gè)讀者的字節(jié)面試經(jīng)歷,我會(huì)從面試技巧和消息中間件的設(shè)計(jì)兩個(gè)方面闡述。
我覺(jué)得重點(diǎn)在于面試技巧,因?yàn)樗ㄓ谩?/p>
?
兩種極端的情況
大多數(shù)同學(xué)遇到這種問(wèn)題會(huì)出現(xiàn)兩種極端的情況:
第一種:一臉懵逼,兩眼無(wú)神,不知從何說(shuō)起,萬(wàn)般思緒,都化作一聲嘆息。
第二種:夸夸其談,像是口中架起了一把加特林,噠噠噠噠噠噠噠噠,還冒著藍(lán)火。
第一種不用說(shuō)了,好一點(diǎn)的面試官可能會(huì)引導(dǎo)你,會(huì)問(wèn)一些提示性的問(wèn)題,一步一步地帶你漸入佳境,當(dāng)然你要是胸中無(wú)點(diǎn)滴,那還是沒(méi)救的,場(chǎng)面就異常地尷尬。
第二種會(huì)把面試官整蒙了,或許你真的懂很多,很多細(xì)節(jié)也都清晰,但是你不能一股腦兒的都拋出來(lái),這會(huì)顯得你抓不住重點(diǎn)。
?
面試官也是人
這點(diǎn)其實(shí)很關(guān)鍵,很多把面試官當(dāng)成一個(gè)莫得感情的提問(wèn)機(jī)器人,覺(jué)得他無(wú)所不能可以完全 get 到你的點(diǎn),殊不知你引以為傲的細(xì)節(jié)回答,他可能覺(jué)得你在說(shuō)蛇皮。
是人就會(huì)有感情,就需要交流,好的面試官會(huì)把控整體進(jìn)度,從拉家常開(kāi)始,讓場(chǎng)子熱起來(lái)再一步一步的深挖。
當(dāng)然也有一些面試官比較弱,這時(shí)候就需要你來(lái)特意地流出一點(diǎn)空白,來(lái)讓面試官涂鴉,讓面試官感覺(jué)你這人就很舒服,你這波就穩(wěn)了。
當(dāng)然即使面對(duì)著把控全場(chǎng)的面試官你也得主動(dòng)出擊,每個(gè)人都有自己的擅長(zhǎng)點(diǎn),你需要引導(dǎo)面試官來(lái)詢問(wèn)你的長(zhǎng)處。
?
正確的回答姿勢(shì)
正確的回答姿勢(shì)是 BFS(廣度優(yōu)先搜索) 而不是 DFS (深度優(yōu)先搜索),什么意思呢?
就是我們需要先從大局上講出需要設(shè)計(jì)的東西的重點(diǎn),然后再等待面試官的繼續(xù)提問(wèn),深挖。
我們需要揣摩面試官的心理,從他的提問(wèn)可以看出他想要知道的重點(diǎn)是哪個(gè)方向的。
比如就拿 HashMap 來(lái)說(shuō),你簡(jiǎn)單的把獲取、寫(xiě)入、沖突處理、擴(kuò)容啥的都說(shuō)了,然后等待面試官接下來(lái)的提問(wèn),有可能會(huì)往線程安全方面深入,也有可能會(huì)往擴(kuò)容方向再挖,比如引出 Redis 的 hash 擴(kuò)容等等。
所以說(shuō)給面試官留提問(wèn)的機(jī)會(huì),抓住他的喜好或者說(shuō)熟知的方向回答,這樣如果你答得好,相互之間談的來(lái),面試官會(huì)對(duì)你高度認(rèn)可。
而且在說(shuō)各設(shè)計(jì)要點(diǎn)的時(shí)候也要注意停頓,要留機(jī)會(huì)給面試官插話,讓面試官充分參與你的設(shè)計(jì)。
還是拿 HashMap 作為例子,比如你說(shuō)了獲取、寫(xiě)入、沖突之后稍作停頓,這時(shí)候大概率面試官還會(huì)問(wèn)還有嗎?讓面試官有參與感,讓他感覺(jué)經(jīng)過(guò)他的引導(dǎo)這個(gè)設(shè)計(jì)才逐步地完善。
當(dāng)然如果不問(wèn)也沒(méi)事,你停頓下繼續(xù)說(shuō)就行。
讓面試成為一場(chǎng)技術(shù)交流,這是面試的最高境界,相信面試完了之后雙方都會(huì)有意猶未盡的感覺(jué),惺惺相惜就是這么來(lái)的。
但是這種場(chǎng)景也不是這么容易碰到的,首先你和面試官得有相同方向的喜好,比如你對(duì) JVM 有很深入的研究,而面試官對(duì)存儲(chǔ)方面有很深入的研究,JVM 懂的不深,這樣就碰不出火花了。
所以說(shuō)會(huì)有很多人碰到這么個(gè)情況:我面這個(gè)公司一面掛,另一家公司面面超神,這都是很正常的。
當(dāng)然你要是說(shuō)你全能,那當(dāng)我沒(méi)說(shuō)。
?
小結(jié)一下面試技巧
首先要正確的看待面試官,你和面試官是同等的,不要一來(lái)就低聲下氣的。
其次回答問(wèn)題需要抓住重點(diǎn),不要一股腦兒的把你知道的都說(shuō)了,要留白待面試官提問(wèn)。
要把控面試的節(jié)奏,往自己熟知的方向上引。
?
如何寫(xiě)個(gè)消息中間件
接下來(lái)咱們?cè)倏纯慈绾螌?xiě)個(gè)消息中間件。
首先我們需要明確地提出消息中間件的幾個(gè)重要角色,分別是生產(chǎn)者、消費(fèi)者、Broker、注冊(cè)中心。
簡(jiǎn)述下消息中間件數(shù)據(jù)流轉(zhuǎn)過(guò)程,無(wú)非就是生產(chǎn)者生成消息,發(fā)送至 Broker,Broker 可以暫緩消息,然后消費(fèi)者再?gòu)?Broker 獲取消息,用于消費(fèi)。
而注冊(cè)中心用于服務(wù)的發(fā)現(xiàn)包括:Broker 的發(fā)現(xiàn)、生產(chǎn)者的發(fā)現(xiàn)、消費(fèi)者的發(fā)現(xiàn),當(dāng)然還包括下線,可以說(shuō)服務(wù)的高可用離不開(kāi)注冊(cè)中心。
然后開(kāi)始簡(jiǎn)述實(shí)現(xiàn)要點(diǎn),可以同通信講起:各模塊的通信可以基于 Netty 然后自定義協(xié)議來(lái)實(shí)現(xiàn),注冊(cè)中心可以利用 zookeeper、consul、eureka、nacos 等等,也可以像 RocketMQ 自己實(shí)現(xiàn)簡(jiǎn)單的 namesrv (這一句話就都是關(guān)鍵詞)。
為了考慮擴(kuò)容和整體的性能,采用分布式的思想,像 Kafka 一樣采取分區(qū)理念,一個(gè) Topic 分為多個(gè) partition,并且為保證數(shù)據(jù)可靠性,采取多副本存儲(chǔ),即 Leader 和 follower,根據(jù)性能和數(shù)據(jù)可靠的權(quán)衡提供異步和同步的刷盤(pán)存儲(chǔ)。
并且利用選舉算法保證 Leader 掛了之后 follower 可以頂上,保證消息隊(duì)列的高可用。
也同樣為了提高消息隊(duì)列的可靠性利用本地文件系統(tǒng)來(lái)存儲(chǔ)消息,并且采用順序?qū)懙姆绞絹?lái)提高性能。
可根據(jù)消息隊(duì)列的特性利用內(nèi)存映射、零拷貝進(jìn)一步的提升性能,還可利用像 Kafka 這種批處理思想提高整體的吞吐。
至此就差不多了,該說(shuō)的要點(diǎn)說(shuō)的都差不多了,面試官心里已經(jīng)想,這人好像有點(diǎn)東西。
之后可以深挖的點(diǎn)就很多了,比如提到的 Netty,各種注冊(cè)中心就能問(wèn)很多,比如各注冊(cè)中心之間的選型對(duì)比等。
你還提到了選舉算法,所以可能會(huì)問(wèn) Bully 算法、Raft 算法、ZAB 算法等等。
你還提到了分區(qū),可能會(huì)問(wèn)這個(gè)分區(qū)和 RocketMQ 的隊(duì)列有什么不同啊?具體分區(qū)要怎么實(shí)現(xiàn)?
然后你提到順序?qū)?#xff0c;可能會(huì)問(wèn)為什么要順序?qū)懓?#xff1f;你說(shuō)的內(nèi)存映射和零拷貝又是什么啊?那你知道 RocketMQ 和 Kafka 用了哪個(gè)嗎?
當(dāng)然還有可能問(wèn)各種細(xì)節(jié),比如消息的寫(xiě)入如何存儲(chǔ)、消息的索引如何生成等等,來(lái)深挖看你有沒(méi)有看過(guò)消息中間件的源碼。
可以問(wèn)的還很多,這篇文章我也不可能每個(gè)點(diǎn)都延伸開(kāi)說(shuō),這些知識(shí)點(diǎn)還是得靠大家日積月累和平日的多加思考。
當(dāng)然日后的文章可以寫(xiě)一寫(xiě)今天提到的一些點(diǎn),比如 Netty、選舉算法啊,多種注冊(cè)中心對(duì)比啊啥的。
?
面試官想問(wèn)的是什么
再回到這個(gè)面試題,其實(shí)面試官想問(wèn)的就是大方向上的設(shè)計(jì),包括整體的架構(gòu)、數(shù)據(jù)的流轉(zhuǎn)和一些特性的把握,所以對(duì)于這個(gè)問(wèn)題他想聽(tīng)到的就是那些重點(diǎn),而不是那些細(xì)節(jié)。
而繼續(xù)的深挖取決于你回答這個(gè)問(wèn)題時(shí)提出的各個(gè)關(guān)鍵詞,對(duì)于面試官自身而言熟悉的詞一抓到,他就已經(jīng)知道下一步要問(wèn)你什么了。
所以在回答面試官的時(shí)候不僅要 get 到他的點(diǎn),還得為之后的回答鋪路,不會(huì)說(shuō)的點(diǎn)不要提,擅長(zhǎng)的點(diǎn)多提提。
?
最后
之前我已經(jīng)提到了,這篇文章的重點(diǎn)其實(shí)不在于如何回答寫(xiě)一個(gè)消息中間件,而在于面試的技巧。
因?yàn)槊嬖囶}千千萬(wàn),而技巧掌握了那么千千萬(wàn)的面試題都適用。
我還想提一下關(guān)于面試的一些個(gè)人看法,我個(gè)人是面試驅(qū)動(dòng)學(xué)習(xí)型選手,我學(xué)習(xí)的動(dòng)力就是面試,我享受面試官問(wèn)我啥我都嘴角一翹微微一笑的那種不羈。
但是我不提倡那種純粹背面試題的做法,學(xué)習(xí)是一個(gè)日積月累的過(guò)程,就像我每篇文末說(shuō)的,從一點(diǎn)點(diǎn)到億點(diǎn)點(diǎn),又像我每篇開(kāi)頭都會(huì)提的,每個(gè)時(shí)代,都不會(huì)虧待會(huì)學(xué)習(xí)的人。
我的面試驅(qū)動(dòng)不僅僅是說(shuō)為了面試而學(xué)習(xí),還要以面試場(chǎng)景來(lái)學(xué)習(xí),什么意思呢?
學(xué)任何一種東西,都模擬一個(gè)面試官在你前面,讓他從各種角度向你提問(wèn),驅(qū)動(dòng)你全方位的理解一個(gè)知識(shí)點(diǎn),這才是我說(shuō)的面試驅(qū)動(dòng)學(xué)習(xí)型選手。
所以如果你看過(guò)我之前的文章會(huì)發(fā)現(xiàn)我經(jīng)常會(huì)提出為什么呢,然后再作答。
還有一點(diǎn)要注意,動(dòng)手能力,這很關(guān)鍵。
Talk is cheap, show me the code。
有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號(hào)
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的头条终面:写个消息中间件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 面试官:Netty的线程模型可不是Rea
- 下一篇: ZT Android4.2蓝牙基础架构