導航:首頁 > 編程語言 > java線程消息隊列

java線程消息隊列

發布時間:2024-05-27 15:48:55

㈠ 濡備綍鐢↗AVA瀹炵幇Linux涓婄殑娑堟伅闃熷垪鍔熻兘

浣犲ソ錛岃繘鍏Linux鍚庯紝鐐瑰嚮[搴旂敤紼嬪簭]錛岄夋嫨[緋葷粺璁劇疆]錛屽啀閫夋嫨[緗戠粶]錛屾墦寮緗戠粶璁劇疆欏甸潰錛岀偣鍑籟鏂板緩]錛屽湪鍒楄〃涓閫夋嫨[xDSL]錛屼笅涓姝ラ夋嫨榪炴帴xDSL鐨勭綉鍗★紙濡傛灉浣犳湁涓ゅ潡緗戝崱鐨勮瘽錛夛紝鍐嶈劇疆鍚嶇О錛岀敤鎴峰悕銆佸瘑鐮佷俊鎮鍚庯紝鐐瑰嚮][搴旂敤]鍗寵劇疆瀹屾瘯銆傜偣鍑籟嬋媧籡鍗沖彲榪炴帴鍒頒簰鑱旂綉銆

java涓鍏充簬濡備綍瀹炵幇澶氱嚎紼嬫秷鎮闃熷垪鐨勫疄渚(java澶氱嚎紼嬮氫俊)

java涓鐨勬秷鎮闃熷垪

娑堟伅闃熷垪鏄綰跨▼闂撮氳鐨勬墜孌碉細

importjava.util.*

publicclassMsgQueue{

privateVectorqueue=null;

publicMsgQueue(){

queue=newVector();

}

publicvoidsend(Objecto)

{

queue.addElement(o);

}

publicObjectrecv()

{

if(queue.size()==0)

returnnull;

Objecto=queue.();

queue.(0);//orqueue[0]=nullcanalsowork

returno;

}

}

鍥犱負java涓鏄痩ockedbyobject鐨勬墍浠ユ坊鍔犲氨鍙浠ョ敤浜庣嚎紼嬪悓姝ラ攣瀹氬硅薄

鍙浠ヤ綔涓哄氱嚎紼嬪勭悊澶氫換鍔$殑瀛樻斁task鐨勯槦鍒椼備粬鐨刢lient鍖呮嫭灝佽呭ソ鐨則ask綾諱互鍙妕hread綾

Java鐨勫氱嚎紼-綰跨▼闂寸殑閫氫俊2009-08-2521:58

1.綰跨▼鐨勫嚑縐嶇姸鎬

綰跨▼鏈夊洓縐嶇姸鎬侊紝浠諱綍涓涓綰跨▼鑲瀹氬勪簬榪欏洓縐嶇姸鎬佷腑鐨勪竴縐嶏細

1)浜х敓錛圢ew錛夛細綰跨▼瀵硅薄宸茬粡浜х敓錛屼絾灝氭湭琚鍚鍔錛屾墍浠ユ棤娉曟墽琛屻傚傞氳繃new浜х敓浜嗕竴涓綰跨▼瀵硅薄鍚庢病瀵瑰畠璋冪敤start()鍑芥暟涔嬪墠銆

2)鍙鎵ц岋紙Runnable錛夛細姣忎釜鏀鎸佸氱嚎紼嬬殑緋葷粺閮芥湁涓涓鎺掔▼鍣錛屾帓紼嬪櫒浼氫粠綰跨▼奼犱腑閫夋嫨涓涓綰跨▼騫跺惎鍔ㄥ畠銆傚綋涓涓綰跨▼澶勪簬鍙鎵ц岀姸鎬佹椂錛岃〃紺哄畠鍙鑳芥e勪簬綰跨▼奼犱腑絳夊緟鎺掓帓紼嬪櫒鍚鍔ㄥ畠錛涗篃鍙鑳藉畠宸叉e湪鎵ц屻傚傛墽琛屼簡涓涓綰跨▼瀵硅薄鐨剆tart()鏂規硶鍚庯紝綰跨▼灝卞勪簬鍙鎵ц岀姸鎬侊紝浣嗘樉鑰屾槗瑙佺殑鏄姝ゆ椂綰跨▼涓嶄竴瀹氭e湪鎵ц屼腑銆

3)姝諱骸錛圖ead錛夛細褰撲竴涓綰跨▼姝e父緇撴潫錛屽畠渚垮勪簬姝諱骸鐘舵併傚備竴涓綰跨▼鐨剅un()鍑芥暟鎵ц屽畬姣曞悗綰跨▼灝辮繘鍏ユ諱骸鐘舵併

4)鍋滄粸錛圔locked錛夛細褰撲竴涓綰跨▼澶勪簬鍋滄粸鐘舵佹椂錛岀郴緇熸帓紼嬪櫒灝變細蹇界暐瀹冿紝涓嶅瑰畠榪涜屾帓紼嬨傚綋澶勪簬鍋滄粸鐘舵佺殑綰跨▼閲嶆柊鍥炲埌鍙鎵ц岀姸鎬佹椂錛屽畠鏈夊彲鑳介噸鏂版墽琛屻傚傞氳繃瀵逛竴涓綰跨▼璋冪敤wait()鍑芥暟鍚庯紝綰跨▼灝辮繘鍏ュ仠婊炵姸鎬侊紝鍙鏈夊綋涓ゆ″硅ョ嚎紼嬭皟鐢╪otify鎴杗otifyAll鍚庡畠鎵嶈兘涓ゆ″洖鍒板彲鎵ц岀姸鎬併

2.classThread涓嬬殑甯哥敤鍑芥暟鍑芥暟

2.1suspend()銆乺esume()

1)閫氳繃suspend()鍑芥暟錛屽彲浣跨嚎紼嬭繘鍏ュ仠婊炵姸鎬併傞氳繃suspend()浣跨嚎紼嬭繘鍏ュ仠婊炵姸鎬佸悗錛岄櫎闈炴敹鍒皉esume()娑堟伅錛屽惁鍒欒ョ嚎紼嬩笉浼氬彉鍥炲彲鎵ц岀姸鎬併

2)褰撹皟鐢╯uspend()鍑芥暟鍚庯紝綰跨▼涓嶄細閲婃斁瀹冪殑鈥滈攣鏍囧織鈥濄

渚11錛

classextendsThread{

publicstaticintshareVar=0;

public(Stringname){

super(name);

}

publicvoidrun(){

if(shareVar==0){

for(inti=0;i<5;i){

shareVar;

if(shareVar==5){

this.suspend();//錛1錛

}}}

else{

System.out.print(Thread.().getName());

System.out.println("shareVar="shareVar);

this.resume();//錛2錛

}}

}

publicclassTestThread{

publicstaticvoidmain(String[]args){

t1=new("t1");

t2=new("t2");

t1.start();//錛5錛

//t1.start();//錛3錛

t2.start();//錛4錛

}}

㈢ Java 開源消息隊列ActiveMQ等

你這個Queue是一個在內存里的數據結構類,提供了入隊出隊方法。實例化以後可以使用。

MessageQueue是一個Java的協議,是基於標準的,ActiveMQ是這個協議標準的具體實現。這個隊列具有MessageQueue的通用功能,例如支持:發布者-訂閱者,點到點的多種方式。用於SOA的大型分布式環境,12306搶票的時候你看見有多少人排隊等待,就是使用的這個。

12306的後台伺服器是分布式的,比如接受訂票功能有100台伺服器,一個LinkedBlockingQueue的范圍只是一台機器,這樣的話就會出現多個隊,而且功能單一。這時候就會把訂票請求發送給MessageQueue ,這個東西是分布式、非同步的。。。。是完全不同的兩個東西,沒有可比性

㈣ 消息隊列原理及選型

消息隊列(Message Queue)是一種進程間通信或同一進程的不同線程間的通信方式。

Broker(消息伺服器)
Broker的概念來自與Apache ActiveMQ,通俗的講就是MQ的伺服器。

Procer(生產者)
業務的發起方,負責生產消息傳輸給broker

Consumer(消費者)
業務的處理方,負責從broker獲取消息並進行業務邏輯處理

Topic(主題)
發布訂閱模式下的消息統一匯集地,不同生產者向topic發送消息,由MQ伺服器分發到不同的訂閱 者,實現消息的廣播

Queue(隊列)
PTP模式下,特定生產者向特定queue發送消息,消費者訂閱特定的queue完成指定消息的接收。

Message(消息體)
根據不同通信協議定義的固定格式進行編碼的數據包,來封裝業務數據,實現消息的傳輸

點對點模型用於消息生產者和消息消費者之間點到點的通信。

點對點模式包含三個角色:

每個消息都被發送到一個特定的隊列,接收者從隊列中獲取消息。隊列保留著消息,可以放在內存 中也可以持久化,直到他們被消費或超時。

特點:

發布訂閱模型包含三個角色:

多個發布者將消息發送到Topic,系統將這些消息傳遞給多個訂閱者。

特點:

AMQP即Advanced Message Queuing Protocol,是應用層協議的一個開放標准,為面向消息的中間件設計。消息中間件主要用於組件之間的解耦,消息的發送者無需知道消息使用者的存在,反之亦然。AMQP 的主要特徵是面向消息、隊列、路由(包括點對點和發布/訂閱)、可靠性、安全。

優點:可靠、通用

MQTT(Message Queuing Telemetry Transport,消息隊列遙測傳輸)是IBM開發的一個即時通訊協議,有可能成為物聯網的重要組成部分。該協議支持所有平台,幾乎可以把所有聯網物品和外部連接起來,被用來當做感測器和致動器(比如通過Twitter讓房屋聯網)的通信協議。

優點:格式簡潔、佔用帶寬小、移動端通信、PUSH、嵌入式系統

STOMP(Streaming Text Orientated Message Protocol)是流文本定向消息協議,是一種為MOM(Message Oriented Middleware,面向消息的中間件)設計的簡單文本協議。STOMP提供一個可互操作的連接格式,允許客戶端與任意STOMP消息代理(Broker)進行交互。

優點:命令模式(非topicqueue模式)

XMPP(可擴展消息處理現場協議,Extensible Messaging and Presence Protocol)是基於可擴展標記語言(XML)的協議,多用於即時消息(IM)以及在線現場探測。適用於伺服器之間的准即時操作。核心是基於XML流傳輸,這個協議可能最終允許網際網路用戶向網際網路上的其他任何人發送即時消息,即使其操作系統和瀏覽器不同。

優點:通用公開、兼容性強、可擴展、安全性高,但XML編碼格式佔用帶寬大

RabbitMQ 是實現 AMQP(高級消息隊列協議)的消息中間件的一種,最初起源於金融系統,用於在分布式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。 RabbitMQ 主要是為了實現系統之間的雙向解耦而實現的。當生產者大量產生數據時,消費者無法快速消費,那麼需要一個中間層。保存這個數據。

RabbitMQ 是一個開源的 AMQP 實現,伺服器端用Erlang語言編寫,支持多種客戶端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP 等,支持 AJAX。用於在分布式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。

Channel(通道)
道是兩個管理器之間的一種單向點對點的的通信連接,如果需要雙向交流,可以建立一對通道。

Exchange(消息交換機)
Exchange類似於數據通信網路中的交換機,提供消息路由策略。

RabbitMq中,procer不是通過信道直接將消息發送給queue,而是先發送給Exchange。一個Exchange可以和多個Queue進行綁定,procer在傳遞消息的時候,會傳遞一個ROUTING_KEY,Exchange會根據這個ROUTING_KEY按照特定的路由演算法,將消息路由給指定的queue。和Queue一樣,Exchange也可設置為持久化,臨時或者自動刪除。

Exchange有4種類型:direct(默認),fanout, topic, 和headers。
不同類型的Exchange轉發消息的策略有所區別:

Binding(綁定)
所謂綁定就是將一個特定的 Exchange 和一個特定的 Queue 綁定起來。Exchange 和Queue的綁定可以是多對多的關系。

Routing Key(路由關鍵字)
exchange根據這個關鍵字進行消息投遞。

vhost(虛擬主機)
在RabbitMq server上可以創建多個虛擬的message broker,又叫做virtual hosts (vhosts)。每一個vhost本質上是一個mini-rabbitmq server,分別管理各自的exchange,和bindings。vhost相當於物理的server,可以為不同app提供邊界隔離,使得應用安全的運行在不同的vhost實例上,相互之間不會干擾。procer和consumer連接rabbit server需要指定一個vhost。

假設P1和C1注冊了相同的Broker,Exchange和Queue。P1發送的消息最終會被C1消費。
基本的通信流程大概如下所示:

Consumer收到消息時需要顯式的向rabbit broker發送basic。ack消息或者consumer訂閱消息時設置auto_ack參數為true。

在通信過程中,隊列對ACK的處理有以下幾種情況:

即消息的Ackownledge確認機制,為了保證消息不丟失,消息隊列提供了消息Acknowledge機制,即ACK機制,當Consumer確認消息已經被消費處理,發送一個ACK給消息隊列,此時消息隊列便可以刪除這個消息了。如果Consumer宕機/關閉,沒有發送ACK,消息隊列將認為這個消息沒有被處理,會將這個消息重新發送給其他的Consumer重新消費處理。

消息的收發處理支持事務,例如:在任務中心場景中,一次處理可能涉及多個消息的接收、處理,這應該處於同一個事務范圍內,如果一個消息處理失敗,事務回滾,消息重新回到隊列中。

消息的持久化,對於一些關鍵的核心業務來說是非常重要的,啟用消息持久化後,消息隊列宕機重啟後,消息可以從持久化存儲恢復,消息不丟失,可以繼續消費處理。

fanout 模式
模式特點:

direct 模式
任何發送到Direct Exchange的消息都會被轉發到routing_key中指定的Queue。

如果一個exchange 聲明為direct,並且bind中指定了routing_key,那麼發送消息時需要同時指明該exchange和routing_key。

簡而言之就是:生產者生成消息發送給Exchange, Exchange根據Exchange類型和basic_publish中的routing_key進行消息發送 消費者:訂閱Exchange並根據Exchange類型和binding key(bindings 中的routing key) ,如果生產者和訂閱者的routing_key相同,Exchange就會路由到那個隊列。

topic 模式
前面講到direct類型的Exchange路由規則是完全匹配binding key與routing key,但這種嚴格的匹配方式在很多情況下不能滿足實際業務需求。

topic類型的Exchange在匹配規則上進行了擴展,它與direct類型的Exchage相似,也是將消息路由到binding key與routing key相匹配的Queue中,但這里的匹配規則有些不同。
它約定:

以上圖中的配置為例,routingKey=」quick.orange.rabbit」的消息會同時路由到Q1與Q2,routingKey=」lazy.orange.fox」的消息會路由到Q1,routingKey=」lazy.brown.fox」的消息會路由到Q2,routingKey=」lazy.pink.rabbit」的消息會路由到Q2(只會投遞給Q2一次,雖然這個routingKey與Q2的兩個bindingKey都匹配);routingKey=」quick.brown.fox」、routingKey=」orange」、routingKey=」quick.orange.male.rabbit」的消息將會被丟棄,因為它們沒有匹配任何bindingKey。

RabbitMQ,部署分三種模式:單機模式,普通集群模式,鏡像集群模式。

普通集群模式
多台機器部署,每個機器放一個rabbitmq實例,但是創建的queue只會放在一個rabbitmq實例上,每個實例同步queue的元數據。

如果消費時連的是其他實例,那個實例會從queue所在實例拉取數據。這就會導致拉取數據的開銷,如果那個放queue的實例宕機了,那麼其他實例就無法從那個實例拉取,即便開啟了消息持久化,讓rabbitmq落地存儲消息的話,消息不一定會丟,但得等這個實例恢復了,然後才可以繼續從這個queue拉取數據, 這就沒什麼高可用可言,主要是提供吞吐量 ,讓集群中多個節點來服務某個queue的讀寫操作。

鏡像集群模式

queue的元數據和消息都會存放在多個實例,每次寫消息就自動同步到多個queue實例里。這樣任何一個機器宕機,其他機器都可以頂上,但是性能開銷太大,消息同步導致網路帶寬壓力和消耗很重,另外,沒有擴展性可言,如果queue負載很重,加機器,新增的機器也包含了這個queue的所有數據,並沒有辦法線性擴展你的queue。此時,需要開啟鏡像集群模式,在rabbitmq管理控制台新增一個策略,將數據同步到指定數量的節點,然後你再次創建queue的時候,應用這個策略,就會自動將數據同步到其他的節點上去了。

Kafka 是 Apache 的子項目,是一個高性能跨語言的分布式發布/訂閱消息隊列系統(沒有嚴格實現 JMS 規范的點對點模型,但可以實現其效果),在企業開發中有廣泛的應用。高性能是其最大優勢,劣勢是消息的可靠性(丟失或重復),這個劣勢是為了換取高性能,開發者可以以稍降低性能,來換取消息的可靠性。

一個Topic可以認為是一類消息,每個topic將被分成多個partition(區),每個partition在存儲層面是append log文件。任何發布到此partition的消息都會被直接追加到log文件的尾部,每條消息在文件中的位置稱為offset(偏移量),offset為一個long型數字,它是唯一標記一條消息。它唯一的標記一條消息。kafka並沒有提供其他額外的索引機制來存儲offset,因為在kafka中幾乎不允許對消息進行「隨機讀寫」。

Kafka和JMS(Java Message Service)實現(activeMQ)不同的是:即使消息被消費,消息仍然不會被立即刪除。日誌文件將會根據broker中的配置要求,保留一定的時間之後刪除;比如log文件保留2天,那麼兩天後,文件會被清除,無論其中的消息是否被消費。kafka通過這種簡單的手段,來釋放磁碟空間,以及減少消息消費之後對文件內容改動的磁碟IO開支。

對於consumer而言,它需要保存消費消息的offset,對於offset的保存和使用,有consumer來控制;當consumer正常消費消息時,offset將會"線性"的向前驅動,即消息將依次順序被消費。事實上consumer可以使用任意順序消費消息,它只需要將offset重置為任意值。(offset將會保存在zookeeper中,參見下文)

kafka集群幾乎不需要維護任何consumer和procer狀態信息,這些信息有zookeeper保存;因此procer和consumer的客戶端實現非常輕量級,它們可以隨意離開,而不會對集群造成額外的影響。

partitions的設計目的有多個。最根本原因是kafka基於文件存儲。通過分區,可以將日誌內容分散到多個server上,來避免文件尺寸達到單機磁碟的上限,每個partiton都會被當前server(kafka實例)保存;可以將一個topic切分多任意多個partitions,來消息保存/消費的效率。此外越多的partitions意味著可以容納更多的consumer,有效提升並發消費的能力。(具體原理參見下文)。

一個Topic的多個partitions,被分布在kafka集群中的多個server上;每個server(kafka實例)負責partitions中消息的讀寫操作;此外kafka還可以配置partitions需要備份的個數(replicas),每個partition將會被備份到多台機器上,以提高可用性。

基於replicated方案,那麼就意味著需要對多個備份進行調度;每個partition都有一個server為"leader";leader負責所有的讀寫操作,如果leader失效,那麼將會有其他follower來接管(成為新的leader);follower只是單調的和leader跟進,同步消息即可。由此可見作為leader的server承載了全部的請求壓力,因此從集群的整體考慮,有多少個partitions就意味著有多少個"leader",kafka會將"leader"均衡的分散在每個實例上,來確保整體的性能穩定。

Procers
Procer將消息發布到指定的Topic中,同時Procer也能決定將此消息歸屬於哪個partition;比如基於"round-robin"方式或者通過其他的一些演算法等。

Consumers
本質上kafka只支持Topic。每個consumer屬於一個consumer group;反過來說,每個group中可以有多個consumer。發送到Topic的消息,只會被訂閱此Topic的每個group中的一個consumer消費。

如果所有的consumer都具有相同的group,這種情況和queue模式很像;消息將會在consumers之間負載均衡。

如果所有的consumer都具有不同的group,那這就是"發布-訂閱";消息將會廣播給所有的消費者。

在kafka中,一個partition中的消息只會被group中的一個consumer消費;每個group中consumer消息消費互相獨立;我們可以認為一個group是一個"訂閱"者,一個Topic中的每個partions,只會被一個"訂閱者"中的一個consumer消費,不過一個consumer可以消費多個partitions中的消息。kafka只能保證一個partition中的消息被某個consumer消費時,消息是順序的。事實上,從Topic角度來說,消息仍不是有序的。

Kafka的設計原理決定,對於一個topic,同一個group中不能有多於partitions個數的consumer同時消費,否則將意味著某些consumer將無法得到消息。

Guarantees

Kafka就比較適合高吞吐量並且允許少量數據丟失的場景,如果非要保證「消息可靠傳輸」,可以使用JMS。

Kafka Procer 消息發送有兩種方式(配置參數 procer.type):

對於同步方式(procer.type=sync)?Kafka Procer 消息發送有三種確認方式(配置參數 acks):

kafka的設計初衷是希望作為一個統一的信息收集平台,能夠實時的收集反饋信息,並需要能夠支撐較大的數據量,且具備良好的容錯能力。

持久性
kafka使用文件存儲消息,這就直接決定kafka在性能上嚴重依賴文件系統的本身特性。且無論任何OS下,對文件系統本身的優化幾乎沒有可能。文件緩存/直接內存映射等是常用的手段。因為kafka是對日誌文件進行append操作,因此磁碟檢索的開支是較小的;同時為了減少磁碟寫入的次數,broker會將消息暫時buffer起來,當消息的個數(或尺寸)達到一定閥值時,再flush到磁碟,這樣減少了磁碟IO調用的次數。

性能
需要考慮的影響性能點很多,除磁碟IO之外,我們還需要考慮網路IO,這直接關繫到kafka的吞吐量問題。kafka並沒有提供太多高超的技巧;對於procer端,可以將消息buffer起來,當消息的條數達到一定閥值時,批量發送給broker;對於consumer端也是一樣,批量fetch多條消息。不過消息量的大小可以通過配置文件來指定。對於kafka broker端,似乎有個sendfile系統調用可以潛在的提升網路IO的性能:將文件的數據映射到系統內存中,socket直接讀取相應的內存區域即可,而無需進程再次和交換。 其實對於procer/consumer/broker三者而言,CPU的開支應該都不大,因此啟用消息壓縮機制是一個良好的策略;壓縮需要消耗少量的CPU資源,不過對於kafka而言,網路IO更應該需要考慮。可以將任何在網路上傳輸的消息都經過壓縮。kafka支持gzip/snappy等多種壓縮方式。

生產者
負載均衡: procer將會和Topic下所有partition leader保持socket連接;消息由procer直接通過socket發送到broker,中間不會經過任何「路由層「。事實上,消息被路由到哪個partition上,有procer客戶端決定。比如可以採用「random「「key-hash「「輪詢「等,如果一個topic中有多個partitions,那麼在procer端實現「消息均衡分發「是必要的。

其中partition leader的位置(host:port)注冊在zookeeper中,procer作為zookeeper client,已經注冊了watch用來監聽partition leader的變更事件。
非同步發送:將多條消息暫且在客戶端buffer起來,並將他們批量的發送到broker,小數據IO太多,會拖慢整體的網路延遲,批量延遲發送事實上提升了網路效率。不過這也有一定的隱患,比如說當procer失效時,那些尚未發送的消息將會丟失。

消費者
consumer端向broker發送「fetch」請求,並告知其獲取消息的offset;此後consumer將會獲得一定條數的消息;consumer端也可以重置offset來重新消費消息。

在JMS實現中,Topic模型基於push方式,即broker將消息推送給consumer端。不過在kafka中,採用了pull方式,即consumer在和broker建立連接之後,主動去pull(或者說fetch)消息;這中模式有些優點,首先consumer端可以根據自己的消費能力適時的去fetch消息並處理,且可以控制消息消費的進度(offset);此外,消費者可以良好的控制消息消費的數量,batch fetch。

其他JMS實現,消息消費的位置是有prodiver保留,以便避免重復發送消息或者將沒有消費成功的消息重發等,同時還要控制消息的狀態。這就要求JMS broker需要太多額外的工作。在kafka中,partition中的消息只有一個consumer在消費,且不存在消息狀態的控制,也沒有復雜的消息確認機制,可見kafka broker端是相當輕量級的。當消息被consumer接收之後,consumer可以在本地保存最後消息的offset,並間歇性的向zookeeper注冊offset。由此可見,consumer客戶端也很輕量級。

對於JMS實現,消息傳輸擔保非常直接:有且只有一次(exactly once)。
在kafka中稍有不同:

at most once: 消費者fetch消息,然後保存offset,然後處理消息;當client保存offset之後,但是在消息處理過程中出現了異常,導致部分消息未能繼續處理。那麼此後"未處理"的消息將不能被fetch到,這就是"at most once"。

at least once: 消費者fetch消息,然後處理消息,然後保存offset。如果消息處理成功之後,但是在保存offset階段zookeeper異常導致保存操作未能執行成功,這就導致接下來再次fetch時可能獲得上次已經處理過的消息,這就是"at least once",原因offset沒有及時的提交給zookeeper,zookeeper恢復正常還是之前offset狀態。

exactly once: kafka中並沒有嚴格的去實現(基於2階段提交,事務),我們認為這種策略在kafka中是沒有必要的。

通常情況下「at-least-once」是我們首選。(相比at most once而言,重復接收數據總比丟失數據要好)。

kafka高可用由多個broker組成,每個broker是一個節點;

創建一個topic,這個topic會劃分為多個partition,每個partition存在於不同的broker上,每個partition就放一部分數據。

kafka是一個分布式消息隊列,就是說一個topic的數據,是分散放在不同的機器上,每個機器就放一部分數據。

在0.8版本以前,是沒有HA機制的,就是任何一個broker宕機了,那個broker上的partition就廢了,沒法寫也沒法讀,沒有什麼高可用性可言。

0.8版本以後,才提供了HA機制,也就是就是replica副本機制。每個partition的數據都會同步到其他的機器上,形成自己的多個replica副本。然後所有replica會選舉一個leader出來,那麼生產和消費都跟這個leader打交道,然後其他replica就是follower。

寫的時候,leader會負責把數據同步到所有follower上去,讀的時候就直接讀leader上數據即可。

kafka會均勻的將一個partition的所有replica分布在不同的機器上,從而提高容錯性。

如果某個broker宕機了也沒事,它上面的partition在其他機器上都有副本的,如果這上面有某個partition的leader,那麼此時會重新選舉一個新的leader出來,大家繼續讀寫那個新的leader即可。這就有所謂的高可用性了。

寫數據的時候,生產者就寫leader,然後leader將數據落地寫本地磁碟,接著其他follower自己主動從leader來pull數據。一旦所有follower同步好數據了,就會發送ack給leader,leader收到所有follower的ack之後,就會返回寫成功的消息給生產者。

消息丟失會出現在三個環節,分別是生產者、mq中間件、消費者:

RabbitMQ

Kafka
大體和RabbitMQ相同。

Rabbitmq
需要保證順序的消息投遞到同一個queue中,這個queue只能有一個consumer,如果需要提升性能,可以用內存隊列做排隊,然後分發給底層不同的worker來處理。

Kafka
寫入一個partition中的數據一定是有序的。生產者在寫的時候 ,可以指定一個key,比如指定訂單id作為key,這個訂單相關數據一定會被分發到一個partition中去。消費者從partition中取出數據的時候也一定是有序的,把每個數據放入對應的一個內存隊列,一個partition中有幾條相關數據就用幾個內存隊列,消費者開啟多個線程,每個線程處理一個內存隊列。

㈤ 如何用JAVA實現Linux上的消息隊列功能

下面來說說如何用不用消息隊列來進行進程間的通信,消息隊列與命名管道有很多相似之處。有關命名管道的更多內容可以參閱我的另一篇文章:Linux進程間通信——使用命名管道
一、什麼是消息隊列
消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。 每個數據塊都被認為含有一個類型,接收進程可以獨立地接收含有不同類型的數據結構。我們可以通過發送消息來避免命名管道的同步和阻塞問題。但是消息隊列與命名管道一樣,每個數據塊都有一個最大長度的限制。
Linux用宏MSGMAX和MSGMNB來限制一條消息的最大長度和一個隊列的最大長度。
二、在Linux中使用消息隊列
Linux提供了一系列消息隊列的函數介面來讓我們方便地使用它來實現進程間的通信。它的用法與其他兩個System V PIC機制,即信號量和共享內存相似。
1、msgget函數
該函數用來創建和訪問一個消息隊列。它的原型為:
int msgget(key_t, key, int msgflg);

與其他的IPC機制一樣,程序必須提供一個鍵來命名某個特定的消息隊列。msgflg是一個許可權標志,表示消息隊列的訪問許可權,它與文件的訪問許可權一樣。msgflg可以與IPC_CREAT做或操作,表示當key所命名的消息隊列不存在時創建一個消息隊列,如果key所命名的消息隊列存在時,IPC_CREAT標志會被忽略,而只返回一個標識符。
它返回一個以key命名的消息隊列的標識符(非零整數),失敗時返回-1.
2、msgsnd函數
該函數用來把消息添加到消息隊列中。它的原型為:
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);

msgid是由msgget函數返回的消息隊列標識符。
msg_ptr是一個指向准備發送消息的指針,但是消息的數據結構卻有一定的要求,指針msg_ptr所指向的消息結構一定要是以一個長整型成員變數開始的結構體,接收函數將用這個成員來確定消息的類型。所以消息結構要定義成這樣:
struct my_message{
long int message_type;
/* The data you wish to transfer*/
};

msg_sz是msg_ptr指向的消息的長度,注意是消息的長度,而不是整個結構體的長度,也就是說msg_sz是不包括長整型消息類型成員變數的長度。
msgflg用於控制當前消息隊列滿或隊列消息到達系統范圍的限制時將要發生的事情。
如果調用成功,消息數據的一分副本將被放到消息隊列中,並返回0,失敗時返回-1.
3、msgrcv函數
該函數用來從一個消息隊列獲取消息,它的原型為
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);

msgid, msg_ptr, msg_st的作用也函數msgsnd函數的一樣。
msgtype可以實現一種簡單的接收優先順序。如果msgtype為0,就獲取隊列中的第一個消息。如果它的值大於零,將獲取具有相同消息類型的第一個信息。如果它小於零,就獲取類型等於或小於msgtype的絕對值的第一個消息。
msgflg用於控制當隊列中沒有相應類型的消息可以接收時將發生的事情。
調用成功時,該函數返回放到接收緩存區中的位元組數,消息被復制到由msg_ptr指向的用戶分配的緩存區中,然後刪除消息隊列中的對應消息。失敗時返回-1.
4、msgctl函數
該函數用來控制消息隊列,它與共享內存的shmctl函數相似,它的原型為:
int msgctl(int msgid, int command, struct msgid_ds *buf);

command是將要採取的動作,它可以取3個值,
IPC_STAT:把msgid_ds結構中的數據設置為消息隊列的當前關聯值,即用消息隊列的當前關聯值覆蓋msgid_ds的值。
IPC_SET:如果進程有足夠的許可權,就把消息列隊的當前關聯值設置為msgid_ds結構中給出的值

IPC_RMID:刪除消息隊列
buf是指向msgid_ds結構的指針,它指向消息隊列模式和訪問許可權的結構。msgid_ds結構至少包括以下成員:
struct msgid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};

成功時返回0,失敗時返回-1.
三、使用消息隊列進行進程間通信
馬不停蹄,介紹完消息隊列的定義和可使用的介面之後,我們來看看它是怎麼讓進程進行通信的。由於可以讓不相關的進程進行行通信,所以我們在這里將會編寫兩個程序,msgreceive和msgsned來表示接收和發送信息。根據正常的情況,我們允許兩個程序都可以創建消息,但只有接收者在接收完最後一個消息之後,它才把它刪除。
接收信息的程序源文件為msgreceive.c的源代碼為:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>

struct msg_st
{
long int msg_type;
char text[BUFSIZ];
};

int main()
{
int running = 1;
int msgid = -1;
struct msg_st data;
long int msgtype = 0; //注意1

//建立消息隊列
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
//從隊列中獲取消息,直到遇到end消息為止
while(running)
{
if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)
{
fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
exit(EXIT_FAILURE);
}
printf("You wrote: %s\n",data.text);
//遇到end結束
if(strncmp(data.text, "end", 3) == 0)
running = 0;
}
//刪除消息隊列
if(msgctl(msgid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}

發送信息的程序的源文件msgsend.c的源代碼為:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>

#define MAX_TEXT 512
struct msg_st
{
long int msg_type;
char text[MAX_TEXT];
};

int main()
{
int running = 1;
struct msg_st data;
char buffer[BUFSIZ];
int msgid = -1;

//建立消息隊列
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}

//向消息隊列中寫消息,直到寫入end
while(running)
{
//輸入數據
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = 1; //注意2
strcpy(data.text, buffer);
//向隊列發送數據
if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
{
fprintf(stderr, "msgsnd failed\n");
exit(EXIT_FAILURE);
}
//輸入end結束輸入
if(strncmp(buffer, "end", 3) == 0)
running = 0;
sleep(1);
}
exit(EXIT_SUCCESS);
}

轉載僅供參考,版權屬於原作者。祝你愉快,滿意請採納哦

㈥ 到底什麼是消息隊列Java中如何實現消息隊列

消息隊列,顧名思義 首先是個隊列。 隊列的操作有入隊和出隊
也就是你有一個程序在產生內容然後入隊(生產者) 另一個程序讀取內容,內容出隊(消費者)
這是最最基本的概念。
我想你應該是缺乏一個使用場景。
當你不需要立即獲得結果,但是並發量又不能無限大的時候,差不多就是你需要使用消息隊列的時候。
比如你寫日誌,因為可能一個客戶端有多個操作去寫,又有很多個客戶端,顯然並發不能無窮大,於是你就需要把寫日誌的請求放入到消息隊列里,在消費者那邊依次把隊列中產生的日誌寫到資料庫里。
至於怎麼實現消息隊列,其實你本身一個普通的隊列就行呀~看你需要什麼附加功能而已。

閱讀全文

與java線程消息隊列相關的資料

熱點內容
主力吸籌派發區域指標源碼 瀏覽:695
單片機pc的低位元組怎麼算 瀏覽:230
pythoneval函數源碼 瀏覽:242
linuxmongodb服務啟動 瀏覽:766
在哪裡下載核酸檢測app 瀏覽:310
esxi啟動虛擬機命令 瀏覽:969
軍工級單片機 瀏覽:113
伺服器安全保護是什麼意思 瀏覽:789
刪除運行命令 瀏覽:720
龍之召喚伺服器如何 瀏覽:119
linux目錄跳轉 瀏覽:368
程序員和老闆稱兄道弟 瀏覽:759
直播網路連接源碼 瀏覽:736
用安卓手機怎麼登錄蘋果手機id 瀏覽:710
論文查重工具源碼 瀏覽:401
android銀聯demo 瀏覽:86
智能演算法發展 瀏覽:351
房車露營地用什麼app 瀏覽:70
spark編程指南python 瀏覽:553
phparray源碼 瀏覽:1002