1. 程式人生 > >JAVA面試——訊息佇列

JAVA面試——訊息佇列

1、訊息佇列的使用場景。
 ☞ 以下介紹訊息佇列在實際應用常用的使用場景。非同步處理、應用解耦、流量削鋒和訊息通訊四個場景。
 1)、非同步處理:場景說明:使用者註冊後,需要發註冊郵件和註冊簡訊。
     
      引入訊息佇列後架構如下:使用者的響應時間=註冊資訊寫入資料庫的時間,例如50毫秒。發註冊郵箱、發註冊簡訊寫入訊息佇列後,直接返回客戶端,因寫入訊息佇列的速度很快,基本可以忽略,因此使用者的響應時間可能是50毫秒。按照傳統的做法①、序列方式,將註冊資訊寫入資料庫成功後,發註冊郵件,再發送註冊簡訊,以上三個成功後,返回客戶端。可能需要150毫秒,這樣使用訊息佇列提高了3倍。②、並行方式,將註冊資訊寫入資料庫成功後,傳送註冊郵件,同時傳送註冊簡訊。也可能需要100毫秒,這樣使用訊息佇列提高了2倍。
  2)、應用解耦:

場景說明:使用者下單後,訂單系統需要通知庫存系統。如下圖:
         
 傳統模式的缺點:1)、庫存系統無法訪問時,則訂單減庫存業務將會失敗,從而導致訂單失敗。
                              2)、訂單系統與庫存系統耦合。
 引入訊息佇列:1)、使用者下單後,訂單系統完成持久化處理,將訊息寫入訊息佇列,返回使用者訂單下單成功。
                          2)、庫存系統:訂閱下單的訊息,採用拉/推的方式,獲取下單資訊,庫存系統根據下單資訊,進行庫存操作。
  ☛ 當庫存系統不能正常使用時,也不會影響正常下單,因為下單後,訂單系統寫入訊息佇列就不再關心其他的後續操作了。實現訂單系統與庫存系統的解耦

      
  3)、流量削鋒:場景說明:秒殺或團搶活動中使用廣泛。秒殺活動,一般會因為流量過大,導致流量暴增,應用掛掉。一般需要在應用前端加入訊息佇列。
      
       使用者請求:伺服器接受後,首先寫入訊息佇列。當訊息佇列長度超出最大數量,則直接拋棄使用者請求或跳轉至錯誤頁面。
       秒殺業務處理:根據訊息佇列中的請求資訊,再做後續處理。
  ▁▂▃ 這樣可以有效的控制活動人數和有效緩解短時間內的高流量衝擊,防止壓垮應用系統。
  4)、日誌處理:指將訊息佇列用在日誌處理中,比如Kafka的應用,解決大量日誌傳輸的問題。
     
   ▷ 日誌採集客戶端:
負責日誌資料採集,定時寫受寫入Kafka佇列。
   ▷ kafka訊息佇列:負責日誌資料的接收,儲存和轉發。
   ▷ 日誌處理應用:訂閱並消費kafka佇列中的日誌資料。
  5)、訊息通訊:訊息佇列一般都內建了高效的通訊機制,因此也可以用純訊息通訊。比如實現點對點訊息佇列,或者聊天室。
     【1】、點對點通訊:客戶端A和客戶端B使用同一佇列,進行訊息通訊。
     
     【2】、聊天室通訊(釋出訂閱模式):客戶端A,客戶端B,客戶端N訂閱同一主題,進行訊息釋出和接收。實現類似聊天室效果。
     

2、訊息的重發,補發策略。
    【訊息重發】:1)、如果訊息接受者在處理訊息過程中沒有對MOM(訊息中間鍵)進行應答,則訊息將有MOM重發。
           2)、如果佇列中設定了預讀引數(consumer.perfetchSize),如果訊息接受者在處理第一條訊息時(沒有向MOM進行確認)就宕機了,則預讀數量的所有訊息將被重發。
    3)、如果Session是事務的,則只要訊息接受者有一條訊息沒有確認,或訊息傳送期間MOM或客戶端某一方突然宕機了,則該事務範圍中的所有訊息MOM都將重發。
▷ ActiveMQ訊息伺服器怎麼知道客戶端到底是訊息正在處理中還是以處理完成沒應答MOM或者宕機等等情況?其實是所有的客戶端機器,都執行著一套客戶端的ActiveMQ環境,該環境快取發來的訊息,維持著和ActiveMQ伺服器的訊息通訊,負責失效轉移(fail-over)等,所有的判斷和處理都是由這套客戶端環境來完成的。
    【補發策略】:前提:Broker根據自己的規則,通過BrokerInfo命令包和客戶端建立連線,向客戶端傳送預設傳送策略。但是客戶端可以使用ActiveMQConnect.getRedeliveryPolicy()方法覆蓋override這個策略設定。

    RedeliveryPolicy policy = connection.getRedeliveryPolicy();  
    policy.setInitialRedeliveryDelay(500);  
    policy.setBackOffMultiplier(2);  
    policy.setUseExponentialBackOff(true);  
    policy.setMaximumRedeliveries(2);  

   ★ 一旦訊息重發嘗試超過重發策略中配置的maximumRedeliveries(預設=6)會給Broler傳送一個“Poison ack”通知它,這個訊息被認為是a poison pill,接著broker會將這個訊息傳送給DLQ(Dead Letter Queue),以便後續處理。
   策略:(1)、 預設死信佇列(Dead Letter Queue)叫做Active.DLQ;所有的未送達訊息將傳送到這個佇列,導致非常難於管理。此時就可以通過設定activemq.xml檔案中的destination policy map的“individualDeadLetterStrategy”屬性來修改。

    <broker...>  
      <destinationPolicy>  
        <policyMap>  
          <policyEntries>  
            <!-- 使用“>”萬用字元在所有佇列上設定以下策略-->  
            <policyEntry queue=">">  
              <deadLetterStrategy>  
                <!--  
                  為目標名稱使用字首“DLQ.”,並使DLQ是一個佇列而不是一個主題 
                -->  
                <individualDeadLetterStrategy  
                  queuePrefix="DLQ." useQueueForQueueMessages="true" />  
              </deadLetterStrategy>  
            </policyEntry>  
          </policyEntries>  
        </policyMap>  
      </destinationPolicy>  
      ...  
    </broker>  

   (2)、自動丟棄過期訊息(Expired Messages):一些應用可能只是簡單的丟棄過期訊息,而不是將它們放到DLQ。在dead  letter strategy死信策略上配置processExpired屬性為false,可以實現這個功能。

    <broker...>  
      <destinationPolicy>  
       <policyMap>  
         <policyEntries>  
           <!-- 使用“>”萬用字元在所有佇列上設定以下策略 -->  
           <policyEntry queue=">">  
             <!--  
               告訴死信策略不要處理過期訊息所以他們將被丟棄而不是被送到DLQ
             -->  
             <deadLetterStrategy>  
               <sharedDeadLetterStrategy processExpired="false" />  
             </deadLetterStrategy>  
           </policyEntry>  
         </policyEntries>  
       </policyMap>  
      </destinationPolicy>  
    ...  
    </broker>  

    (3)、將非持久資訊(non-persistent messages)放入死信佇列ActiveMQ預設不會將未傳送到的非持久資訊放入死信佇列。如果一個應用程式並不想將訊息message設定為持久的,那麼記錄下來的那些未傳送到的訊息對它來說往往也就沒有價值。不過如果想實現這個功能,可以在dead-letter-strategy死信策略上設定processNonPersistent="true"。

    <broker...>  
      <destinationPolicy>  
       <policyMap>  
         <policyEntries>  
           <!-- 使用“>”萬用字元在所有佇列上設定以下策略 -->  
           <policyEntry queue=">">  
             <!--  
               告訴死信策略不要處理過期訊息所以他們將被丟棄而不是被送到DLQ 
             -->  
             <deadLetterStrategy>  
               <sharedDeadLetterStrategy processNonPersistent="true" />  
             </deadLetterStrategy>  
           </policyEntry>  
         </policyEntries>  
       </policyMap>  
      </destinationPolicy>  
    ...  
    </broker>  

3、如何保證訊息的有序性。
    ♣ 在Active中有兩種方式保證訊息消費的順序性。
    
1)、通過高階特性consumer獨有的消費者(exclusive consumer)。如果一個queue設定為exclusive,broker會挑選一個consumer,並且將所有的訊息都發給這個consumer。如果這個consumer掛了,broker會自動挑選另外一個consumer。

queue = new ActiveMQQueue("TEST.QUEUE?consumer.exclusive=true"); 
consumer = session.createConsumer(queue);

    2)、 利用Activemq的高階特性:messageGroups。Message Groups特性是一種負載均衡的機制。在一個訊息被分發到consumer之前,broker首先檢查訊息JMSXGroupID屬性。如果存在,那麼broker會檢查是否有某個consumer擁有這個message group。如果沒有,那麼broker會選擇一個consumer,並將它關聯到這個message group。此後,這個consumer會接收這個message group的所有訊息,直到:
     ①、Consumer被關閉。
     ②、Message group被關閉,通過傳送一個訊息,並設定這個訊息的JMSXGroupSeq為-1。
 ♠ 消費者實際上根據兩個維度排序了,一個是消費者的Priority,即消費者的優先順序。還有一個是消費者的指定的訊息組的個數AssignedGroupCount。這個順序直接影響到下一條訊息是誰來接收。

4、用過哪些MQ,和其他mq比較有什麼優缺點,MQ的連線是執行緒安全的嗎,你們公司的MQ服務架構怎樣的。
 ◥ Kafka是LinkedIn開發的一個高效能、分散式的訊息系統,廣泛用於日誌收集、流式資料處理、線上和離線訊息分發等場景。雖然不是作為傳統的MQ來設計,但在大部分情況下,Kafka也可以代替原有ActiveMQ等傳統的訊息系統。
 ◥ Kafka將訊息流按Topic組織,儲存訊息的伺服器稱為Broker,消費者可以訂閱一個或者多個Topic。為了均衡負載,一個Topic的訊息又可以劃分到多個分割槽(Partition),分割槽越多,Kafka並行能力和吞吐量越高。
 ◥ Kafka 叢集需要zookeeper 支援來實現叢集,kafka 發行包中已經包含了zookeeper,部署的時候可以在一臺伺服器上同時啟動一個zookeeper Server 和 一個Kafka Server,也可以使用已有的其他zookeeper叢集。
 ◥ 和傳統的MQ不同,消費者需要自己保留一個offset,從kafka 獲取訊息時,只拉取當前offset 以後的訊息。Kafka 的scala/java 版的client 已經實現了這部分的邏輯,將offset 儲存到zookeeper 上。每個消費者可以選擇一個id,同樣id 的消費者對於同一條訊息只會收到一次。一個Topic 的消費者如果都使用相同的id,就是傳統的 Queue;如果每個消費者都使用不同的id, 就是傳統的pub-sub。
如果在MQ的場景下,將Kafka 和 ActiveMQ 相比,Kafka的優點:
   1)、分散式可高可擴充套件:Kafka叢集可以透明的擴充套件,增加新的伺服器進叢集。
   2)、高效能:Kafka的效能大大超過傳統的ActiveMQ、RabbitMQ等MQ實現,尤其是Kafka還支援batch操作。
   3)、容錯:Kafka每個Partition的資料都會複製到幾臺伺服器上。當某個Broker故障失效時,ZooKeeper服務將通知生產者和消費者,生產者和消費者轉而使用其它Broker。
   4)、高吞吐,在一臺普通的伺服器上既可以達到10W/s的吞吐速率。
   5)、完全的分散式系統,Broker、Producer、Consumer都原生自動支援分散式,自動實現負載均衡。
   6)、快速持久化,可以在O(1)的系統開銷下進行訊息持久化。
 Kafka的缺點:
   1)、重複訊息:Kafka 只保證每個訊息至少會送達一次,雖然機率很小,但一條訊息有可能會被送達多次。
   2)、訊息亂序:雖然一個Partition 內部的訊息是保證有序的,但是如果一個Topic 有多個Partition,Partition 之間的訊息送達不保證有序。
   3)、複雜性:Kafka需要zookeeper 叢集的支援,Topic通常需要人工來建立,部署和維護較一般訊息佇列成本更高。
MQ是非執行緒安全的
  ☛ Kafka架構:1)、Producers(生產者):生產者是傳送一個或多個主題Topic的釋出者。生產者向Kafka代理髮送資料。每當生產者將訊息釋出給代理時,代理只需要將訊息附加到最後一個段檔案。實際上,該訊息將被附加到分割槽。生產者也可以向指定的分割槽傳送訊息。
     

   2)、Brokers:代理(經紀人)負責維護髮布資料的簡單系統。
   3)、Topic(主題):屬於特定類別的資訊流成為主題。陣列儲存在主題中。Topic相當於Queue。主題被拆分成分割槽。分割槽被實現為具有相等大小的一組分段檔案。
   4)、Partition(分割槽):每個Partition內部訊息有序,其中每個訊息都有一個offset序號。一個Partition值對應一個Broker,一個Broker可以管理多個Partition。

   5)、Partition offset(分割槽偏移):每個分割槽訊息具有成為offset的唯一序列標識。
   6)、Replicas of partition(分割槽備份):副本只是一個分割槽備份:不讀取和寫入資料,主要用於防止資料丟失。
           
    7)、Kafka Cluster(Kafka叢集):Kafka有多個代理被稱為Kafka叢集。可以擴充套件Kafka叢集,無需停機。這些叢集用於管理訊息資料的永續性和複製。
    8)、Consumers(消費者):Consumers從經紀人處讀取資料。 消費者訂閱一個或多個主題,並通過從代理中提取資料來使用已釋出的訊息。Consumer自己維護消費到哪個offet。
 ▁▂▃ 每個Consumer都有對應的group:①、group內是queue消費模型:各個Consumer消費不同的partition,因此一個訊息在group內只消費一次。
②、group間是publish-subscribe消費模型:各個group各自獨立消費,互不影響,因此一個訊息被每個group消費一次。

5、MQ系統的資料如何保證不丟失。
 ◢  資料丟失的原因1)、使用同步模式的時候,有3種狀態保證訊息被安全生產,當配置=1時(只保證寫入Leader成功)的話,如果剛好Leader partition掛了,資料就會丟失。
     2)、使用非同步模式的時候,當緩衝區滿了,如果配置=0(還沒有收到確認的資料資料就立即被丟棄掉)。
 ◢  解決辦法只要能避免以上兩種情況就可以保證訊息不會被丟失。
     1)、當同步模式時,確認機制設定為-1,就是讓訊息寫入Leader和所有副本。
     2)、當非同步模式是,當訊息發出,當還麼收到確認的時候,緩衝區也滿了。在配置檔案中設定成不限制阻塞超時的時間,也就是說讓生產者一直阻塞,這樣就能保證資料不會丟失。在資料消費時,避免資料丟失的方法:如果使用storm,需要開啟storm的ackfail機制;如果沒有使用storm,確認資料被完成處理之後,再更新offset值。
消費端弄丟了資料情況(擴充)當你消費到了這個訊息,然後消費者那邊自動提交了offset,讓kafka以為你已經消費好了這個訊息,其實你剛準備處理這個訊息,你還沒處理,你自己就掛了,此時這條訊息就丟了。
     Kafka會自動提交offset,如果將自動提交offset改為手動提交,就可以保證資料不會丟。但是此時確實還是會重複消費,比如你剛處理完,還沒有提交offset,結果自己掛了,此時肯定會重複消費一次,自己保證冪等性就好了。

6、Rabbitmq如何實現叢集高可用。
   
映象模式:佇列的資料都映象了一份到所有的節點上。這樣任何一個節點失效,不會影響到整個叢集的使用。在實現上mirror queue內部有一套選舉演算法,會選出一個master和若干的slaver。master和slaver通過相互之間不斷的傳送心跳來檢查是否連線斷開。可以通過指定net_ticktime來控制心跳檢查頻率。注意一個單位時間net_ticktime實際上做了4次互動,故當超過net_ticktime (± 25%) 秒沒有響應的話則認為節點掛掉。另外注意修改net_ticktime時需要所有節點都一致。配置舉例:

        {rabbit, [{tcp_listeners, [5672]}]}, 
        {kernel, [{net_ticktime,  120}]} 

  ♠  consumer,任意連線一個節點,若連上的不是master,請求會轉發給master,為了保證訊息的可靠性,consumer回覆ack給master後,master刪除訊息並廣播所有的slaver去刪除。 
  ♠  publisher:任意連線一個節點,若連上的不是master,則轉發給master,由master儲存並轉發給其他的slaver儲存。
  ♠  如果slaver掛掉,則叢集的節點狀態沒有任何變化。只要client沒有連到這個節點上,也不會給client傳送失敗的通知。在檢測到slaver掛掉的期間publish訊息會有延遲。如果配置了高可用策略是自動同步,當slaver起來後,佇列中有大量的訊息需要同步,將會整個叢集阻塞長時間的不能讀寫直到同步結束。 
  ♠  這兩個掛掉的情況都需要客戶端映象容錯,比如在連線斷開的時候進行重連(官方的Java和.net 客戶端提供了callback方法在監聽到連結失敗的時候呼叫。Java在Connection和channel類中提供了ShutdownListener 的callback方法,.net client在IConnecton中提供了ConnectionShuedown在Imodel中提供了ImodelShutdown事件供呼叫) 。也可以在client和server之間加入LoadBalancer.比如haproxy做負載均衡。 
  ♠  RabbitMQ實現了一種映象佇列(mirrored queue)的演算法提供HA:建立佇列時可以通過傳入“x-ha-policy”引數設定佇列為映象佇列,映象佇列會儲存在多個Rabbit MQ節點上,並配置成一主多從的結構,可以通過“x-ha-policy-params”引數來具體指定master節點和slave節點的列表。所有傳送到映象佇列上的操作,比如訊息的傳送和刪除,都會先在master節點上執行,再通過一種叫GM(Guaranteed Multicast)的原子廣播(atomic broadcast)演算法同步到各slave節點。GM演算法通過兩階段的提交,可以保證master節點發送到所有slave節點上的訊息要麼全部執行成功,要麼全部失敗;通過環形的訊息傳送順序,即master節點發送訊息給一個slave節點,這個slave節點依次傳送給下一個slave節點,最終訊息回到master節點,保證了主從節點上的負載差別不大。通過傳入“x-ha-policy”引數設定佇列為映象佇列(mirrored queue):定義一個policy:以“ha.”開頭的佇列都被映象到叢集中的所有節點上:rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}'。定義一個policy:以“cinder”開頭的佇列被映象到叢集中的任意兩個節點上,並且自動同步:rabbitmqctl set_policy ha-cinder-two "^cinder"或者設定'{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
 
▶ all:佇列將mirrored到所有叢集中的節點中,當新節點新增進來時也會mirrored到新的節點
▶ exactly(需指定count):如果節點數小於count數,則佇列將mirrored到所有的節點。如果節點數大於count,新的節點將不再建立佇列的mirror(即使原來已建立mirror的節點掛掉也不會建立)
▶ nodes:對指定的節點進行mirror。如果沒有一個指定的節點在執行中,那麼只有client連線的那個節點才會宣告queue(這裡有個遷移策略:假如queue是在[A,B]上且A為master,若給定的新的策略為nodes[C,D],那麼為了防止資料丟失,在遷移中會同時存在[A,C,D]直到C,D已經同步好以後,A才會關閉)

7、kafka吞吐量高的原因。
   
1)、順序讀寫磁碟,充分利用了作業系統的預讀機制。
    2)、Linux中使用sendfile命令,減少一次資料拷貝:
        ①、把資料從硬碟讀取到核心中的頁快取。
        ②、把資料從核心中讀取到使用者空間(sendfile命令跳過此步驟)。
       
③、把使用者空間的資料寫到socket快取區中。
        ④、作業系統將資料從socket緩衝區中複製到網絡卡緩衝區,以便將資料經網路發出。
    3)、生產者客戶端快取訊息批量傳送,消費者批量從broker獲取訊息,減少IO次數,充分利用磁碟順序讀寫的效能。
    4)、通常情況下kafka的瓶頸不是CPU或者磁碟,而是網路寬頻,所以生產者可以對資料進行壓縮。

8、kafka 和其他訊息佇列的區別,kafka 主從同步怎麼實現。
與RabbitMQ的區別:ღ RabbitMQ:用在實時的對可靠性要求比較高的訊息傳遞上。kafka:用於處理活躍的流式資料,大資料量的資料處理上。
【1】、在架構模型方面:RabbitMQ遵循AMQP協議,RabbitMQ的broker由Exchange,Binding,queue組成,其中exchange和binding組成了訊息的路由鍵;客戶端Producer通過連線channel和server進行通訊,Consumer從queue獲取訊息進行消費(長連線,queue有訊息會推送到consumer端,consumer迴圈從輸入流讀取資料)。rabbitMQ以broker為中心;有訊息的確認機制。
  ♐ kafka遵從一般的MQ結構,producer,broker,consumer,以consumer為中心,訊息的消費資訊儲存的客戶端consumer上,consumer根據消費的點,從broker上批量pull資料;無訊息確認機制。
【2】、在吞吐量方面:rabbitMQ在吞吐量方面稍遜於kafka,他們的出發點不一樣,rabbitMQ支援對訊息的可靠的傳遞,支援事務,不支援批量的操作;基於儲存的可靠性的要求儲存可以採用記憶體或者硬碟。
  ♐ kafka具有高的吞吐量,內部採用訊息的批量處理,zero-copy機制,資料的儲存和獲取是本地磁碟順序批量操作,具有O(1)的複雜度,訊息處理的效率很高。
 【3】、在可用性方面:rabbitMQ支援mirror的queue,主queue失效,mirror queue接管。
  ♐ kafka的broker支援主備模式。
 【4】、在叢集負載均衡方面:rabbitMQ的負載均衡需要單獨的loadbalancer進行支援。
  ♐ kafka採用zookeeper對叢集中的broker、consumer進行管理,可以註冊topic到zookeeper上;通過zookeeper的協調機制,producer儲存對應topic的broker資訊,可以隨機或者輪詢傳送到broker上;並且producer可以基於語義指定分片,訊息傳送到broker的某分片上。
與ActiveMQ的區別:ActiveMQ和Kafka,前者完全實現了JMS的規範,後者並沒有糾結於JMS規範,設計了另一套吞吐非常高的分散式釋出-訂閱訊息系統,目前非常流行。接下來我們結合三個點(訊息安全性,伺服器的穩定性容錯性以及吞吐量)來分別談談這兩個訊息中介軟體。
   Kafka是LinkedIn開源的分散式釋出-訂閱訊息系統,目前歸屬於Apache定級專案。Kafka並沒有遵守JMS規範,他只用檔案系統來管理訊息的生命週期。Kafka的設計目標是:
(1)以時間複雜度為O(1)的方式提供訊息持久化能力,即使對TB級以上資料也能保證常數時間複雜度的訪問效能。
(2)高吞吐率。即使在非常廉價的商用機器上也能做到單機支援每秒100K條以上訊息的傳輸。
(3)支援Kafka Server間的訊息分割槽,及分散式消費,同時保證每個Partition內的訊息順序傳輸。
(4)同時支援離線資料處理和實時資料處理。
(5)Scale out:支援線上水平擴充套件。
  所以,不像AMQ,Kafka從設計開始極為高可用為目的,天然HA。broker支援叢集,訊息亦支援負載均衡,還有副本機制。同樣,Kafka也是使用Zookeeper管理叢集節點資訊,包括consumer的消費資訊也是儲存在zk中,下面我們分話題來談:
 【1】、訊息的安全性:Kafka叢集中的Leader負責某一topic的某一partition的訊息的讀寫,理論上consumer和producer只與該Leader 節點打交道,一個叢集裡的某一broker即是Leader的同時也可以擔當某一partition的follower,即Replica。Kafka分配Replica的演算法如下:
(1)將所有Broker(假設共n個Broker)和待分配的Partition排序。
(2)將第i個Partition分配到第(i mod n)個Broker上。
(3)將第i個Partition的第j個Replica分配到第((i + j) mode n)個Broker上。
    同時,Kafka與Replica既非同步也不是嚴格意義上的非同步。一個典型的Kafka傳送-消費訊息的過程如下:首先首先Producer訊息傳送給某Topic的某Partition的Leader,Leader先是將訊息寫入本地Log,同時follower(如果落後過多將會被踢出出 Replica列表)從Leader上pull訊息,並且在未寫入log的同時即向Leader傳送ACK的反饋,所以對於某一條已經算作commit的訊息來講,在某一時刻,其存在於Leader的log中,以及Replica的記憶體中。這可以算作一個危險的情況(聽起來嚇人),因為如果此時叢集掛了這條訊息就算丟失了,但結合producer的屬性(request.required.acks=2,當所有follower都收到訊息後返回ack)可以保證在絕大多數情況下訊息的安全性。當訊息算作commit的時候才會暴露給consumer,並保證at-least-once的投遞原則。
 【2】、服務的穩定容錯性:前面提到過,Kafka天然支援HA,整個leader/follower機制通過zookeeper排程,它在所有broker中選出一個 controller,所有Partition的Leader選舉都由controller決定,同時controller也負責增刪Topic以及 Replica的重新分配。如果Leader掛了,叢集將在ISR(in-sync replicas)中選出新的Leader,選舉基本原則是:新的Leader必須擁有原來的Leader commit過的所有訊息。假如所有的follower都掛了,Kafka會選擇第一個“活”過來的Replica(不一定是ISR中的)作為Leader,因為如果此時等待ISR中的Replica是有風險的,假如所有的ISR都無法“活”,那此partition將會變成不可用。
 【3】、吞吐量:Leader節點負責某一topic(可以分成多個partition)的某一partition的訊息的讀寫,任何釋出到此partition的訊息都會被直接追加到log檔案的尾部,因為每條訊息都被append到該partition中,是順序寫磁碟,因此效率非常高(經驗證,順序寫磁碟效率比隨機寫記憶體還要高,這是Kafka高吞吐率的一個很重要的保證),同時通過合理的partition,訊息可以均勻的分佈在不同的partition裡面。 Kafka基於時間或者partition的大小來刪除訊息,同時broker是無狀態的,consumer的消費狀態(offset)是由
 consumer自己控制的(每一個consumer例項只會消費某一個或多個特定partition的資料,而某個partition的資料只會被某一個特定的consumer例項所消費),也不需要broker通過鎖機制去控制訊息的消費,所以吞吐量驚人,這也是Kafka吸引人的地方。最後說下由於zookeeper引起的腦裂(Split Brain)問題:每個consumer分別單獨通過Zookeeper判斷哪些partition down了,那麼不同consumer從Zookeeper“看”到的view就可能不一樣,這就會造成錯誤的reblance嘗試。而且有可能所有的 consumer都認為rebalance已經完成了,但實際上可能並非如此。
和傳統的MQ不同:消費者需要自己保留一個offset,從kafka 獲取訊息時,只拉去當前offset 以後的訊息。Kafka 的Scala/Java 版的client。已經實現了這部分的邏輯,將offset 儲存到zookeeper 上。每個消費者可以選擇一個id,同樣id 的消費者對於同一條訊息只會收到一次。一個Topic 的消費者如果都使用相同的id,就是傳統的 Queue;如果每個消費者都使用不同的id, 就是傳統的pub-sub。
kafka 主從同步怎麼實現:kafka的主從同步,主要是針對它的broker來說。在kafka的broker中,同一個topic可以被分配成多個Partition,每個Partition的可以有一個或者多個replicas(備份),即會有一個leader以及0到多個Follower,在consumer讀取資料的時候,只會從Leader上讀取資料,Follower只是在Leader宕機的時候來替代Leader,主從同步有兩種方式:同步複製和非同步複製,Kafka採用的是中間策略ISR(In Sync Replicas)。
   Kafka的ISR策略:在有資料寫上Leader的時候,Leader會檢視Follower組成的ISR列表,並且符合以下兩點才算是屬於ISR列表:1、broker可以維護和zookeeper的連線,zookeeper通過心跳機制檢查每個節點的連線。2、如果節點是個follow它必須能及時同步Leader的寫操作,不能延時太久。當有寫訊息的時候,我們可以根據配置做如下配置:request.required.acks引數的設定來進行調整:
  ☞ 0 ,相當於非同步傳送,訊息傳送完畢即offset增加,繼續生產;相當於At most once。
  ☞ 1,leader收到leader replica 對一個訊息的接受ack才增加offset,然後繼續生產。
  ☞ -1,leader收到所有replica 對一個訊息的接受ack才增加offset,然後繼續生產。

9、利用mq怎麼實現最終一致性。
   
RabbitMQ遵循了AMQP規範,用訊息確認機制來保證:只要訊息傳送,就能確保被消費者消費來做到了訊息最終一致性。
    Rabbitmq的整個傳送過程如下:
    (1)生產者傳送訊息到訊息服務。
    (2)如果訊息落地持久化完成,則返回一個標誌給生產者。生產者拿到這個確認後,才能放心的說訊息終於成功發到訊息服務了。否則進入異常處理流程。
    (3)訊息服務將訊息傳送給消費者。
    (4)消費者接受並處理訊息,如果處理成功則手動確認。當訊息服務拿到這個確認後,才放心的說終於消費完成了。否則重發,或者進入異常處理。

11、MQ有可能發生重複消費,如何避免,如何做到冪等。
    
描述:使用者在頁面停止查詢時,會導致消費者程序被殺死,因此ACK狀態碼未反饋至MQ,從而訊息一直存留在MQ中,當新的消費者啟動時會重新消費;
     解決方案:消費者每次執行查詢前,首先在DB上查詢任務的執行狀態,若處於「取消/失敗/成功」則表示已經由其它消費者消費過,那麼直接返回ACK狀態碼給MQ,將訊息從MQ中移除;
     冪等:https://www.cnblogs.com/zhangxianming/p/8724590.html

12、MQ的訊息延遲了怎麼處理,訊息可以設定過期時間麼,過期了你們一般怎麼處理。
 
延遲處理:可以通過設定延遲級別,控制訊息延遲的時間。
設定過期時間:

<broker>     ...     
    <plugins>         <!-- 86,400,000ms = 1 day -->           
          <timeStampingBrokerPluginttlCeiling="30000" zeroExpirationOverride="30000" />      
    </plugins>     ... 
</broker>

   1)、message過期則客戶端不能接收
   2)、ttlCeiling:表示過期時間上限(程式寫的過期時間不能超過此時間,超過則以此時間為準)
   3)、zeroExpirationOverride:表示過期時間(給未分配過期時間的訊息分配過期時間)
過期訊息處理辦法:訊息過期後會進入死信佇列,如不想拋棄死信佇列,預設進入ACTIVEMQ.DLQ佇列,且不會自動清除;對於過期的訊息進入死信佇列還有一些可選的策略:放入各自的死信通道、儲存在一個共享的佇列(預設),且可以設定是否將過期訊息放入佇列的開關以及死信佇列訊息過期時間。
    1)、直接拋棄死信佇列:AcitveMQ提供了一個便捷的外掛:DiscardingDLQBrokerPlugin,來拋棄DeadLetter。如果開發者不需要關心DeadLetter,可以使用此策略。

<broker>
...
    <plugins>
		<!-- 丟棄所有死信-->
		<discardingDLQBrokerPlugindropAll="true"  dropTemporaryTopics="true" dropTemporaryQueues="true" />
		<!-- 丟棄指定死信-->
		<!-- <discardingDLQBrokerPlugindropOnly="MY.EXAMPLE.TOPIC.29 MY.EXAMPLE.QUEUE.87" reportInterval="1000" />-->
		<!--使用丟棄正則匹配到死信-->
		<!--<discardingDLQBrokerPlugindropOnly="MY.EXAMPLE.TOPIC.[0-9]{3} MY.EXAMPLE.QUEUE.[0-9]{3}" reportInterval="3000"/>-->
    </plugins>
    ...
 </broker>

  2)、定時拋棄死信佇列:預設情況下,ActiveMQ永遠不會過期傳送到DLQ的訊息。但是,從ActiveMQ5.12開始,deadLetterStrategy支援expiration屬性,其值以毫秒為單位。

<policyEntryqueue=">"…>
   ...
	<deadLetterStrategy>
		<sharedDeadLetterStrategy processExpired="true" expiration="30000"/>
	</deadLetterStrategy>
   ...
</policyEntry>

   3)、慢消費者策略設定:Broker將會啟動一個後臺執行緒用來檢測所有的慢速消費者,並定期關閉關閉它們;中斷慢速消費者,慢速消費將會被關閉。abortConnection是否關閉連線;如果慢速消費者最後一個ACK距離現在的時間間隔超過閥maxTimeSinceLastAck,則中斷慢速消費者。

<policyEntryqueue=">"…>
    …
    <slowConsumerStrategy>
        <abortSlowConsumerStrategyabortConnection="false"/>  <!--不關閉底層連結-->
    </slowConsumerStrategy>
    …
</policyEntry>