测试项目搭建

定义一个简单的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数据就不合适了,因为保存与读取 的执行顺序无法保障。

关于Application_End 与 Application_Start事件触发情况的测试(待续)的更多相关文章

  1. 【深入浅出Linux网络编程】 “基础 -- 事件触发机制”

    回顾一下“"开篇 -- 知其然,知其所以然"”中的两段代码,第一段虽然只使用1个线程但却也只能处理一个socket,第二段虽然能处理成百上千个socket但却需要创建同等数量的线程 ...

  2. [转载] 理解 epoll 的事件触发机制

    原文: http://weibo.com/p/1001603862394207076573?sudaref=weibo.com epoll的I/O事件触发方式有两种模式:ET(Edge Trigger ...

  3. Zendframework 模块加载事件触发顺序。

    模块加载时事件触发的时间顺序: 0.loadModules(ModuleEvent::EVENT_LOAD_MODULES) 1.  loadModule.resolve(ModuleEvent::E ...

  4. Android中Touch事件分析--解决HorizontalScrollView滑动和按钮事件触发问题

    之前写过关于HorizontalScrollView滑动和按钮事件触发问题,但是不能所有的情况,最近几天一直在想这个问题,今天有一个比较好的解决思路,最终应用在项目里面效果也很好,首先说明一下功能: ...

  5. 基于raw os 的事件触发系统

    Raw os的事件触发系统有以下特点: 1 基于UML的状态机理念设计,实现了有限状态机(fsm)以及层次状态机(HSM). 2 实现了活动对象(ACTIVE OBJECT)的特性,一个活动对象包含了 ...

  6. jQuery事件触发和参数传递

    jQuery事件触发和参数传递: 参考:http://www.jb51.net/article/36249.htm <%@ page language="java" impo ...

  7. Javascript事件触发顺序

    html标签是有子和父的,这个时候就出现了事件触发顺序的问题,比如: <!DOCTYPE html> <html> <head> <style> .fi ...

  8. 【阿里云IoT+YF3300】4.Alink物模型之事件触发

    名词解释:设备的功能模型之一,设备运行时的事件,事件一般包含需要被外部感知和处理的通知信息,可包含多个输出参数.如,某项任务完成的信息,或者设备发生故障或告警时的温度等,事件可以被订阅和推送. 在工控 ...

  9. epoll水平/边缘触发模式下阻塞/非阻塞EPOLLOUT事件触发条件及次数

    在IO多路复用技术中,epoll默认的事件触发模式为Level_triggered(水平触发)模式,即当被监控的文件描述符上有可读/写事件发生时,epoll_wait()会通知处理程序去读写.如果这次 ...

随机推荐

  1. linux环境 安装chromedriver 和 phantomjs的方法

    1 首先要下载浏览器驱动: 常用的是chromedriver 和phantomjs chromedirver下载地址: https://npm.taobao.org/mirrors/chromedri ...

  2. python 字符串 字节

    字符串 字节   a. 字符串转字节 1 2 key = "xxxx" bkey = bytes(key,encoding='utf-8') b. bytearray 数组 1 2 ...

  3. Java-NIO(九):管道 (Pipe)

    Java NIO 管道是2个线程之间的单向数据连接.Pipe有一个source通道和一个sink通道.数据会被写到sink通道,从source通道读取. 代码使用示例: @Test public vo ...

  4. Java-NIO(八):DatagramChannel

    Java NIO中的DatagramChannel是一个能收发UDP包的通道.操作步骤: 1)打开 DatagramChannel 2)接收/发送数据 同样它也支持NIO的非阻塞模式操作,例如: @T ...

  5. www的构建技术

    www的构建技术分别是: html超文本标记语言,页面的文本显示 http超文本传输协议,信息传输转移的约定 url统一资源定位符,客户端浏览超文本的地址集合

  6. 闲聊 Exp/Imp 命令使用

    1.Exp/Imp 命令可用的参数: 2.userid 参数必须跟在 Exp/Imp 命令的后面,可以省略,如 Exp Userid=用户名/密码@数据库实例名 或者 Exp 用户名/密码@数据库实例 ...

  7. 使Asp.net Core同时支持输出Json/Xml

    我们知道Asp.net Core是支持输出为Json格式的.同时也支持输出为xml格式.只要我们正确的配置.并在Request时指定正确的Accept,即可根据不同的Header来输出不同的格式. 前 ...

  8. Python系列之 - python运算符

    废话不多说,上节说的是数据类型,本篇讲讲数据运算. 在算式"1+2"中,"1"和"2"被称为操作数,"+"被称为运算符 ...

  9. C++11 作用域内枚举

    enum class MyEnum{ P1 = , P2, P3, P4, P5 }; MyEnum myEnum = MyEnum::P2; 使用作用域的方式获取并限定P2的值.之所以要使用作用域, ...

  10. codevs 搜索题汇总(钻石+大师级)

    1043 方格取数 2000年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题目描述 Description 设有N*N的方格图 ...