2017-08-08 64 views
0

我有一個默認standalone.xml配置,其中有一個最大的20個連接的是活躍在到數據庫連接池的同時。我想,有很好的理由。我們運行一個Oracle數據庫。的Java-EE數據庫連接池耗盡了最大

有數據庫流量的合理量有第三方API的流量,例如我正在開發的企業應用程序中的SOAP和HTTP調用。

我們經常做類似如下:

@PersistenceContext(unitName = "some-pu") 
private EntityManager em; 

public void someBusinessMethod() { 
    someEntity = em.findSomeEntity(); 
    soap.callEndPoint(someEntity.getSomeProperty()); // may take up to 1 minute 
    em.update(someEntity); 
    cdiEvent.fire(finishedBusinessEvent); 
} 

然而,在這種情況下,當實體獲取和更新(實際上當整個交易完成)後,被釋放的數據庫連接收購。關於交易,一切都是容器管理的,沒有額外的註釋。我知道你不應該「持有」數據庫連接超過必要的時間,這正是我想要解決的問題。對於一個我不知道如何以編程方式釋放連接我也不認爲這是一個好主意,因爲你仍然希望能夠回滾整個事務。

那麼,如何攻擊這個問題?有多種選擇我想:

選項1,使用ManagedExecutorService

@Resource 
private ManagedExecutorService mes; 

public void someBusinessMethod() { 
    someEntity = em.findSomeEntity(); 

    this.mes.submit(() -> { 
     soap.callEndPoint(someEntity.getSomeProperty()); // may take up to 1 minute 
     em.update(someEntity); 
     cdiEvent.fire(finishedBusinessEvent); 
    }); 
} 

選項2,使用@Asynchronous

@Inject 
private AsyncBean asyncBean; 

public void someBusinessMethod() { 
    someEntity = em.findSomeEntity(); 
    this.asyncBean.process(someEntity); 
} 

public class AsyncBean { 

    @Asynchronous 
    public void process() { 
     soap.callEndPoint(someEntity.getSomeProperty()); // may take up to 1 minute 
     em.update(someEntity); 
     cdiEvent.fire(finishedBusinessEvent); 
    } 

} 

這實際上解決了數據庫連接池的問題,例如該連接在soap.callEndPoint發生時立即釋放。但它感覺不太穩定(不能指出這裏的問題)。當然,一旦你進入a-sync處理,交易就完成了,所以在肥皂調用過程中出現問題時,沒有任何回滾。

結束了... 我要長時間運行的IO任務(SOAP和HTTP調用)移動到通過隊列的卸載應用程序的一個單獨的部分,並通過隊列的一次是在應用餵養結果返回再次。在這種情況下,所有事情都是通過交易完成的,並且沒有任何連接被阻止但是這是一個很大的開銷,因此在這之前我想聽聽你的意見/最佳實踐如何解決這個問題!

+0

有多少併發web請求正常執行並處於高峯? –

+0

大約在50到100之間,峯值在200.蜻蜓配置爲等待大約30秒,然後才從池中獲取連接。沒有重試。 – Velth

+0

處理同一實體的併發更改的策略是什麼? –

回答

0

你排隊的解決方案是可行的,但也許不是必要的,如果你只是你的電話之前執行讀操作,你可以通過使用DAO模式的交易分成2個交易(你也將與隊列做)。

例子:

@Stateless 
private DaoBean dao; 

@TransactionAttribute(TransactionAttributeType.NEVER) 
public void someBusinessMethod() { 
    Entity e = dao.getEntity(); // creates and discards TX 
    e = soap.callEndPoint(e.getSomeProperty()); 
    dao.update(e); // creates TX 2 and commits 
} 

這個解決方案有幾個注意事項。

  • 上面的業務方法無法在事務處於活動狀態時調用,因爲它會否定DAO的目的(一個TX以NOT_SUPPORTED掛起)。
  • 你將不得不處理或忽略,可能對實體SOAP調用(@版本...)期間發生的可能的變化。
  • 的實體將在商業方法被分離,所以你必須給你的SOAP調用需要急於負荷的一切。

我不能告訴你這是否適合你,因爲它取決於業務電話之前完成的工作。雖然仍然複雜,但比排隊更容易。

+0

我確實立即輸入了一個事務,所以我懷疑你的目標解決方案是否真的有效:-(。dao.getEntity()'help上的'@Transactional(Transactional.TxType.REQUIRES_NEW)'是否會自己擁有它事務並且在它從數據庫中讀取完成後丟棄它(髒讀)?因此,在執行soap調用時沒有連接。但是,我需要實現@Version以查找可能的衝突。 – Velth

0

你在選擇2的時候正走在正確的軌道上,它只是需要多一點分解才能讓事務管理以一種非常短的方式發生。

既然你有一個潛在的長期運行的Web服務調用你肯定會需要在兩個獨立的交易執行數據庫更新:

  1. 短find操作
  2. 長期Web服務調用
  3. 01:短更新操作

這可以通過如下引入第三EJB來實現

切入點

@Stateless 
public class MyService { 

    @Inject 
    private AsyncService asyncService; 

    @PersistenceContext 
    private EntityManager em; 

    /* 
    * Short lived method call returns promptly 
    * (unless you need a fancy multi join query) 
    * It will execute in a short REQUIRED transaction by default 
    */ 
    public void someBusinessMethod(long entityId) { 
     SomeEntity someEntity = em.find(SomeEntity.class, entityId); 
     asyncService.process(someEntity); 
    } 

} 

處理Web服務調用

@Stateless 
public class AsyncService { 

    @Inject 
    private BusinessCompletionService businessCompletionService; 

    @Inject 
    private SomeSoapService soap; 

    /* 
    * Long lived method call with no transaction. 
    * 
    * Asynchronous methods are effectively run as REQUIRES_NEW 
    * unless it is disabled. 
    * This should avoid transaction timeout problems. 
    */ 
    @Asynchronous 
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) 
    public void process(SomeEntity someEntity) { 
     soap.callEndPoint(someEntity.getSomeProperty()); // may take up to 1 minute 
     businessCompletionService.handleBusinessProcessCompletion(someEntity); 
    } 
} 

完成了

@Stateless 
public class BusinessCompletionService { 

    @PersistenceContext 
    private EntityManager em; 

    @Inject 
    @Any 
    private Event<BusinessFinished> businessFinishedEvent; 

    /* 
    * Short lived method call returns promptly. 
    * It defaults to REQUIRED, but will in effect get a new transaction 
    * for this scenario. 
    */ 
    public void handleBusinessProcessCompletion(SomeEntity someEntity) { 
     someEntity.setSomething(SOMETHING); 
     someEntity = em.merge(someEntity); 
     // you may have to deal with optimistic locking exceptions... 
     businessFinishedEvent.fire(new BusinessFinished(someEntity)); 
    } 

} 

我懷疑你可能仍然需要一些連接池調優與您的高峯負荷有效地應對。監測應該明確這一點。