关于Application_End 与 Application_Start事件触发状况的测试

测试项目搭建

定义一个简单的Mvc项目,有以下文件:html

(1)apache

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)c#

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)session

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的日志记录。并发

高并发

使用工具模拟请求高并发地访问这个站点app

在此期间修改config, 日志文件中只有Application_End的记录,却没有Application_Start的记录。
再次修改config,此次只有Application_Start的记录,却又没有end的了。尝试屡次,一直都这样轮替出现。

虽然上面日志中的记录看上去很日常,可是实际状况却不是这样。socket


因而作了以下修改(每次启动都使用新的日志文件)

一样进行了几回config文件的修改,新的日志记录以下
async

能够看到Application_Start事件并不会等待Application_End执行完毕才触发。ide

待解决的疑问:

  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数据就不合适了,由于保存与读取 的执行顺序没法保障。

相关文章
相关标签/搜索