[英]How do I retrieve a started Ignite instance when a website restart occurs in IIS?
I use Apache Ignite .NET EntityFramework second level caching in my ASP.NET MVC 5 website. 我在ASP.NET MVC 5网站中使用Apache Ignite .NET EntityFramework二级缓存。 Everything work as expected on my development machine, but things get complicated when I try to publish the website using Web Deploy on the production IIS.
一切都在我的开发机器上按预期工作,但当我尝试在生产IIS上使用Web Deploy发布网站时,事情变得复杂。
I always get a class org.apache.ignite.IgniteException: Ignite instance with this name has already been started: GridName
when I publish or restart the website on IIS. 我总是得到一个
class org.apache.ignite.IgniteException: Ignite instance with this name has already been started: GridName
当我在IIS上发布或重新启动网站时, class org.apache.ignite.IgniteException: Ignite instance with this name has already been started: GridName
。 The error is pretty clear, Ignite is already running. 错误非常明显,Ignite已经在运行。
The only way I found to get away this error is restarting the application pool of the website. 我发现逃避此错误的唯一方法是重新启动网站的应用程序池。 However, I don't want to do this every time I publish the website in production (which happens several times a week).
但是,每次我在生产中发布网站(每周发生几次)时,我都不想这样做。
I tried stopping the Ignite instance in the global.asax Dispose() or Application_End(), but the thing is that the AppDomain takes many seconds to stop. 我尝试在global.asax Dispose()或Application_End()中停止Ignite实例,但事实是AppDomain需要很多秒才能停止。 Ignite therefore has the time to try starting itself before it is stopped and causes the error mentioned above.
因此,Ignite有时间在停止之前尝试自行启动并导致上述错误。
I also tried to call Ignition.TryGetIgnite()
to retrieve the running instance instead of trying to start it, but it always return null. 我还尝试调用
Ignition.TryGetIgnite()
来检索正在运行的实例,而不是尝试启动它,但它总是返回null。 Looking at the source code of this function in the Apache Ignite Github repository, I see that the Ignition object simply keeps a static list of nodes in memory and perform operations on this list. 在Apache Ignite Github存储库中查看此函数的源代码,我看到Ignition对象只是在内存中保留一个静态节点列表并在此列表上执行操作。 Since the AppDomain restarted at this point, the list is empty but the Ignite node is still running in the JVM.
由于此时AppDomain重新启动,因此列表为空,但Ignite节点仍在JVM中运行。
Is there a way to retrieve the Ignite instance or to stop it reliably, so I don't need to restart the application pool each time? 有没有办法检索Ignite实例或可靠地停止它,所以我不需要每次都重启应用程序池?
This is a known problem: AppDomain is stopped, but JVM keeps running (because process is not stopped), so Java part of Ignite nodes is still there. 这是一个已知问题:AppDomain已停止,但JVM仍在运行(因为进程未停止),因此Ignite节点的Java部分仍然存在。
Workaround is to stop all Ignite nodes with Ignition.StopAll
in Application_End
event like this in Global.asax.cs
: 解决方法是在
Global.asax.cs
中的Application_End
事件中使用Ignition.StopAll
停止所有Ignite节点:
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);
}
}
Mutex is necessary here because old domain stopping overlaps with new domain starting. 此处需要互斥锁,因为旧域停止与新域启动重叠。 Process id is included in mutex name to ensure process-wide exclusive lock, but not machine-wide.
进程ID包含在互斥锁名称中,以确保进程范围的独占锁定,但不是机器范围的。
Another workaround , probably more robust and clean: 另一个解决方法 ,可能更强大和干净:
1) Stop all nodes in AppDomain.DomainUnload: 1)停止AppDomain.DomainUnload中的所有节点:
AppDomain.CurrentDomain.DomainUnload += (sender, args) => Ignition.StopAll(true);
2) Use different IgniteConfiguration.GridName
every time: 2)每次使用不同的
IgniteConfiguration.GridName
:
Ignition.Start(new IgniteConfiguration { GridName = Guid.NewGuid().ToString() });
This way existing nodes won't prevent you from starting a new one. 这样,现有节点不会阻止您启动新节点。 Eventually, old nodes will be stopped and cleaned up from memory.
最终,旧节点将被停止并从内存中清除。
Documentation: https://apacheignite-net.readme.io/docs/deployment#section-aspnet-deployment 文档: https : //apacheignite-net.readme.io/docs/deployment#section-aspnet-deployment
Not exactly what I was looking for but I solved the problem for the moment. 不完全是我想要的,但我暂时解决了这个问题。
What I do is in Application_End
, I call Ignition.StopAll()
as suggested by Pavel, but since Application_End
sometimes takes a long time to be called, the new AppDomain loaded for the website has time to start and receive requests before Ignite is stopped in the other AppDomain. 我所做的是在
Application_End
,我按照Pavel的建议调用Ignition.StopAll()
,但由于Application_End
有时需要很长时间才能被调用,因此为网站加载的新AppDomain有时间启动并在Ignite停止之前接收请求另一个AppDomain。
To go around this, I start Ignite using the following code (which could be improved): 为了解决这个问题,我使用以下代码启动Ignite(可以改进):
//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...
Note that in the code above, I put an additionnal check (which may not be needed by everyone), to check if there is a current request originating from the same machine. 请注意,在上面的代码中,我进行了额外的检查(每个人可能不需要),以检查是否有来自同一台机器的当前请求。 If you want to know why, read below, otherwise just take the code and remove the last
if else
part and simply keep the Thread.Sleep()
. 如果你想知道原因,请阅读下面的内容,否则只需获取代码并删除最后一个
if else
部分,然后保留Thread.Sleep()
。
If request originates from the same machine, an HttpException is thrown to cancel the request, otherwise, it is conserved. 如果请求来自同一台机器,则抛出HttpException以取消请求,否则,它将被保留。 The reason I put this code is that if I publish the website on IIS, I don't want consumers to see an error, but I don't mind if they wait a bit for the previous AppDomain to stop.
我放这个代码的原因是,如果我在IIS上发布网站,我不希望消费者看到错误,但我不介意他们等待前一个AppDomain停止。 But if I receive a request from the same machine (or website), I want the request to abort, since it probably originates from code of the old AppDomain and will therefore delay the
Application_End
and Ignition.StopAll()
from the old AppDomain. 但是,如果我收到来自同一台机器(或网站)的请求,我希望请求中止,因为它可能源自旧AppDomain的代码,因此会延迟旧AppDomain中的
Application_End
和Ignition.StopAll()
。
Hopefully some future Apache Ignite .NET development could make the instance closing easier and more reliable than this workaround. 希望未来的一些Apache Ignite .NET开发可以使实例关闭比这种解决方案更容易,更可靠。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.