• Orchard源码:热启动


    概述

    IIS线程池中的线程数量是有限制的。当有多个长时间请求时,可能会耗尽IIS可用线程。出现503错误。在MVC中。当遇到非CPU操作的长时间请求时,MVC提供了异步方法来解决这个问题。

    例:利用async和await实现异步方法

    // GET: Async
    
    [AsyncTimeout(1000)]
    
    public async Task<ActionResult> Index()
    
    {
    
    var data = await GetPageTaskAsync("http://www.baidu.com");
    
    return data;
    
    }
    

    回到Orchard,在Orchard启动时,需要一定时间加载模块插件,这时候如果出现大量请求,则有可能出现上面提到的错误。来看一下Orchard中是如何解决这个问题。

    项目结构Orchard.WarmupStarter ,是一个单独的项目。方便复用

    clip_image001

    具体实现

    一句话概括实现步骤: 启动时初始化一个异步请求列表,初始化期间有请求到来时,把该请求添加到请求列表中。当系统初始化完成时回调起步请求列表并且清空。

    Starter.cs 

    /// <summary>
    
    /// Run the initialization delegate asynchronously in a queued work item
    
    /// </summary>
    
    public void LaunchStartupThread(HttpApplication application) {
    
    // Make sure incoming requests are queued
    
    WarmupHttpModule.SignalWarmupStart();
    
    ThreadPool.QueueUserWorkItem(
    
    state => {
    
    try {
    
    var result = _initialization(application);
    
    _initializationResult = result;
    
    }
    
    catch (Exception e) {
    
    lock (_synLock) {
    
    _error = e;
    
    _previousError = null;
    
    }
    
    }
    
    finally {
    
    // Execute pending requests as the initialization is over
    
    WarmupHttpModule.SignalWarmupDone();
    
    }
    
    });
    
    }
    
    WarmupHttpModule.SignalWarmupStart(); 系统开始加载,初始化请求列表
    
    public static void SignalWarmupStart() {
    
    lock (_synLock) {
    
    if (_awaiting == null) {
    
    _awaiting = new List<Action>();
    
    }
    
    }
    
    }
    
    WarmupHttpModule.SignalWarmupDone(); 系统加载完成,回调请求列表并且清空
    
    public static void SignalWarmupDone() {
    
    IList<Action> temp;
    
    lock (_synLock) {
    
    temp = _awaiting;
    
    _awaiting = null;
    
    }
    
    if (temp != null) {
    
    foreach (var action in temp) {
    
    action();
    
    }
    
    }
    
    }
    
    

    WebConfig中注册WarmupHttpModule

    请求到来时,执行WarmupHttpModule.BeginBeginRequest回调, 如果加载中,则请求添加到异步列表,否则继续执行回调

    private IAsyncResult BeginBeginRequest(object sender, EventArgs e, AsyncCallback cb, object extradata) {
    
    // host is available, process every requests, or file is processed
    
    if (!InWarmup() || WarmupUtility.DoBeginRequest(_context)) {
    
    var asyncResult = new DoneAsyncResult(extradata);
    
    cb(asyncResult);
    
    return asyncResult;
    
    }
    
    else {
    
    // this is the "on hold" execution path
    
    var asyncResult = new WarmupAsyncResult(cb, extradata);
    
    Await(asyncResult.Completed);
    
    return asyncResult;
    
    }
    
    }

    异步编程模型 DoneAsyncResult 和 WarmupAsyncResult 实现IAsyncResult

    WarmupAsyncResult

    /// <summary>
    
    /// AsyncResult for "on hold" request (resumes when "Completed()" is called)
    
    /// </summary>
    
    private class WarmupAsyncResult : IAsyncResult {
    
    private readonly EventWaitHandle _eventWaitHandle = new AutoResetEvent(false/*initialState*/);
    
    private readonly AsyncCallback _cb;
    
    private readonly object _asyncState;
    
    private bool _isCompleted;
    
    public WarmupAsyncResult(AsyncCallback cb, object asyncState) {
    
    _cb = cb;
    
    _asyncState = asyncState;
    
    _isCompleted = false;
    
    }
    
    public void Completed() {
    
    _isCompleted = true;
    
    _eventWaitHandle.Set();
    
    _cb(this);
    
    }
    
    bool IAsyncResult.CompletedSynchronously {
    
    get { return false; }
    
    }
    
    bool IAsyncResult.IsCompleted {
    
    get { return _isCompleted; }
    
    }
    
    object IAsyncResult.AsyncState {
    
    get { return _asyncState; }
    
    }
    
    WaitHandle IAsyncResult.AsyncWaitHandle {
    
    get { return _eventWaitHandle; }
    
    }
    
    }
    
    

    DoneAsyncResult(不阻塞)

    /// <summary>
    
    /// Async result for "ok to process now" requests
    
    /// </summary>
    
    private class DoneAsyncResult : IAsyncResult {
    
    private readonly object _asyncState;
    
    private static readonly WaitHandle _waitHandle = new ManualResetEvent(true/*initialState*/);
    
    public DoneAsyncResult(object asyncState) {
    
    _asyncState = asyncState;
    
    }
    
    bool IAsyncResult.CompletedSynchronously {
    
    get { return true; }
    
    }
    
    bool IAsyncResult.IsCompleted {
    
    get { return true; }
    
    }
    
    WaitHandle IAsyncResult.AsyncWaitHandle {
    
    get { return _waitHandle; }
    
    }
    
    object IAsyncResult.AsyncState {
    
    get { return _asyncState; }
    
    }
    
    }
    
    

    总结

    Orchard.WarmupStarter 已封装好相关热启动代码, 实际项目中如果初始化时间比较长,稍改造Orchard.WarmupStarter就可复用到自己的项目中。

    参考

    http://www.cnblogs.com/alby/archive/2012/10/18/orchard-WarmupStarter.html

  • 相关阅读:
    React.js学习笔记之事件系统
    彻底解决Webpack打包慢的问题:npm run build:dll
    gulp详细入门教程
    cmd、node、npm 常用命令
    ant design中ES6写法个人总结
    自定义浏览器滚动条的样式,打造属于你的滚动条风格
    js相关知识
    day31-python阶段性复习五
    day30-python阶段性复习四
    day29-python阶段性复习三
  • 原文地址:https://www.cnblogs.com/miku/p/4282487.html
Copyright © 2020-2023  润新知