JMS API中約定了Client端可以使用四種ACK_MODE,在javax.jms.Session接口中:
- AUTO_ACKNOWLEDGE = 1 自動確認
- CLIENT_ACKNOWLEDGE = 2 客戶端手動確認
- DUPS_OK_ACKNOWLEDGE = 3 自動批量確認
- SESSION_TRANSACTED = 0 事務提交并確認
- INDIVIDUAL_ACKNOWLEDGE = 4 單條消息確認(AcitveMQ補充的一個自定義的ACK_MODE,只有ActiveMQ支持)
ACK_MODE描述了Consumer與broker確認消息的方式(時機),比如當消息被Consumer接收之后,Consumer將在何時確認消息。對于broker而言,只有接收到ACK指令,才會認為消息被正確的接收或者處理成功了。ACK在consumer與Broker之間建立一種簡單的“擔保”機制。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
我們需要在創建Session時指定ACK_MODE,由此可見,ACK_MODE將是session共享的,意味著一個session下所有的 consumer都使用同一種ACK_MODE。
但如果此session為事務類型,用戶指定的ACK_MODE將被忽略,而強制使用"SESSION_TRANSACTED"類型。如果session非事務類型時,也將不能將ACK_MODE設定為"SESSION_TRANSACTED"。
Client端指定了ACK_MODE,但是在Client與broker在交換ACK指令的時候,還需要告知ACK_TYPE,ACK_TYPE表示此確認指令的類型,比如Consumer消費消息時出現異常,就需要向broker發送ACK指令,ACK_TYPE為"REDELIVERED_ACK_TYPE",那么broker就會重新發送此消息。在JMS API中并沒有定義ACT_TYPE,因為它通常是一種內部機制,并不會面向開發者。ActiveMQ中定義了如下幾種ACK_TYPE(參看MessageAck類):
- DELIVERED_ACK_TYPE = 0 消息"已接收",但尚未處理結束
- STANDARD_ACK_TYPE = 2 "標準"類型,通常表示為消息"處理成功",broker端可以刪除消息了
- POSION_ACK_TYPE = 1 消息"錯誤",通常表示"拋棄"此消息,比如消息重發多次后,都無法正確處理時,消息將會被刪除或者DLQ(死信隊列)
- REDELIVERED_ACK_TYPE = 3 消息需"重發",比如consumer處理消息時拋出了異常,broker稍后會重新發送此消息
- INDIVIDUAL_ACK_TYPE = 4 表示只確認"單條消息",無論在任何ACK_MODE下
- UNMATCHED_ACK_TYPE = 5 BROKER間轉發消息時,接收端"拒絕"消息
ACK指令中不同的ACK_MODE,將意味著在不同的時機發送ACK指令,每個ACK指令中又會包含不同的ACK_TYPE,那么broker端就可以根據ACK_TYPE來決定此消息的后續操作。
ACK_MODE:
AUTO_ACKNOWLEDGE : 自動確認,這就意味著消息的確認時機將由consumer擇機確認。對于consumer而言,optimizeAcknowledge(優化應答)屬性只會在AUTO_ACK模式下有效。AUTO_ACK又分為兩種情況:
在同步(receive)情況下,單條消息將立即確認,如果開發者在處理message過程中出現異常,會導致此消息也不會redelivery,可能造成消息的丟失。
在異步(messageListener)情況下,將會調用listener.onMessage(message)
,此后再ACK。如果onMessage方法異常且沒有被catch,將導致client端補充發送一個ACK_TYPE為REDELIVERED_ACK_TYPE確認指令,此消息會被redelivery。如果重發次數到達閾值,此消息將會進入DLQ。
public class Listener implements MessageListener {
public void onMessage(Message message) {
int i = 8/0;//會導致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;//不會導致redelivery
if(message instanceof ActiveMQTextMessage){
ActiveMQTextMessage textMessage = (ActiveMQTextMessage) message;
System.out.println("收到的消息:" + textMessage.getText());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
因此當我們使用messageListener方式消費消息時,通常建議在onMessage方法中使用try-catch,這樣可以在處理消息出錯時記錄一些信息,而不是讓consumer不斷去重發消息。
CLIENT_ACKNOWLEDGE : 客戶端手動確認,這就意味著AcitveMQ將不會自動ACK任何消息。如果一個conmuser在消費結束前沒有調用message.acknowledge()確認一個消息,之后調用其他conmuser時會再次消費它,因為對于broker而言,那些尚未真正ACK的消息被視為未消費,直到它被確認。
DUPS_ACKNOWLEGE:也是一種潛在的AUTO_ACK,在確認消息的條數和時間上有所不同。
SESSION_TRANSACTED : 當session使用事務時,就是使用此模式。
在事務開啟之后,和session.commit()之前,所有消費的消息,要么全部正常確認,要么全部redelivery。commit()方法將會導致當前session的事務中所有消息立即被確認。 當session.commit()異常時,會自動調用inner-rollback回滾事務(也可以捕捉到異常,手動調用session.rollback()回滾事務),也可以在事務開始之后的任何時候手動調用session.rollback()。rollback意味著當前事務的結束,事務中所有的消息都將被重發。