你知道的越多,你不知道的越多前端
點贊再看,養成習慣java
本文GitHub https://github.com/JavaFamily 已收錄,有一線大廠面試點腦圖、我的聯繫方式和技術交流羣,歡迎Star和指教c++
消息隊列在互聯網技術存儲方面使用如此普遍,幾乎全部的後端技術面試官都要在消息隊列的使用和原理方面對小夥伴們進行360°的刁難。github
做爲一個在互聯網公司面一次拿一次Offer的麪霸,戰勝了無數競爭對手,每次都只能看到無數落寞的身影失望的離開,略感愧疚(請容許我使用一下誇張的修辭手法)。面試
因而在一個寂寞難耐的夜晚,我痛定思痛,決定開始寫《吊打面試官》系列,但願能幫助各位讀者之後面試勢如破竹,對面試官進行360°的反擊,吊打問你的面試官,讓一同面試的同僚瞠目結舌,瘋狂收割大廠Offer!算法
消息隊列系列前面兩章分別講了消息隊列的基礎知識,還有比較常見的問題和常見分佈式事務解決方案,那麼在實際開發過程當中,咱們使用頻率比較高的消息隊列中間件有哪些呢?數據庫
帥丙我工做以來接觸的消息隊列中間件有RocketMQ、Kafka、自研,是的由於我主要接觸的都是電商公司,相對而言業務體量還有場景來講都是他們比較適合,再加上杭州阿里系公司偏多,身邊同事或者公司老大基本都是阿里出來創業的,那在使用技術棧的時候阿里系的開源框架也就成了首選。apache
就算是自研的中間件多多少少也是借鑑RocketMQ、Kafka的優勢自研的,那我後面兩章就分別簡單的介紹下二者,他們分別在業務場景和大數據領域各自發光發熱。編程
那究竟是道德的淪喪,仍是人性的泯滅,讓咱們跟着敖丙走進RocketMQ的心裏世界。
RocketMQ是一個純Java、分佈式、隊列模型的開源消息中間件,前身是MetaQ,是阿里參考Kafka特色研發的一個隊列模型的消息中間件,後開源給apache基金會成爲了apache的頂級開源項目,具備高性能、高可靠、高實時、分佈式特色。
咱們再看下阿里給他取的名字哈:Rocket 火箭 阿里這是但願他上天呀,不過我以爲這個名字確實挺酷的。
2007年:淘寶實施了「五彩石」項目,「五彩石」用於將交易系統從單機變成分佈式,也是在這個過程當中產生了阿里巴巴第一代消息引擎——Notify。
2010年:阿里巴巴B2B部門基於ActiveMQ的5.1版本也開發了本身的一款消息引擎,稱爲Napoli,這款消息引擎在B2B裏面普遍地被使用,不只僅是在交易領域,在不少的後臺異步解耦等方面也獲得了普遍的應用。
2011年:業界出現瞭如今被不少大數據領域所推崇的Kafka消息引擎,阿里巴巴在研究了Kafka的總體機制和架構設計以後,基於Kafka的設計使用Java進行了徹底重寫並推出了MetaQ 1.0版本,主要是用於解決順序消息和海量堆積的問題。
2012年:阿里巴巴開源其自研的第三代分佈式消息中間件——RocketMQ。
通過幾年的技術打磨,阿里稱基於RocketMQ技術,目前雙十一當天消息容量可達到萬億級。
2016年11月:阿里將RocketMQ捐獻給Apache軟件基金會,正式成爲孵化項目。
阿里稱會將其打形成頂級項目。這是阿里邁出的一大步,由於加入到開源軟件基金會須要通過評審方的考覈與觀察。
坦率而言,業界還對國人的代碼開源參與度仍保持着刻板印象;而Apache基金會中的342個項目中,暫時還只有Kylin、CarbonData、Eagle 、Dubbo和 RocketMQ 共計五個中國技術人主導的項目。
2017年2月20日:RocketMQ正式發佈4.0版本,專家稱新版本適用於電商領域,金融領域,大數據領域,兼有物聯網領域的編程模型。
以上就是RocketMQ的總體發展歷史,其實在阿里巴巴內部圍繞着RocketMQ內核打造了三款產品,分別是MetaQ、Notify和Aliware MQ。
這三者分別採用了不一樣的模型,MetaQ主要使用了拉模型,解決了順序消息和海量堆積問題;Notify主要使用了推模型,解決了事務消息;而云產品Aliware MQ則是提供了商業化的版本。
在備戰2016年雙十一時,RocketMq團隊重點作了兩件事情,優化慢請求與統一存儲引擎。
RocketMQ天生爲金融互聯網領域而生,追求高可靠、高可用、高併發、低延遲,是一個阿里巴巴由內而外成功孕育的典範,除了阿里集團上千個應用外,根據咱們不徹底統計,國內至少有上百家單位、科研教育機構在使用。
RocketMQ在阿里集團也被普遍應用在訂單,交易,充值,流計算,消息推送,日誌流式處理,binglog分發等場景。
咱們直接去GitHub上看Apache對他的描述可能會好點
是的功能完整到爆炸基本上開發徹底夠用,什麼?看不懂專業詞彙的英文?
帥丙是暖男來的嘛,中文功能以下 ↓
GitHub地址:https://github.com/apache/rocketmq
他的核心模塊:
他主要有四大核心組成部分:NameServer、Broker、Producer以及Consumer四部分。
Tip:咱們能夠看到RocketMQ啥都是集羣部署的,這是他吞吐量大,高可用的緣由之一,集羣的模式也很花哨,能夠支持多master 模式、多master多slave異步複製模式、多 master多slave同步雙寫模式。
並且這個模式好像Kafka啊!(我這裏是廢話,自己就是阿里基於Kafka的不少特性研發的)。
主要負責對於源數據的管理,包括了對於Topic和路由信息的管理。
NameServer是一個功能齊全的服務器,其角色相似Dubbo中的Zookeeper,但NameServer與Zookeeper相比更輕量。主要是由於每一個NameServer節點互相之間是獨立的,沒有任何信息交互。
NameServer壓力不會太大,平時主要開銷是在維持心跳和提供Topic-Broker的關係數據。
但有一點須要注意,Broker向NameServer發心跳時, 會帶上當前本身所負責的全部Topic信息,若是Topic個數太多(萬級別),會致使一次心跳中,就Topic的數據就幾十M,網絡狀況差的話, 網絡傳輸失敗,心跳失敗,致使NameServer誤認爲Broker心跳失敗。
NameServer 被設計成幾乎無狀態的,能夠橫向擴展,節點之間相互之間無通訊,經過部署多臺機器來標記本身是一個僞集羣。
每一個 Broker 在啓動的時候會到 NameServer 註冊,Producer 在發送消息前會根據 Topic 到 NameServer 獲取到 Broker 的路由信息,Consumer 也會定時獲取 Topic 的路由信息。
因此從功能上看NameServer應該是和 ZooKeeper 差很少,聽說 RocketMQ 的早期版本確實是使用的 ZooKeeper ,後來改成了本身實現的 NameServer 。
咱們看一下Dubbo中註冊中心的角色,是否是真的一毛同樣,師出同門類似點真的不少:
消息生產者,負責產生消息,通常由業務系統負責產生消息。
Producer由用戶進行分佈式部署,消息由Producer經過多種負載均衡模式發送到Broker集羣,發送低延時,支持快速失敗。
RocketMQ 提供了三種方式發送消息:同步、異步和單向
同步發送:同步發送指消息發送方發出數據後會在收到接收方發回響應以後才發下一個數據包。通常用於重要通知消息,例如重要通知郵件、營銷短信。
異步發送:異步發送指發送方發出數據後,不等接收方發回響應,接着發送下個數據包,通常用於可能鏈路耗時較長而對響應時間敏感的業務場景,例如用戶視頻上傳後通知啓動轉碼服務。
單向發送:單向發送是指只負責發送消息而不等待服務器迴應且沒有回調函數觸發,適用於某些耗時很是短但對可靠性要求並不高的場景,例如日誌收集。
消息中轉角色,負責存儲消息,轉發消息。
消息消費者,負責消費消息,通常是後臺系統負責異步消費。
Consumer也由用戶部署,支持PUSH和PULL兩種消費模式,支持集羣消費和廣播消息,提供實時的消息訂閱機制。
Pull:拉取型消費者(Pull Consumer)主動從消息服務器拉取信息,只要批量拉取到消息,用戶應用就會啓動消費過程,因此 Pull 稱爲主動消費型。
Push:推送型消費者(Push Consumer)封裝了消息的拉取、消費進度和其餘的內部維護工做,將消息到達時執行的回調接口留給用戶應用程序來實現。因此 Push 稱爲被動消費類型,但從實現上看仍是從消息服務器中拉取消息,不一樣於 Pull 的是 Push 首先要註冊消費監聽器,當監聽器處觸發後纔開始消費消息。
Message(消息)就是要傳輸的信息。
一條消息必須有一個主題(Topic),主題能夠看作是你的信件要郵寄的地址。
一條消息也能夠擁有一個可選的標籤(Tag)和額處的鍵值對,它們能夠用於設置一個業務 Key 並在 Broker 上查找此消息以便在開發期間查找問題。
Topic(主題)能夠看作消息的規類,它是消息的第一級類型。好比一個電商系統能夠分爲:交易消息、物流消息等,一條消息必須有一個 Topic 。
Topic 與生產者和消費者的關係很是鬆散,一個 Topic 能夠有0個、1個、多個生產者向其發送消息,一個生產者也能夠同時向不一樣的 Topic 發送消息。
一個 Topic 也能夠被 0個、1個、多個消費者訂閱。
Tag(標籤)能夠看做子主題,它是消息的第二級類型,用於爲用戶提供額外的靈活性。使用標籤,同一業務模塊不一樣目的的消息就能夠用相同 Topic 而不一樣的 Tag 來標識。好比交易消息又能夠分爲:交易建立消息、交易完成消息等,一條消息能夠沒有 Tag 。
標籤有助於保持您的代碼乾淨和連貫,而且還能夠爲 RocketMQ 提供的查詢系統提供幫助。
分組,一個組能夠訂閱多個Topic。
分爲ProducerGroup,ConsumerGroup,表明某一類的生產者和消費者,通常來講同一個服務能夠做爲Group,同一個Group通常來講發送和消費的消息都是同樣的
在Kafka中叫Partition,每一個Queue內部是有序的,在RocketMQ中分爲讀和寫兩種隊列,通常來講讀寫隊列數量一致,若是不一致就會出現不少問題。
Message Queue(消息隊列),主題被劃分爲一個或多個子主題,即消息隊列。
一個 Topic 下能夠設置多個消息隊列,發送消息時執行該消息的 Topic ,RocketMQ 會輪詢該 Topic 下的全部隊列將消息發出去。
消息的物理管理單位。一個Topic下能夠有多個Queue,Queue的引入使得消息的存儲能夠分佈式集羣化,具備了水平擴展能力。
在RocketMQ 中,全部消息隊列都是持久化,長度無限的數據結構,所謂長度無限是指隊列中的每一個存儲單元都是定長,訪問其中的存儲單元使用Offset 來訪問,Offset 爲 java long 類型,64 位,理論上在 100年內不會溢出,因此認爲是長度無限。
也能夠認爲 Message Queue 是一個長度無限的數組,Offset 就是下標。
消息消費模式有兩種:Clustering(集羣消費)和Broadcasting(廣播消費)。
默認狀況下就是集羣消費,該模式下一個消費者集羣共同消費一個主題的多個隊列,一個隊列只會被一個消費者消費,若是某個消費者掛掉,分組內其它消費者會接替掛掉的消費者繼續消費。
而廣播消費消息會發給消費者組中的每個消費者進行消費。
Message Order(消息順序)有兩種:Orderly(順序消費)和Concurrently(並行消費)。
順序消費表示消息消費的順序同生產者爲每一個消息隊列發送的順序一致,因此若是正在處理全局順序是強制性的場景,須要確保使用的主題只有一個消息隊列。
並行消費再也不保證消息順序,消費的最大並行數量受每一個消費者客戶端指定的線程池限制。
Producer 與 NameServer集羣中的其中一個節點(隨機選擇)創建長鏈接,按期從 NameServer 獲取 Topic 路由信息,並向提供 Topic 服務的 Broker Master 創建長鏈接,且定時向 Broker 發送心跳。
Producer 只能將消息發送到 Broker master,可是 Consumer 則不同,它同時和提供 Topic 服務的 Master 和 Slave創建長鏈接,既能夠從 Broker Master 訂閱消息,也能夠從 Broker Slave 訂閱消息。
具體以下圖:
我上面說過他跟Dubbo像不是我瞎說的,就連他的註冊過程都很像Dubbo的服務暴露過程。
是否是以爲很簡單,可是你同時也產生了好奇心,每一步是怎麼初始化啓動的呢?
帥丙呀就知道你們都是求知慾極強的人才,這不我都準備好了,咱們一步步分析一下。
主要是人才羣裏的仔要求我寫出來。。。(文末有進羣方式)
在org.apache.rocketmq.namesrv目錄下的NamesrvStartup這個啓動類基本上描述了他的啓動過程咱們能夠看一下代碼:
第一步是初始化配置
建立NamesrvController實例,並開啓兩個定時任務:
每隔10s掃描一次Broker,移除處於不激活的Broker;
每隔10s打印一次KV配置。
第三步註冊鉤子函數,啓動服務器並監聽Broker。
NameService還有不少東西的哈我這裏就介紹他的啓動流程,你們還能夠去看看代碼,仍是頗有意思的,好比路由註冊會發送心跳包,還有心跳包的處理流程,路由刪除,路由發現等等。
Tip:原本我想貼不少源碼的,後面跟歪歪(Java3y)討論了好久作出了不貼的決定,你們理解過程爲主!我主要是作只是掃盲還有一些痛點分析嘛,深究仍是得你們花時間,我要啥都介紹篇幅就不夠了。
鏈路很長涉及的細節也多,我就發一下鏈路圖。
Producer是消息發送方,那他怎麼發送的呢?
經過輪訓,Producer輪訓某個Topic下面的全部隊列實現發送方的負載均衡
Broker在RocketMQ中是進行處理Producer發送消息請求,Consumer消費消息的請求,而且進行消息的持久化,以及HA策略和服務端過濾,就是集羣中很重的工做都是交給了Broker進行處理。
Broker模塊是經過BrokerStartup進行啓動的,會實例化BrokerController,而且調用其初始化方法
你們去看Broker的源碼的話會發現,他的初始化流程很冗長,會根據配置建立不少線程池主要用來發送消息、拉取消息、查詢消息、客戶端管理和消費者管理,也有不少定時任務,同時也註冊了不少請求處理器,用來發送拉取消息查詢消息的。
不說了直接懟圖吧!要死了,下次我仍是作掃盲,寫點爽文吧555
Consumer是消息接受,那他怎麼接收消息的呢?
消費端會經過RebalanceService線程,10秒鐘作一次基於Topic下的全部隊列負載。
RocketMQ優勢:
單機吞吐量:十萬級
可用性:很是高,分佈式架構
消息可靠性:通過參數優化配置,消息能夠作到0丟失
功能支持:MQ功能較爲完善,仍是分佈式的,擴展性好
支持10億級別的消息堆積,不會由於堆積致使性能降低
源碼是java,咱們能夠本身閱讀源碼,定製本身公司的MQ,能夠掌控
天生爲金融互聯網領域而生,對於可靠性要求很高的場景,尤爲是電商裏面的訂單扣款,以及業務削峯,在大量交易涌入時,後端可能沒法及時處理的狀況
RoketMQ在穩定性上可能更值得信賴,這些業務場景在阿里雙11已經經歷了屢次考驗,若是你的業務有上述併發場景,建議能夠選擇RocketMQ
RocketMQ缺點:
支持的客戶端語言很少,目前是java及c++,其中c++不成熟
社區活躍度不是特別活躍那種
沒有在 mq 核心中去實現JMS等接口,有些系統要遷移須要修改大量代碼
去重原則:使用業務端邏輯保持冪等性
冪等性:就是用戶對於同一操做發起的一次請求或者屢次請求的結果是一致的,不會由於屢次點擊而產生了反作用,數據庫的結果都是惟一的,不可變的。
只要保持冪等性,無論來多少條重複消息,最後處理的結果都同樣,須要業務端來實現。
去重策略:保證每條消息都有惟一編號(好比惟一流水號),且保證消息處理成功與去重表的日誌同時出現。
創建一個消息表,拿到這個消息作數據庫的insert操做。給這個消息作一個惟一主鍵(primary key)或者惟一約束,那麼就算出現重複消費的狀況,就會致使主鍵衝突,那麼就再也不處理這條消息。
消息領域有一個對消息投遞的QoS定義,分爲:
QoS:Quality of Service,服務質量
幾乎全部的MQ產品都聲稱本身作到了At least once。
既然是至少一次,那避免不了消息重複,尤爲是在分佈式網絡環境下。
好比:網絡緣由閃斷,ACK返回失敗等等故障,確認信息沒有傳送到消息隊列,致使消息隊列不知道本身已經消費過該消息了,再次將該消息分發給其餘的消費者。
不一樣的消息隊列發送的確認信息形式不一樣,例如RabbitMQ是發送一個ACK確認消息,RocketMQ是返回一個CONSUME_SUCCESS成功標誌,Kafka實際上有個offset的概念。
RocketMQ沒有內置消息去重的解決方案,最新版本是否支持還需確認。
當咱們選擇好了集羣模式以後,那麼咱們須要關心的就是怎麼去存儲和複製這個數據,RocketMQ對消息的刷盤提供了同步和異步的策略來知足咱們的,當咱們選擇同步刷盤以後,若是刷盤超時會給返回FLUSH_DISK_TIMEOUT,若是是異步刷盤不會返回刷盤相關信息,選擇同步刷盤能夠盡最大程度知足咱們的消息不會丟失。
除了存儲有選擇以後,咱們的主從同步提供了同步和異步兩種模式來進行復制,固然選擇同步能夠提高可用性,可是消息的發送RT時間會降低10%左右。
RocketMQ採用的是混合型的存儲結構,即爲Broker單個實例下全部的隊列共用一個日誌數據文件(即爲CommitLog)來存儲。
而Kafka採用的是獨立型的存儲結構,每一個隊列一個文件。
這裏帥丙認爲,RocketMQ採用混合型存儲結構的缺點在於,會存在較多的隨機讀操做,所以讀的效率偏低。同時消費消息須要依賴ConsumeQueue,構建該邏輯消費隊列須要必定開銷。
Broker 在消息的存取時直接操做的是內存(內存映射文件),這能夠提供系統的吞吐量,可是沒法避免機器掉電時數據丟失,因此須要持久化到磁盤中。
刷盤的最終實現都是使用NIO中的 MappedByteBuffer.force() 將映射區的數據寫入到磁盤,若是是同步刷盤的話,在Broker把消息寫到CommitLog映射區後,就會等待寫入完成。
異步而言,只是喚醒對應的線程,不保證執行的時機,流程如圖所示。
我簡單的說一下咱們使用的RocketMQ裏面的一個簡單實現吧。
Tip:爲啥用RocketMQ舉例呢,這玩意是阿里開源的,我問了下身邊的朋友不少公司都有使用,因此讀者大機率是這個的話我就用這個舉例吧,具體的細節我後面會在RocketMQ和Kafka各自章節說到。
生產者消費者通常須要保證順序消息的話,可能就是一個業務場景下的,好比訂單的建立、支付、發貨、收貨。
那這些東西是否是一個訂單號呢?一個訂單的確定是一個訂單號的說,那簡單了呀。
一個topic下有多個隊列,爲了保證發送有序,RocketMQ提供了MessageQueueSelector隊列選擇機制,他有三種實現:
咱們可以使用Hash取模法,讓同一個訂單發送到同一個隊列中,再使用同步發送,只有同個訂單的建立消息發送成功,再發送支付消息。這樣,咱們保證了發送有序。
RocketMQ的topic內的隊列機制,能夠保證存儲知足FIFO(First Input First Output 簡單說就是指先進先出),剩下的只須要消費者順序消費便可。
RocketMQ僅保證順序發送,順序消費由消費者業務保證!!!
這裏很好理解,一個訂單你發送的時候放到一個隊列裏面去,你同一個的訂單號Hash一下是否是仍是同樣的結果,那確定是一個消費者消費,那順序是否是就保證了?
真正的順序消費不一樣的中間件都有本身的不一樣實現我這裏就舉個例子,你們思路理解下。
是指暫不能被Consumer消費的消息。Producer 已經把消息成功發送到了 Broker 端,但此消息被標記爲暫不能投遞
狀態,處於該種狀態下的消息稱爲半消息。須要 Producer
對消息的二次確認
後,Consumer才能去消費它。
因爲網絡閃段,生產者應用重啓等緣由。致使 Producer 端一直沒有對 Half Message(半消息) 進行 二次確認。這是Brock服務器會定時掃描長期處於半消息的消息
,會
主動詢問 Producer端 該消息的最終狀態(Commit或者Rollback),該消息即爲 消息回查。
Broker的Buffer一般指的是Broker中一個隊列的內存Buffer大小,這類Buffer一般大小有限。
另外,RocketMQ沒有內存Buffer概念,RocketMQ的隊列都是持久化磁盤,數據按期清除。
RocketMQ同其餘MQ有很是顯著的區別,RocketMQ的內存Buffer抽象成一個無限長度的隊列,無論有多少數據進來都能裝得下,這個無限是有前提的,Broker會按期刪除過時的數據。
例如Broker只保存3天的消息,那麼這個Buffer雖然長度無限,可是3天前的數據會被從隊尾刪除。
回溯消費是指Consumer已經消費成功的消息,因爲業務上的需求須要從新消費,要支持此功能,Broker在向Consumer投遞成功消息後,消息仍然須要保留。而且從新消費通常是按照時間維度。
例如因爲Consumer系統故障,恢復後須要從新消費1小時前的數據,那麼Broker要提供一種機制,能夠按照時間維度來回退消費進度。
RocketMQ支持按照時間回溯消費,時間維度精確到毫秒,能夠向前回溯,也能夠向後回溯。
消息中間件的主要功能是異步解耦,還有個重要功能是擋住前端的數據洪峯,保證後端系統的穩定性,這就要求消息中間件具備必定的消息堆積能力,消息堆積分如下兩種狀況:
定時消息是指消息發到Broker後,不能馬上被Consumer消費,要到特定的時間點或者等待特定的時間後才能被消費。
若是要支持任意的時間精度,在Broker層面,必需要作消息排序,若是再涉及到持久化,那麼消息排序要不可避免的產生巨大性能開銷。
RocketMQ支持定時消息,可是不支持任意時間精度,支持特定的level,例如定時5s,10s,1m等。
寫這種單純介紹中間件的枯燥乏味,你們看起來估計也累,目前已經破一萬個字了,之後我這種類型的少寫,你們總是讓我寫點深度的,我說真的不少東西我源碼一貼,看都沒人看。
Kafka我就不發博客了,你們能夠去GItHub上第一時間閱讀,後面會出怎麼搭建項目在服務器的教程,還有一些大牛我的經歷和我的書單的東西,今年應該先這麼寫,主要是真心太忙了,望理解。
我也不過多描述了,反正嘛網絡上重拳出擊嘛,現實中惟惟諾諾,讓他說理由也說不出來,不回我。
他說的是下面這個場景多線程的狀況,就是第一個線程還沒走完,第二個如今進來,也判斷沒處理過那不就兩個都繼續加了麼?
訂單號+業務場景,組成一個惟一主鍵,你插入數據庫只能成功第一個,後續的都會報錯的,報違反惟一主鍵的錯誤。
還有就是有人疑惑爲啥不直接就不判斷就等他插入的時候報錯,丟掉後續的就行了?
你要知道報錯有不少種,你哪裏知道不是數據庫掛了的錯?或者別的運行時異常?
不過你若是能夠作到拋特定的異常也能夠,反正咱們要減小數據庫的報錯,若是併發大,像我如今負責的系統都是10W+QPS,那日誌會打滿瘋狂報警的。(就是正常狀況咱們都常常報警)
解決問題的思路有不少,噴我能夠,講清楚問題,講清楚你的理由。
不少你們都只是單方面的知識攝入,就這樣還要噴我,還有一上來就問我爲啥今天沒發文章,我欠你的?我工做日上班,週六週日都懟上去了,時間有限啊,哥哥。
你們都有本身的事情,寫文章也耗時耗腦,不免出錯,還望理解。
好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是人才。
我後面會每週都更新幾篇《吊打面試官》系列和互聯網經常使用技術棧相關的文章,很是感謝人才們能看到這裏,若是這個文章寫得還不錯,以爲「敖丙」我有點東西的話 求點贊👍 求關注❤️ 求分享👥 對暖男我來講真的 很是有用!!!
創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見!
敖丙 | 文 【原創】【轉載請聯繫本人】 若是本篇博客有任何錯誤,請批評指教,不勝感激 !
《吊打面試官》系列每週持續更新,能夠關注個人公衆號「 JavaFamily 」第一時間閱讀和催更(公衆號比博客早一到兩篇喲),本文GitHubhttps://github.com/JavaFamily 已收錄,有一線大廠面試點思惟導圖,歡迎Star和完善,裏面也有我我的聯繫方式有什麼問題也能夠直接找我,也有技術交流羣,咱們一塊兒有點東西。