JMS API中約定了Client端可以使用四種ACK_MODE,在javax.jms.Session接口中:
- AUTO_ACKNOWLEDGE = 1 自動確認(rèn)
- CLIENT_ACKNOWLEDGE = 2 客戶端手動確認(rèn)
- DUPS_OK_ACKNOWLEDGE = 3 自動批量確認(rèn)
- SESSION_TRANSACTED = 0 事務(wù)提交并確認(rèn)
- INDIVIDUAL_ACKNOWLEDGE = 4 單條消息確認(rèn)(AcitveMQ補充的一個自定義的ACK_MODE鬼譬,只有ActiveMQ支持)
ACK_MODE描述了Consumer與broker確認(rèn)消息的方式(時機),比如當(dāng)消息被Consumer接收之后,Consumer將在何時確認(rèn)消息。對于broker而言恨闪,只有接收到ACK指令,才會認(rèn)為消息被正確的接收或者處理成功了究飞。ACK在consumer與Broker之間建立一種簡單的“擔(dān)敝们”機制堂鲤。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
我們需要在創(chuàng)建Session時指定ACK_MODE,由此可見,ACK_MODE將是session共享的,意味著一個session下所有的 consumer都使用同一種ACK_MODE。
但如果此session為事務(wù)類型,用戶指定的ACK_MODE將被忽略,而強制使用"SESSION_TRANSACTED"類型媒峡。如果session非事務(wù)類型時,也將不能將ACK_MODE設(shè)定為"SESSION_TRANSACTED"瘟栖。
Client端指定了ACK_MODE,但是在Client與broker在交換ACK指令的時候,還需要告知ACK_TYPE,ACK_TYPE表示此確認(rèn)指令的類型,比如Consumer消費消息時出現(xiàn)異常,就需要向broker發(fā)送ACK指令,ACK_TYPE為"REDELIVERED_ACK_TYPE",那么broker就會重新發(fā)送此消息谅阿。在JMS API中并沒有定義ACT_TYPE,因為它通常是一種內(nèi)部機制,并不會面向開發(fā)者半哟。ActiveMQ中定義了如下幾種ACK_TYPE(參看MessageAck類):
- DELIVERED_ACK_TYPE = 0 消息"已接收",但尚未處理結(jié)束
- STANDARD_ACK_TYPE = 2 "標(biāo)準(zhǔn)"類型,通常表示為消息"處理成功"签餐,broker端可以刪除消息了
- POSION_ACK_TYPE = 1 消息"錯誤",通常表示"拋棄"此消息镜沽,比如消息重發(fā)多次后,都無法正確處理時贱田,消息將會被刪除或者DLQ(死信隊列)
- REDELIVERED_ACK_TYPE = 3 消息需"重發(fā)"缅茉,比如consumer處理消息時拋出了異常,broker稍后會重新發(fā)送此消息
- INDIVIDUAL_ACK_TYPE = 4 表示只確認(rèn)"單條消息",無論在任何ACK_MODE下
- UNMATCHED_ACK_TYPE = 5 BROKER間轉(zhuǎn)發(fā)消息時,接收端"拒絕"消息
ACK指令中不同的ACK_MODE,將意味著在不同的時機發(fā)送ACK指令,每個ACK指令中又會包含不同的ACK_TYPE,那么broker端就可以根據(jù)ACK_TYPE來決定此消息的后續(xù)操作男摧。
ACK_MODE:
AUTO_ACKNOWLEDGE : 自動確認(rèn),這就意味著消息的確認(rèn)時機將由consumer擇機確認(rèn)蔬墩。對于consumer而言,optimizeAcknowledge(優(yōu)化應(yīng)答)屬性只會在AUTO_ACK模式下有效耗拓。AUTO_ACK又分為兩種情況:
在同步(receive)情況下拇颅,單條消息將立即確認(rèn),如果開發(fā)者在處理message過程中出現(xiàn)異常乔询,會導(dǎo)致此消息也不會redelivery,可能造成消息的丟失樟插。
在異步(messageListener)情況下,將會調(diào)用listener.onMessage(message)
,此后再ACK。如果onMessage方法異常且沒有被catch,將導(dǎo)致client端補充發(fā)送一個ACK_TYPE為REDELIVERED_ACK_TYPE確認(rèn)指令竿刁,此消息會被redelivery黄锤。如果重發(fā)次數(shù)到達閾值,此消息將會進入DLQ食拜。
public class Listener implements MessageListener {
public void onMessage(Message message) {
int i = 8/0;//會導(dǎo)致redelivery
try {
if(message instanceof ActiveMQTextMessage){
ActiveMQTextMessage textMessage = (ActiveMQTextMessage) message;
System.out.println("收到的消息:" + textMessage.getText()); }
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Listener implements MessageListener {
public void onMessage(Message message) {
try {
int i = 8/0;//不會導(dǎo)致redelivery
if(message instanceof ActiveMQTextMessage){
ActiveMQTextMessage textMessage = (ActiveMQTextMessage) message;
System.out.println("收到的消息:" + textMessage.getText());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
因此當(dāng)我們使用messageListener方式消費消息時鸵熟,通常建議在onMessage方法中使用try-catch,這樣可以在處理消息出錯時記錄一些信息,而不是讓consumer不斷去重發(fā)消息负甸。
CLIENT_ACKNOWLEDGE : 客戶端手動確認(rèn)流强,這就意味著AcitveMQ將不會自動ACK任何消息。如果一個conmuser在消費結(jié)束前沒有調(diào)用message.acknowledge()確認(rèn)一個消息呻待,之后調(diào)用其他conmuser時會再次消費它打月,因為對于broker而言,那些尚未真正ACK的消息被視為未消費蚕捉,直到它被確認(rèn)奏篙。
DUPS_ACKNOWLEGE:也是一種潛在的AUTO_ACK,在確認(rèn)消息的條數(shù)和時間上有所不同。
SESSION_TRANSACTED : 當(dāng)session使用事務(wù)時鱼冀,就是使用此模式报破。
在事務(wù)開啟之后悠就,和session.commit()之前,所有消費的消息充易,要么全部正常確認(rèn)梗脾,要么全部redelivery。commit()方法將會導(dǎo)致當(dāng)前session的事務(wù)中所有消息立即被確認(rèn)盹靴。 當(dāng)session.commit()異常時炸茧,會自動調(diào)用inner-rollback回滾事務(wù)(也可以捕捉到異常,手動調(diào)用session.rollback()回滾事務(wù))稿静,也可以在事務(wù)開始之后的任何時候手動調(diào)用session.rollback()梭冠。rollback意味著當(dāng)前事務(wù)的結(jié)束,事務(wù)中所有的消息都將被重發(fā)改备。