在簡單的描述,我有一個servlet和響應時間長,所以我決定把它分成兩個部分,一個剛剛組成到客戶端的響應,而第二個假設進行一些業務邏輯和商店導致DB。爲了減少響應時間,我使用ThreadPoolExecutor
結合ArrayBlockingQueue
異步執行業務邏輯。使用ArrayBlockingQueue
如果請求對同一客戶端是連續的,我可以確保原始的FIFO排序。這是重要的先決條件。ArrayBlockingQueue同步
這裏是一個片段:
的Servlet
public class HelloServlet extends HttpServlet {
AsyncExecutor exe = new AsyncExecutor();
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
PrintWriter w = resp.getWriter();
exe.executeAsync(exe.new Task(this));
w.print("HELLO TO CLIENT");
}
protected void someBusinessMethod(){
// long time execution here
}
}
和執行人
public class AsyncExecutor {
static final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(10, true);
static final Executor executor = new ThreadPoolExecutor(3, 5, 20L, TimeUnit.SECONDS, queue);
public void executeAsync(Task t){
boolean isTaskAccepted = false;
while(!isTaskAccepted){
try {
executor.execute(t);
isTaskAccepted = true;
} catch (RejectedExecutionException e){
}
}
}
class Task implements Runnable{
private HelloServlet servlet;
Task(HelloServlet servlet){
this.servlet = servlet;
}
@Override
public void run() {
// just call back to servlet's business method
servlet.someBusinessMethod();
}
}
}
,如果我是隻部署一個Tomcat的節點本實施工作得很好,因爲我只有一個ArrayBlockingQueue在應用中。但是,如果前面有幾個節點和負載平衡器,那麼我不能保證對同一客戶端的異步執行請求的FIFO排序,因爲我已經有多個隊列。 我的問題是,如何保證在集羣(多節點)部署中爲同一客戶端異步執行相同的請求順序?我認爲ActiveMQ可能是一個解決方案(不適合我)或負載平衡器配置,或者它可以在代碼中實現?
希望其中的一些想法幫助。
感謝薩姆您提示建議。
在第一篇文章我非常簡化的方式描述的問題,以便澄清它更好,讓我們說,我有部署到Tomcat遺留web應用程序,它提供了一些授權模式(舊)。然後,我們獲得了一個新的許可模式(這是一個GlassFish應用程序),我們需要與舊版本一起使用。對於最終用戶來說,這種整合必須透明且不侵入。所以用戶請求是這樣服務的。
- 主叫方發送一個請求(創建例如訂閱)
- 儘管p的結果執行舊的許可模式
- 的新的許可模式
- 執行業務邏輯的業務邏輯。在舊的許可模式格式第2頁第3迴響應返回給呼叫方
- (可選)如有
處理第3頁的失敗這是用Aspect來實現的,它攔截p.1的請求並順序執行剩下的東西。正如我在前一篇文章中所說的那樣,執行時間可能很長,這就是爲什麼我想使其異步。讓我們看一下Aspect的代碼片段(而不是第一篇文章中的Servlet)。
@Aspect @Component public class MyAspect {
@Autowired
private ApplicationContext ctx;
@Autowired
private AsyncExecutor asyncExecutor;
@Around("@annotation(executeOpi)")
public Object around(ProceedingJoinPoint jp, ExecuteOpi executeOpi) throws Throwable {
LegacyMapper newModelExecutor = ctx.getBean(executeOpi.legacyMapper());
// executes a new model and then return result in the format of old model
Object result = newModelExecutor.executeNewModelLogic(joinPoint.getArgs());
// executes old model logic asynchronously
asyncExecutor.executeAsync(asyncExecutor.new Task(this, jp)
return object
}
public void executeOldModelLogic(ProceedingJoinPoint jp) throws Throwable{
// long time execution here
jp.proceed();
}
}
有了這個實現在第一篇文章,我可以保證的executeOldModelLogic方法FIFO順序,如果請求得出相同的Tomcat節點。但是在多節點部署和循環LB前面的情況下,如果對於同一個調用者,「舊模型中的更新訂閱」可以優先於ArrayBlockingQueue,而不是「在舊模型中創建訂閱」,那麼最終會出現這種情況一個錯誤的邏輯錯誤。
至於你點建議:
P1,P2和P4:我可能不能使用它作爲一個解決方案,因爲我沒有對象的狀態如此。你看,我傳遞給Runnable任務看點和JoinPoint的一個引用來撥打電話回executeOldModelLogic從Runnable的看點
P3不知道這可能是值得探討
P5這是一個方向我想進一步調查,我有一種直覺,只有在特定條件下才能解決我的問題。