2012-02-02 52 views
0

在簡單的描述,我有一個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應用程序),我們需要與舊版本一起使用。對於最終用戶來說,這種整合必須透明且不侵入。所以用戶請求是這樣服務的。

  1. 主叫方發送一個請求(創建例如訂閱)
  2. 儘管p的結果執行舊的許可模式
  3. 的新的許可模式
  4. 執行業務邏輯的業務邏輯。在舊的許可模式格式第2頁第3迴響應返回給呼叫方
  5. (可選)如有

處理第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這是一個方向我想進一步調查,我有一種直覺,只有在特定條件下才能解決我的問題。

回答

0

有一些解決方案是想到的。

  1. 使用數據庫:張貼在數據庫表中要運行的工作,有一個輔助服務器進程運行的作業,並在輸出表處的結果。然後當用戶回到網頁時,它可以從輸出表中獲取等待它們的結果。

  2. 使用JMS服務:這是一個非常輕量級的消息服務,它可以很好地與Tomcat應用程序集成。這裏的缺點是你必須運行另一個服務器端組件,並用你的應用程序構建集成層。但這不是一個很大的缺點。

  3. 切換到完整的J2EE容器(Java App Server):並使用EJB Singleton。我不得不承認,我沒有任何跨單獨的服務器實例運行Singleton的經驗,但我相信其中一些可能能夠處理它。使用EHCache或其他一些分佈式緩存:我在EHCache周圍建立了一個隊列封裝器,使它能夠像FIFO隊列一樣使用,並且它還具有RMI(或JMS)複製功能,因此多個節點將看到相同的數據。

  4. 使用負載均衡器:如果負載均衡器支持會話級別平衡,則可以將單個用戶會話的所有請求定向到同一節點。在我工作的大型Web環境中,我們無法在多個服務器之間共享用戶會話狀態,因此我們設置負載均衡以確保用戶的會話始終定向到同一個Web服務器。

希望這些想法中的一些有所幫助。