2017-03-22 28 views
3

我在ASP.NET MVC 5網站中使用Apache Ignite .NET EntityFramework二級緩存。在我的開發機器上,一切都按預期工作,但當我嘗試在生產IIS上使用Web Deploy發佈網站時,情況變得複雜。如何在IIS中發生網站重新啓動時檢索啓動的Ignite實例?

當我發佈或重新啓動IIS上的網站時,我總是得到一個class org.apache.ignite.IgniteException: Ignite instance with this name has already been started: GridName。錯誤非常明顯,Ignite已在運行。

我發現擺脫此錯誤的唯一方法是重新啓動網站的應用程序池。但是,我不想每次在生產中發佈網站時都這樣做(每週發生幾次)。

我試着停止global.asax Dispose()或Application_End()中的Ignite實例,但事情是,AppDomain需要幾秒鐘才能停止。 Ignite因此有時間在停止之前嘗試自行啓動,並導致上述錯誤。

我也嘗試呼叫Ignition.TryGetIgnite()來檢索正在運行的實例,而不是試圖啓動它,但它總是返回null。在Apache Ignite Github存儲庫中查看此函數的源代碼,我看到Ignition對象只是在內存中保留一個靜態的節點列表並在該列表上執行操作。由於此時AppDomain重新啓動,該列表爲空,但Ignite節點仍在JVM中運行。

有沒有辦法檢索Ignite實例或可靠地停止它,所以我不需要每次都重新啓動應用程序池?

回答

3

不正是我一直在尋找,但我解決了一時的問題。

我要做的就是在Application_End,我叫Ignition.StopAll()由帕維爾的建議,但由於Application_End有時需要被稱爲一個很長一段時間,加載的網站新的AppDomain有時間啓動並點燃在停止之前接收請求另一個AppDomain。

去解決這個問題,我開始使用下面的代碼(可以改善)點燃:

//Try to get already launched Ignite Instance 
IIgnite ignite = Ignition.TryGetIgnite(igniteName); 

while (ignite == null) 
{ 
    try 
    { 
     //Try to start Ignite 
     ignite = Ignition.Start(igniteConfig); 
    } 
    catch (Exception) //If failing to start Ignite, wait a bit for the previous AppDomain to stop the Ignite running instance... 
    { 
     HttpRequest request = null; 
     try 
     { 
      request = HttpContext.Current.Request; 
     } 
     catch { } 

     //Check if there is a request coming from the same machine, if yes, cancel it. 
     if (request == null || !(request.IsLocal || request.UserHostName.EndsWith("myhostname.com")) 
      Thread.Sleep(1000); 
     else 
      throw new HttpException(500, "Server error. Server rebooting."); 
    } 
} 

//Use ignite variable here... 

注意,在上面的代碼,我把一個其它附加檢查(這可能不是每個人都需要)來檢查是否有來自同一臺機器的當前請求。如果你想知道爲什麼,請閱讀下面的內容,否則只需拿下代碼並刪除最後的if else部分,只需保留Thread.Sleep()即可。

如果請求源自同一臺機器,則會拋出HttpException來取消該請求,否則它將被保留。我這樣做的原因是,如果我在IIS上發佈網站,我不希望消費者看到一個錯誤,但是我不介意他們是否等待以前的AppDomain停止。但是,如果我收到來自同一臺機器(或網站)的請求,我希望該請求中止,因爲它可能來自舊AppDomain的代碼,因此會延遲舊AppDomain的Application_EndIgnition.StopAll()

希望未來的一些Apache Ignite .NET開發可以使實例比此解決方案更簡單,更可靠。

+0

感謝您提供詳細的解決方法!我已經在Ignite jira中提交了一張票:https://issues.apache.org/jira/browse/IGNITE-4856 –

+0

我編輯了我的答案並提供了一個不需要捕獲異常的解決方案。 –

4

這是一個已知問題:AppDomain已停止,但JVM保持運行(因爲進程未停止),所以Ignite節點的Java部分仍然存在。

解決方法是停止與Ignition.StopAll所有的Ignite節點Application_End事件像這樣Global.asax.cs

protected void Application_Start() 
    { 
     ... 

     using (new Mutex(true, "ignite_" + Process.GetCurrentProcess().Id)) 
     { 
      var ignite = Ignition.TryGetIgnite() ?? Ignition.Start(); 
     } 
    } 

    protected void Application_End() 
    { 
     using (new Mutex(true, "ignite_" + Process.GetCurrentProcess().Id)) 
     { 
      Ignition.StopAll(true); 
     } 
    } 

互斥是必要的,因爲這裏舊域名停止與新域起點重疊。 進程ID包含在互斥鎖名稱中,以確保進程範圍的獨佔鎖定,但不是機器範圍的。


另一個解決方法,可能更強大和清潔:

1)停止所有節點AppDomain.DomainUnload:

AppDomain.CurrentDomain.DomainUnload += (sender, args) => Ignition.StopAll(true); 

2)使用不同的IgniteConfiguration.GridName每次:

Ignition.Start(new IgniteConfiguration { GridName = Guid.NewGuid().ToString() }); 

這種方式存在g節點不會阻止您啓動一個新節點。最終,舊節點將停止並從內存中清除。

文檔:https://apacheignite-net.readme.io/docs/deployment#section-aspnet-deployment

+0

不幸的是,如果在之前的AppDomain Application_End之前觸發了新的AppDomain'Application_Start',就像我的情況一樣,將會發生同樣的問題。新的AppDomain仍會嘗試在現有的Ignite實例停止之前啓動Ignite實例。但是,這確保了在啓動和停止Ignite實例時,不同AppDomain的線程之間不會發生併發問題。我應該更新我的答案,以包含此「全過程」互斥體。 – alexbchr

+0

有趣的是,在我的測試中,沒有任何新應用程序在舊應用程序開始關閉之前啓動的情況。我想這取決於IIS設置。 –

+0

我編輯了我的答案並添加了一個不同的解決方法。你可以請嘗試一下,讓我知道它是否有幫助? –

相關問題