请注意,本站并不支持低于IE8的浏览器,为了获得最佳效果,请下载最新的浏览器,推荐下载 Chrome浏览器
欢迎光临。交流群:166852192

源码阅读Orchard.WarmupStarter程序集


举个形象点的您曾经可能遇见过的例子。对于ASP.NET MVC的项目,我们一般将注册路由的功能放在Application_Start方法中。在第一次请求发生时,执行Application_Start进行程序初始化,如果发生了异常(比如注册了两个名为Dafault的路由),能够捕获并抛出。但第二次及以后的请求将可能导致请求如果找不到合适的路由,将会显示404错误页。如果还想捕捉Application_Start的异常,得重新启动站点。这对于调试来说非常不友好。当然ASP.NET WebForm程序存在着同样的问题。
总结问题:ASP.NET初始化操作出现的异常不能方便的重现,并且不能继续尝试初始化操作,除非重启应用程序。
Orchard.WarmupStarter程序集正是为了解决这个问题,下面对其进行详细的分析。
一、粗糙的解决方案
这里先给出一个最原始的解决方案。请注意并区分所提到的第N次初始化和第N次请求。
第一次请求发生时(请求1)进行程序初始化(初始化1),如果一切正常则皆大欢喜,否则将异常进行记录但不抛出。接着在执行BeginRequest事件处理程序中检查初始化是否有异常,则尝试进行第二次初始化。如果第二次初始化又失败了,则记录第二次的异常,抛出第一次异常。初始化成功了当然就沿着管道继续执行下去。
第二次请求(请求2)发生时,继续检查是否有初始化异常,有则进行第三次初始化。如果第三次初始化又失败了,则记录第三次初始化异常,抛出第二次初始化异常。以此类推。
很明显,我们这个解决方案过于理想。如果初始化是个比较耗时的操作,在第一次初始化时,当前请求线程将会等待初始化完成才能继续,如果还有新来的请求进来,也将处于等待状态,白白消耗着资源——这对于大多数应用来说不是特别大的问题。还有个问题,如果在进行第二次初始化操作时,即BeginRequest事件处理程序中,刚发现第一次初始化有异常,但还没来得及进行第二次初始化时,请求2过来了,它也发现第一次初始化有异常,也会尝试进行初始化。这还是两个请求线程,如果请求更多会更混乱。所以这里涉及初始化操作线程同步的问题——lock住一个是否正在初始化的状态标识也可以解决。
Orchard没有采用这种粗糙的解决方案。如果想看这里给出的解决方案,可以进行如下操作:
1、修改Orchard.Web项目下的web.config文件,将httpmodules节点下名为WarmupHttpModule项注释掉;
2、修改Orchard.WarmupStarter项目下的Starter<T>类,将如下代码复制到LaunchStartupThread方法的最开始处:
            try
            {
                var result = _initialization(application);
                _initializationResult = result;
            }
            catch (Exception e)
            {
                lock (_synLock)
                {
                    _error = e;
                    _previousError = null;
                }
            }
            return;
3、修改Orchard.Web项目下的MvcApplication类,在HostInitialization方法的最开始处抛出一个异常:
throw new Exception();
4、调试启动Orchard.Web项目。
您将会看到,在每一个刷新页面的时候,都将抛出一个异常。
二、Orchard的解决方案
1、在第一次请求发生时,Application_Start方法得以执行,初始化一个Orchard.WarmupStarter.Starter<T>实例,并调用其OnApplicationStart方法。OnApplicationStart方法又调用LaunchStartupThread方法。
2、LaunchStartupThread方法会通过线程池启动一个新的线程进行实际的初始化操作。
3、由于是新开线程进行初始化操作,在这个过程中,可能会有新的请求进入&mdash;&mdash;至少第一次请求会在管道中继续进行。
如果在初始化操作完成之前,任由这些请求进行下去,很可能得不到要想的结果。所以Orchard提供了一个异步HttpMoudle,即Orchard.WarmupStarter.WarmupHttpModule,在初始化正在进行时,将请求的异步BeginRequest处理暂停在那儿,等初始化完成后(不管失败与否),让异步BeginRequest处理完成。在初始化的过程中如果有异常发生,则会将异常记录下来。
4、在异步BeginRequest事件处理完成后,将处理同步BeginRequest事件。事件处理程序将检查上一次初始化请求是否有异常发生;如果检查到有异常发生,则会再次执行LaunchStartupThread方法尝试新的初始化操作;如果新的初始化没有异常发生,就忘记上次初始化出现过异常,否则将本次异常进行记录,抛出上次初始化异常。
注意:在再次执行LaunchStartupThread方法时,如果有新的请求进入,也会将请求的异步BeginRequest处理暂停在那里,直到初始化完成。
5、可以放心,如果上次初始化出现异常,不会导致多个同步BeginRequest事件处理程序尝试都去执行LaunchStartupThread方法,Orchard保证了线程安全。
本文由alby编写

作者原创内容不容易,如果觉得内容不错,请点击右侧“打赏”,赏俩给作者花花,也算是对作者付出的肯定,也可以鼓励作者原创更多更好内容。
更多详情欢迎到QQ群 166852192 交流。