2010-06-22 34 views
7

我在想如何節省查找通過jndi遠程ejb參考的時間。我有一個需要工作得非常快的應用程序,但它也必須調用遠程ejb,這會降低速度。緩存遠程EJB 3.0參考

所以我的解決方案是這樣的: 我帶了apache commons-pool庫,並使用它的StackObjectPool實現了我的遠程ejb引用緩存。

private static final ObjectPool pool = new StackObjectPool(new RemoteEjbFactory()); 

廠看起來是這樣的:

public static class RemoteEjbFactory extends BasePoolableObjectFactory { 

    @Override 
    public Object makeObject() { 
     try { 
      return ServiceLocator.lookup(jndi); 
     } catch (NamingException e) { 
      throw new ConfigurationException("Could not find remote ejb by given name", e); 
     } 
    } 
} 

然後我把對象從池借用它(如果在池中沒有免費的對象,它使用工廠創建一個):

SomeEjbRemote someEjb = null; 
try { 
     someEjb = (SomeEjbRemoteImpl) pool.borrowObject(); 
     someEjb.invokeRemoteMethod(); 
} catch (Throwable t) { 
     if (someEjb != null) { 
      pool.invalidateObject(someEjb); 
     } 
     pool.clear(); // Maybe its not neccessary 
     someEjb = (SomeEjbRemoteImpl) pool.borrowObject(); 
     someEjb.invokeRemoteMethod(); // this time it should work 
} 

當然返回ejb後成功invokacion

finally { 
    try { 
     pool.returnObject(someEjb); 
    } catch (Exception e) { 
     logger.error("Could not return object to pool.", e); 
    } 
} 

據我所知,不能保證遠程引用將保持連接,所以如果我們使用緩存遠程ejb捕獲異常,我們只是使該對象無效並重試。

您對這種方法有什麼看法?這是對的嗎?也許還有其他解決方案,建議?

回答

5

從規範

3.4.9 到Session Bean的併發訪問參考

這是permissable到 獲得一個會話bean參考和 試圖從併發調用相同的參考 對象多個 線程。但是,每個線程上產生的客戶端行爲取決於 目標Bean的併發語義。有關會話bean的併發行爲的詳細信息,請參見第4.3.14節和第4.8.5節。

§4.3.14摘要:

如果bean是SLSB,每次通話將被在應用一個EJB提供服務。服務器池。應用程序。服務器同步對EJB實例的調用,因此每個EJB實例都不會同時被訪問。

對於SFSB,每個調用都調度到一個特定的EJB實例和應用程序。服務器不會同步該呼叫。因此,對遠程引用的兩個併發調用可能導致併發訪問EJB實例,然後引發javax.ejb.ConcurrentAccessException。客戶端負責正確同步對遠程引用的訪問。

而§4.8.5是關於EJB單例,可能不是你正在使用的。

我假定你使用SLSB,所以你不需要在客戶端有一個池:查找一次遠程bean,並使用多個線程中的同一個引用。

你可以然而做基準,看是否使用多個基準提高性能,但增益 - 如果有的話 - 可能是忽略比較遠程調用本身的成本。

如果你仍然決定有多個遠程引用,我會建議其他設計。根據你的問題,我假設你有一個多線程的應用程序。您很有可能已使用爲線程使用池,因此用於參考的池可能是多餘的。如果每個線程在創建時都獲得遠程引用,並且彙集了線程,則不會有那麼多的遠程查找,並且設計也會簡化。

我的2美分

5

我在回答JBoss AS,因爲我對其他AS的經驗有限:

遠程JNDI引用只是(負載平衡)無連接代理(請參閱JBoss clustering proxy architecture)。對它們進行序列化並沒有問題,這意味着您可以將它們保存爲其他EJB中的成員,並像您一樣緩存它們(我不知道您的池是否序列化對象,有些緩存可以)。

關於代理失效: 代理將僅在方法調用期間打開連接,因此本身不具有「已連接」狀態。這些代理還可以擁有多個IP地址和負載平衡。在JBoss中,節點列表在每次方法調用時都會動態更新,因此引用過期的風險很小。如果所有節點停機或代理保持不活動狀態,而所有節點IP地址過期,仍然有可能發生這種情況。根據池重用策略(LRU或其他?),一旦其他緩存代理無效,其概率將有所不同。一個公平的政策將最大限度地減少在池中擁有非常舊的條目的風險,在這種情況下你希望避免這種風險。

隨着公平的政策的到來,出於同樣的原因所有陳舊的可能性增加,你的'明確的池一旦陳舊'的政策將是有道理的。另外,您需要考慮其他節點的情況。就像現在一樣,你的算法會進入一個忙環查找參考,而另一個節點關閉。我會爲重試實現一個指數回退,或者只是認爲它是一個致命的故障,並且使異常成爲運行時異常,具體取決於您是否可以使用遠程EJB暫時擱置一段時間。並讓異常捕獲特定的(如RemoteCommunicationFailedException),避免捕獲泛型異常或像Exception,Error或Throwable等錯誤。

你必須問自己的另一個問題是你想要的併發量。通常,代理對於SLSB而言是線程安全的,對於SFSB而言,代理對於單線程是安全的。 SFSB本身不是線程安全的,並且SLSB按默認順序訪問。這意味着,除非啓用對EJB 3.1 bean的併發訪問(請參閱tss link),否則每個線程需要一個遠程引用。即:彙集N個SLSB遠程引用將爲您提供N個線程的併發訪問。如果啓用併發訪問並將您的SLSB編寫爲帶註釋@ConcurrencyAttribute(NO_LOCK)的線程安全bean,那麼只需一個代理就可以實現無限併發,然後刪除整個池。你的選擇。

編輯:

ewernli是正確的,線程安全SLSB代理創建每次調用服務器上的一個新的實例。這是在4.3.14規定:

無需任何限制 對併發客戶端訪問 無狀態會話bean因爲 集裝箱航線每個請求無狀態 會話bean類的 不同實例。

這意味着你根本不需要任何池。只需使用一個遠程參考。

+0

謝謝你的回答! – nesvarbu 2010-06-23 12:28:34

+1

是否可以對單個EJB或所有遠程EJB執行有限數量的併發遠程調用? JBoss是否通過配置參數來控制它? – 2016-06-15 18:42:02