關於Application_End 與 Application_Start事件觸發情況的測試


測試項目搭建

定義一個簡單的Mvc項目,有如下文件:

(1)

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Run(context =>
        {
            return Task.Run(
                async () => {
                    await Task.Delay(600);
                    await context.Response.WriteAsync("Hello, world." + context.Request.Uri);
                });
        });
    }
}

(2)

public class Global : System.Web.HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        DebugUtils.Log("Application_Start");

        Task.Run(() => { DebugUtils.beginLog($"bugLog.txt"); });

        for (long i = 0; i < 10000000000; i++) ;

        DebugUtils.Log("Application_Start finished");
    }


    protected void Application_End(object sender, EventArgs e)
    {
        DebugUtils.Log("Application_End");

        for (long i = 0; i < 10000000000; i++) ;

        DebugUtils.Log("Application_End finished");
    }
}

(3)

public class DebugUtils
{

    public static ConcurrentQueue<string> queue = new ConcurrentQueue<string>();

    public static void beginLog(string fileName)
    {
        string dirPath = "G:\\c#\\www\\debugLog\\";
        if (!Directory.Exists(dirPath))
            Directory.CreateDirectory(dirPath);

        using (FileStream fs = new FileStream(dirPath + "/" + fileName, FileMode.Append))
        {
            using (StreamWriter sw = new StreamWriter(fs))
            {
                string str;
                while (true)
                {
                    if (queue.TryDequeue(out str))
                    {
                        sw.WriteLine(str);
                        sw.Flush();
                    }
                }

            }
        }
    }

    public static void Log(string str)
    {
        queue.Enqueue("[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff") + "]--[" + Thread.CurrentThread.ManagedThreadId + "]--" + str);
    }
}

測試

常規操作

將站點部署到IIS上后,依次進行 修改config, bin文件夾,站點關閉、重啟,回收線程池 操作,日志里都有Application_End的觸發日志。隨后訪問頁面又會有Application_Start的日志記錄。

高並發

使用工具模擬請求高並發地訪問這個站點

在此期間修改config, 日志文件中只有Application_End的記錄,卻沒有Application_Start的記錄。
再次修改config,這次只有Application_Start的記錄,卻又沒有end的了。嘗試多次,一直都這樣輪替出現。

雖然上面日志中的記錄看上去很平常,但是實際情況卻不是這樣。


於是做了如下修改(每次啟動都使用新的日志文件)

同樣進行了幾次config文件的修改,新的日志記錄如下

可以看到Application_Start事件並不會等待Application_End執行完畢才觸發。

待解決的疑問:

  1. 上面測試中情況
    前面使用同一個日志文件時,日志只記錄部分,甚至很規律地交替出現,是因為文件被占用嗎?

在打開日志文件時刻意拋出異常,網站可以照常訪問,但不再記錄日志了,所以文件占用異常確實可以導致上述情況。

    public  void beginLog(string fileName)
    {
        string dirPath = "G:\\c#\\www\\debugLog\\";
        if (!Directory.Exists(dirPath))
            Directory.CreateDirectory(dirPath);

        throw new Exception("test");

        using (FileStream fs = new FileStream(dirPath + "/" + fileName, FileMode.Append))
        {
            using (StreamWriter sw = new StreamWriter(fs))
            {
                string str;
                while (true)
                {
                    if (queue.TryDequeue(out str))
                    {
                        sw.WriteLine(str);
                        sw.Flush();
                    }
                }

            }
        }
    }

修改日志記錄類為非靜態類

(1)

public class DebugUtils
{
    public static DebugUtils Instance = new DebugUtils();

    public static ConcurrentQueue<string> queue = new ConcurrentQueue<string>();

    public  void beginLog(string fileName)
    {
        string dirPath = "G:\\c#\\www\\debugLog\\";
        if (!Directory.Exists(dirPath))
            Directory.CreateDirectory(dirPath);

        using (FileStream fs = new FileStream(dirPath + "/" + fileName, FileMode.Append))
        {
            using (StreamWriter sw = new StreamWriter(fs))
            {
                string str;
                while (true)
                {
                    if (queue.TryDequeue(out str))
                    {
                        sw.WriteLine(str);
                        sw.Flush();
                    }
                }

            }
        }
    }

    public  void Log(string str, string date)
    {
        queue.Enqueue($"[{ date }]--[{Thread.CurrentThread.ManagedThreadId}]-[{ this.GetHashCode() }]-{str}");
    }
}

(2)

public class Global : System.Web.HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        DebugUtils.Instance.Log("Application_Start", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));

        Task.Run(() => { DebugUtils.Instance.beginLog($"bugLog{DateTime.Now.ToString("yyyyMMdd-HHmmssfff")}.txt"); });

        for (long i = 0; i < 10000000000; i++) ;

        DebugUtils.Instance.Log("Application_Start finished", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
    }


    protected void Application_End(object sender, EventArgs e)
    {
        DebugUtils.Instance.Log("Application_End", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));

        for (long i = 0; i < 10000000000; i++) ;

        DebugUtils.Instance.Log("Application_End finished", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
    }
}

快速地修改了3次config(空格,保存,空格,保存,空格,保存), 發現日志文件的創建要滯后許多。

重試了一遍,這次也是3次修改,只觸發了兩次Application_End



在連續修改config的情況下,start事件幾乎是立即就觸發了,但end事件如果尚有前一個end事件未執行完成,新的end事件會延后觸發。

在end事件中增加代碼記錄end的觸發原因

        try
        {
            HttpRuntime runtime = (HttpRuntime)typeof(HttpRuntime).InvokeMember("_theRuntime",
                                     BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField, null, null, null);
            if (runtime == null)
                return;
            string shutDownMessage = (string)runtime.GetType().InvokeMember("_shutDownMessage",
                        BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null);
            string shutDownStack = (string)runtime.GetType().InvokeMember("_shutDownStack",
                       BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null);

            DebugUtils.Instance.Log("MvcApplicationEnd事件觸發:" + shutDownMessage + shutDownStack, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
        }
        catch (Exception ex)
        {
            DebugUtils.Instance.Log("MvcApplicationEnd事件觸發異常:" + ex.Message, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
        }



此時的網站日志文件中:

兩個明顯的請求處理停頓,剛好分別對應了上面的兩次start事件

項目中遇到的問題

項目中使用log4net記錄日志 ,配置文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender" >
	<file value="App_Data/Logs/Logs.txt" />
	<encoding value="utf-8" />
	<appendToFile value="true" />
	<rollingStyle value="Size" />
	<maxSizeRollBackups value="10" />
	<maximumFileSize value="10000KB" />
	<staticLogFileName value="true" />
	<layout type="log4net.Layout.PatternLayout">
	<conversionPattern value="%-5level %date [%-5.5thread] %-40.40logger - %message%newline" />
	</layout>
</appender>
<root>
	<appender-ref ref="RollingFileAppender" />
	<level value="DEBUG" />
</root>
</log4net>

Application_Start:

    protected override void Application_Start(object sender, EventArgs e)
    {
        Clock.Provider = ClockProviders.Utc;

        //Log4Net configuration
        AbpBootstrapper.IocManager.IocContainer
            .AddFacility<LoggingFacility>(f => f.UseAbpLog4Net()
                .WithConfig(Server.MapPath("log4net.config"))
            );


        base.Application_Start(sender, e);

        SingletonDependency<IOnlineUserManager>.Instance.ReloadWhenApplicationStart();
    }

在站點發布后,常常會在某次Application_End事件觸發后,日志不再記錄,而等過段時間后又會有新的日志出現(Application_Start的日志為起始)。

確實也是因為文件被占用。

解決log4net獨占日志文件的問題以及 log4net的各種輸出配置

http://logging.apache.org/log4net/release/config-examples.html

log4net系列(三)--詳解RollingFileAppender

log4net的配置詳解

Application_Start 中的 SingletonDependency<IOnlineUserManager>.Instance.ReloadWhenApplicationStart();這行代碼為什么經常不生效。

因為有socket連接及輪詢請求的存在,常常是start事件在end之前被觸發,所以在end事件觸發時保存用戶session數據就不合適了,因為保存與讀取 的執行順序無法保障。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM