[英]How to send emails from a Java EE Batch Job
我需要每天處理大量用戶列表,以便根據某些情況向他們發送電子郵件和SMS通知。 我為此使用Java EE批處理模型。 我的Job xml如下:
<step id="sendNotification">
<chunk item-count="10" retry-limit="3">
<reader ref="myItemReader"></reader>
<processor ref="myItemProcessor"></processor>
<writer ref="myItemWriter"></writer>
<retryable-exception-classes>
<include class="java.lang.IllegalArgumentException"/>
</retryable-exception-classes>
</chunk>
</step>
MyItemReader的onOpen方法從數據庫中讀取所有用戶,而readItem()使用列表迭代器一次讀取一個用戶。 在myItemProcessor中,實際的電子郵件通知發送給用戶,然后將用戶保留在該塊的myItemWriter類中的數據庫中。
@Named
public class MyItemReader extends AbstractItemReader {
private Iterator<User> iterator = null;
private User lastUser;
@Inject
private MyService service;
@Override
public void open(Serializable checkpoint) throws Exception {
super.open(checkpoint);
List<User> users = service.getUsers();
iterator = users.iterator();
if(checkpoint != null) {
User checkpointUser = (User) checkpoint;
System.out.println("Checkpoint Found: " + checkpointUser.getUserId());
while(iterator.hasNext() && !iterator.next().getUserId().equals(checkpointUser.getUserId())) {
System.out.println("skipping already read users ... ");
}
}
}
@Override
public Object readItem() throws Exception {
User user=null;
if(iterator.hasNext()) {
user = iterator.next();
lastUser = user;
}
return user;
}
@Override
public Serializable checkpointInfo() throws Exception {
return lastUser;
}
}
我的問題是檢查點存儲了在上一個塊中執行的最后一條記錄。 如果我的下一個10個用戶中有一個大塊,並且第5個用戶的myItemProcessor中拋出異常,則在重試時將執行整個塊,並再次處理所有10個用戶。 我不希望將通知再次發送給已經處理的用戶。
有辦法解決嗎? 應該如何有效地做到這一點?
任何幫助將不勝感激。 謝謝。
您當前的項目處理器正在執行塊事務范圍之外的操作,這導致應用程序狀態不同步。 如果您的要求是僅在成功完成一個塊中的所有項目之后才發送電子郵件,則可以將電子郵件發送部分移至ItemWriterListener.afterWrite(items) 。
我將以@cheng的評論為基礎。 我在此感謝他,希望我的回答能為組織和有效介紹這些選項提供額外的價值。
正如@cheng指出的那樣,失敗意味着整個事務都會回滾,並且檢查點不會前進。
那么,如何處理您的信息塊已向某些用戶而非全部用戶發送電子郵件的事實呢? (您可能會說它回滾了,但有“副作用”。)
因此,我們可以將您的問題重新聲明為: 如何從批處理步驟發送電子郵件?
好吧,假設您有一種方法可以通過事務性API發送電子郵件(實現XAResource等),則可以使用該API。
假設您不這樣做,我將對JMS隊列進行事務性寫入,然后使用單獨的MDB發送電子郵件(如@cheng在他的評論之一中建議的那樣)。
使用這種方法,您仍然可以通過批量處理和更新數據庫來獲得效率(無論如何一次只能發送一封電子郵件),並且可以從簡單的檢查點和重新啟動中受益,而不必編寫復雜的錯誤處理程序。
這也有可能作為批處理作業甚至批處理之外的模式重用。
為了討論起見,列出了一些我認為不太理想的其他想法:
您可以使用ItemProcessListener方法構建自己的成功/失敗電子郵件列表,包括afterProcess和onProcessError 。
然后,在重新啟動時,您可以知道當前塊中已向哪些用戶發送了電子郵件,自從整個塊回滾以來,我們已經重新定位了哪些用戶,即使已經發送了一些電子郵件。
這無疑使您的批處理邏輯變得復雜,並且您還必須以某種方式保留此成功或失敗列表。 另外,這種方法可能非常針對此工作(而不是排隊等待MDB處理)。
但這很簡單,因為您只有一個批處理作業,而無需消息傳遞提供程序和單獨的應用程序組件。
如果您走這條路線,則可能要同時使用可跳過和“無回滾”可重試異常。
如果使用item-count =“ 1”定義塊,則可以避免復雜的檢查點和錯誤處理代碼。 但是,您犧牲了效率,因此只有在批處理的其他方面非常引人注目時,這才有意義:例如,通過通用接口進行作業的調度和管理,在作業失敗的步驟中重新啟動的能力
如果要走這條路線,您可能需要考慮將套接字和超時異常定義為“無回滾”異常(使用 ),因為回滾並沒有任何好處,您可能想重試網絡超時問題。
既然您特別提到了效率,我想這對您來說不合適。
這也許可以工作,但是批處理API並沒有使它變得特別容易,並且您仍然可能遇到塊已完成但一個或多個電子郵件發送失敗的情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.