java數組排序算法,看完這一篇就夠了!

java數組排序算法,看完這一篇就夠了!

一、爲何要使用消息隊列?

分析:一個用消息隊列的人,不知道爲啥用,有點尷尬。沒有複習這點,很容易被問蒙,而後就開始胡扯了。前端

回答:這個問題,咱只答三個最主要的應用場景(不能否認還有其餘的,可是隻答三個主要的),即如下六個字:解耦、異步、削峯java

(1)解耦

傳統模式:git

?傳統模式的缺點:程序員

  • 系統間耦合性太強,如上圖所示,系統A在代碼中直接調用系統B和系統C的代碼,若是未來D系統接入,系統A還須要修改代碼,過於麻煩!

中間件模式:web

中間件模式的的優勢:面試

  • 將消息寫入消息隊列,須要消息的系統本身從消息隊列中訂閱,從而系統A不須要作任何修改。

(2)異步

傳統模式:redis

?傳統模式的缺點:算法

  • 一些非必要的業務邏輯以同步的方式運行,太耗費時間。

中間件模式:數據庫

中間件模式的的優勢:數組

  • 將消息寫入消息隊列,非必要的業務邏輯以異步的方式運行,加快響應速度

(3)削峯

傳統模式

傳統模式的缺點:

  • 併發量大的時候,全部的請求直接懟到數據庫,形成數據庫鏈接異常

中間件模式:

中間件模式的的優勢:

  • 系統A慢慢的按照數據庫能處理的併發量,從消息隊列中慢慢拉取消息。在生產中,這個短暫的高峯期積壓是容許的。

二、使用了消息隊列會有什麼缺點?

分析:一個使用了MQ的項目,若是連這個問題都沒有考慮過,就把MQ引進去了,那就給本身的項目帶來了風險。

咱們引入一個技術,要對這個技術的弊端有充分的認識,才能作好預防。要記住,不要給公司挖坑!

回答:回答也很容易,從如下兩個個角度來答

  • 系統可用性下降:

    你想啊,原本其餘系統只要運行好好的,那你的系統就是正常的。

    如今你非要加個消息隊列進去,那消息隊列掛了,你的系統不是呵呵了。所以,系統可用性下降

  • 系統複雜性增長:

    要多考慮不少方面的問題,好比一致性問題、如何保證消息不被重複消費,如何保證保證消息可靠傳輸。

    所以,須要考慮的東西更多,系統複雜性增大。

可是,咱們該用仍是要用的。

三、消息隊列如何選型?

先說一下,博主只會ActiveMQ,RabbitMQ,RocketMQ,Kafka,對什麼ZeroMQ等其餘MQ沒啥理解,所以只能基於這四種MQ給出回答。

分析:既然在項目中用了MQ,確定事先要對業界流行的MQ進行調研,若是連每種MQ的優缺點都沒了解清楚,就拍腦殼依據喜愛,用了某種MQ,仍是給項目挖坑。

若是面試官問:"你爲何用這種MQ?。"你直接回答"領導決定的。"這種回答就很LOW了。

仍是那句話,不要給公司挖坑。

咱們能夠看出,RabbitMQ版本發佈比ActiveMq頻繁不少。至於RocketMQ和kafka就不帶你們看了,總之也比ActiveMQ活躍的多。詳情,可自行查閱。

再來一個性能對比表

綜合上面的材料得出如下兩點:

(1)中小型軟件公司,建議選RabbitMQ.

一方面,erlang語言天生具有高併發的特性,並且他的管理界面用起來十分方便。

正所謂,成也蕭何,敗也蕭何!他的弊端也在這裏,雖然RabbitMQ是開源的,然而國內有幾個能定製化開發erlang的程序員呢?

所幸,RabbitMQ的社區十分活躍,能夠解決開發過程當中遇到的bug,這點對於中小型公司來講十分重要。

不考慮rocketmq和kafka的緣由是,一方面中小型軟件公司不如互聯網公司,數據量沒那麼大,選消息中間件,應首選功能比較完備的,因此kafka排除。

不考慮rocketmq的緣由是,rocketmq是阿里出品,若是阿里放棄維護rocketmq,中小型公司通常抽不出人來進行rocketmq的定製化開發,所以不推薦。

(2)大型軟件公司,根據具體使用在rocketMq和kafka之間二選一

一方面,大型軟件公司,具有足夠的資金搭建分佈式環境,也具有足夠大的數據量。

針對rocketMQ,大型軟件公司也能夠抽出人手對rocketMQ進行定製化開發,畢竟國內有能力改JAVA源碼的人,仍是至關多的。

至於kafka,根據業務場景選擇,若是有日誌採集功能,確定是首選kafka了。具體該選哪一個,看使用場景。

四、如何保證消息隊列是高可用的?

分析:在第二點說過了,引入消息隊列後,系統的可用性降低。在生產中,沒人使用單機模式的消息隊列。

所以,做爲一個合格的程序員,應該對消息隊列的高可用有很深入的瞭解。

若是面試的時候,面試官問,大家的消息中間件如何保證高可用的?

若是你的回答只是代表本身只會訂閱和發佈消息,面試官就會懷疑你是否是隻是本身搭着玩,壓根沒在生產用過。

所以,請作一個愛思考,會思考,懂思考的程序員。

回答:這問題,其實要對消息隊列的集羣模式要有深入瞭解,纔好回答。

以rcoketMQ爲例,他的集羣就有多master 模式、多master多slave異步複製模式、多 master多slave同步雙寫模式。

多master多slave模式部署架構圖(網上找的,偷個懶,懶得畫):

其實博主第一眼看到這個圖,就以爲和kafka好像,只是NameServer集羣,在kafka中是用zookeeper代替,都是用來保存和發現master和slave用的。

通訊過程以下:

Producer 與 NameServer集羣中的其中一個節點(隨機選擇)創建長鏈接,按期從 NameServer 獲取 Topic 路由信息,並向提供 Topic 服務的 Broker Master 創建長鏈接,且定時向 Broker 發送心跳。

Producer 只能將消息發送到 Broker master,可是 Consumer 則不同,它同時和提供 Topic 服務的 Master 和 Slave創建長鏈接,既能夠從 Broker Master 訂閱消息,也能夠從 Broker Slave 訂閱消息。

那麼kafka呢,爲了對比說明直接上kafka的拓補架構圖(也是找的,懶得畫)

如上圖所示,一個典型的Kafka集羣中包含若干Producer(能夠是web前端產生的Page View,或者是服務器日誌,系統CPU、Memory等),若干broker(Kafka支持水平擴展,通常broker數量越多,集羣吞吐率越高),若干Consumer Group,以及一個Zookeeper集羣。

Kafka經過Zookeeper管理集羣配置,選舉leader,以及在Consumer Group發生變化時進行rebalance。

Producer使用push模式將消息發佈到broker,Consumer使用pull模式從broker訂閱並消費消息。

至於rabbitMQ,也有普通集羣和鏡像集羣模式,自行去了解,比較簡單,兩小時即懂。

要求,在回答高可用的問題時,應該能邏輯清晰的畫出本身的MQ集羣架構或清晰的敘述出來。

五、如何保證消息不被重複消費?

分析:這個問題其實換一種問法就是,如何保證消息隊列的冪等性?

這個問題能夠認爲是消息隊列領域的基本問題。換句話來講,是在考察你的設計能力,這個問題的回答能夠根據具體的業務場景來答,沒有固定的答案。

回答:先來講一下爲何會形成重複消費?

其實不管是那種消息隊列,形成重複消費緣由其實都是相似的。

正常狀況下,消費者在消費消息時候,消費完畢後,會發送一個確認信息給消息隊列,消息隊列就知道該消息被消費了,就會將該消息從消息隊列中刪除。只是不一樣的消息隊列發送的確認信息形式不一樣

例如RabbitMQ是發送一個ACK確認消息,RocketMQ是返回一個CONSUME_SUCCESS成功標誌,kafka實際上有個offset的概念

簡單說一下(若是還不懂,出門找一個kafka入門到精通教程),就是每個消息都有一個offset,kafka消費過消息後,須要提交offset,讓消息隊列知道本身已經消費過了。

那形成重複消費的緣由?

就是由於網絡傳輸等等故障,確認信息沒有傳送到消息隊列,致使消息隊列不知道本身已經消費過該消息了,再次將該消息分發給其餘的消費者。

如何解決?這個問題針對業務場景來答分如下幾點

(1)好比,你拿到這個消息作數據庫的insert操做。

那就容易了,給這個消息作一個惟一主鍵,那麼就算出現重複消費的狀況,就會致使主鍵衝突,避免數據庫出現髒數據。

(2)再好比,你拿到這個消息作redis的set的操做

那就容易了,不用解決。由於你不管set幾回結果都是同樣的,set操做原本就算冪等操做。

(3)若是上面兩種狀況還不行,上大招。

準備一個第三方介質,來作消費記錄。以redis爲例,給消息分配一個全局id,只要消費過該消息,將以K-V形式寫入redis。那消費者開始消費前,先去redis中查詢有沒消費記錄便可。

六、如何保證消費的可靠性傳輸?

分析:咱們在使用消息隊列的過程當中,應該作到消息不能多消費,也不能少消費。若是沒法作到可靠性傳輸,可能給公司帶來千萬級別的財產損失。

一樣的,若是可靠性傳輸在使用過程當中,沒有考慮到,這不是給公司挖坑麼,你能夠拍拍屁股走了,公司損失的錢,誰承擔。

仍是那句話,認真對待每個項目,不要給公司挖坑

回答:其實這個可靠性傳輸,每種MQ都要從三個角度來分析:生產者弄丟數據、消息隊列弄丟數據、消費者弄丟數據

RabbitMQ

(1)生產者丟數據

從生產者弄丟數據這個角度來看,RabbitMQ提供transaction和confirm模式來確保生產者不丟消息。

transaction機制就是說,發送消息前,開啓事物(channel.txSelect()),而後發送消息,若是發送過程當中出現什麼異常,事物就會回滾(channel.txRollback()),若是發送成功則提交事物(channel.txCommit())。

然而缺點就是吞吐量降低了。所以,按照博主的經驗,生產上用confirm模式的居多。

一旦channel進入confirm模式,全部在該信道上面發佈的消息都將會被指派一個惟一的ID(從1開始)

一旦消息被投遞到全部匹配的隊列以後,rabbitMQ就會發送一個Ack給生產者(包含消息的惟一ID)

這就使得生產者知道消息已經正確到達目的隊列了.若是rabiitMQ沒能處理該消息,則會發送一個Nack消息給你,你能夠進行重試操做。

處理Ack和Nack的代碼以下所示(說好不上代碼的,偷偷上了):

channel.addConfirmListener(new ConfirmListener() {
    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("nack: deliveryTag = "+deliveryTag+" multiple: "+multiple);
    }
    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("ack: deliveryTag = "+deliveryTag+" multiple: "+multiple);
    }
});

(2)消息隊列丟數據

處理消息隊列丟數據的狀況,通常是開啓持久化磁盤的配置。

這個持久化配置能夠和confirm機制配合使用,你能夠在消息持久化磁盤後,再給生產者發送一個Ack信號。

這樣,若是消息持久化磁盤以前,rabbitMQ陣亡了,那麼生產者收不到Ack信號,生產者會自動重發。

那麼如何持久化呢,這裏順便說一下吧,其實也很容易,就下面兩步

一、將queue的持久化標識durable設置爲true,則表明是一個持久的隊列

二、發送消息的時候將deliveryMode=2

這樣設置之後,rabbitMQ就算掛了,重啓後也能恢復數據

(3)消費者丟數據

消費者丟數據通常是由於採用了自動確認消息模式。

這種模式下,消費者會自動確認收到信息。這時rahbitMQ會當即將消息刪除,這種狀況下若是消費者出現異常而沒能處理該消息,就會丟失該消息。

至於解決方案,採用手動確認消息便可。

kafka

Producer在發佈消息到某個Partition時,先經過ZooKeeper找到該Partition的Leader

而後不管該Topic的Replication Factor爲多少(也即該Partition有多少個Replica),Producer只將該消息發送到該Partition的Leader。

Leader會將該消息寫入其本地Log。每一個Follower都從Leader中pull數據。
針對上述狀況,得出以下分析

(1)生產者丟數據

在kafka生產中,基本都有一個leader和多個follwer。follwer會去同步leader的信息。

所以,爲了不生產者丟數據,作以下兩點配置

  1. 第一個配置要在producer端設置acks=all。這個配置保證了,follwer同步完成後,才認爲消息發送成功。

  2. 在producer端設置retries=MAX,一旦寫入失敗,這無限重試

(2)消息隊列丟數據

針對消息隊列丟數據的狀況,無外乎就是,數據還沒同步,leader就掛了,這時zookpeer會將其餘的follwer切換爲leader,那數據就丟失了。

針對這種狀況,應該作兩個配置。

  1. replication.factor參數,這個值必須大於1,即要求每一個partition必須有至少2個副本

  2. min.insync.replicas參數,這個值必須大於1,這個是要求一個leader至少感知到有至少一個follower還跟本身保持聯繫

這兩個配置加上上面生產者的配置聯合起來用,基本可確保kafka不丟數據

(3)消費者丟數據

這種狀況通常是自動提交了offset,而後你處理程序過程當中掛了。kafka覺得你處理好了。

再強調一次offset是幹嗎的

offset:指的是kafka的topic中的每一個消費組消費的下標。

簡單的來講就是一條消息對應一個offset下標,每次消費數據的時候若是提交offset,那麼下次消費就會從提交的offset加一那裏開始消費。

好比一個topic中有100條數據,我消費了50條而且提交了,那麼此時的kafka服務端記錄提交的offset就是49(offset從0開始),那麼下次消費的時候offset就從50開始消費。

解決方案也很簡單,改爲手動提交便可。

ActiveMQ和RocketMQ

你們自行查閱吧

七、如何保證消息的順序性?

分析:其實並不是全部的公司都有這種業務需求,可是仍是對這個問題要有所複習。

回答:針對這個問題,經過某種算法,將須要保持前後順序的消息放到同一個消息隊列中(kafka中就是partition,rabbitMq中就是queue)。而後只用一個消費者去消費該隊列。

有的人會問:那若是爲了吞吐量,有多個消費者去消費怎麼辦?

這個問題,沒有固定回答的套路。好比咱們有一個微博的操做,發微博、寫評論、刪除微博,這三個異步操做。若是是這樣一個業務場景,那隻要重試就行。

好比你一個消費者先執行了寫評論的操做,可是這時候,微博都還沒發,寫評論必定是失敗的,等一段時間。等另外一個消費者,先執行寫評論的操做後,再執行,就能夠成功。

總之,針對這個問題,個人觀點是保證入隊有序就行,出隊之後的順序交給消費者本身去保證,沒有固定套路。

驚喜

最後還準備了一套上面資料對應的面試題(有答案哦)和麪試時的高頻面試算法題(若是面試準備時間不夠,那麼集中把這些算法題作完便可,命中率高達85%+)

image.png

image.png

資料獲取方式:戳這裏免費領取

相關文章
相關標籤/搜索