ThreadStatic
ThreadStatic是C#里的一个特性,它可以让你的字段在一个线程里有效,但你不能控制这个字段在何时被回收,即如果声明一个int32的字段为ThreadStatic,然后你为它赋值时为100,那么它什么被恢复成默认值0,我们不得而知,这在开发时,我们可能只有手动将它设为0才行,比较难看,但也没办法,谁让咱们用了ThreadStatic呢,被声明为ThreadStatic之后,已经证明这个字段是静态化的,只不过它是被局限在一个线程内的。
Quartz
Quartz是一个任务调度框架,起源于java,它目前被广泛的使用在各种后台处理数据的场合,像一些统计数据,推送数据,消息数据等,它可以大大降低前端服务器的并发压力,并且Quartz的管理界面也有很多,直接nuget安装即可,在这些产品中最知名的应该就是CrystalQuartz了,它可以在WEB界面中管理咱们的JOB项目!
日志系统Lind.DDD.Logger
Logger本来是Lind框架的一个日志组件,它是最低层的组件,是其它组件的基础,也被用到其它的业务系统里,而其中一个Quartz组件里,使用Logger时提出了一个问题,就是如何根据job去自动建立日志目录,让每个JOB都有自己的目录,这样在分析日志时还是很有必要的。
希望看到的结果如图
aaarticlea/png;base64,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" alt="" />
测试用的两个Job
public class Hello_Job : JobBase
{ protected override void ExcuteJob()
{ Console.WriteLine("Hello Job方法:" + Thread.CurrentThread.ManagedThreadId);
Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info("Hello Job日志!");
}
} public class Hi_Job : Lind.DDD.QuartzJob.JobBase
{ protected override void ExcuteJob()
{ Console.WriteLine("Hi Job!" + Thread.CurrentThread.ManagedThreadId);
Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info("Hi Job!"); }
}
JobBase做于所有Job的基类存在,它主要有自己的抽象方法和IJob的接口方法,其中抽象方法由字类Job自己去实现,去实现自己的业务逻辑;而IJob方法由Quartz框架去调用,并在方法中自己调用了抽象方法的内容,大致代码如下
[DisallowConcurrentExecution()]
public abstract class JobBase : IJob
{ #region IJob 成员
/// <summary>
/// Job主方法
/// </summary>
/// <param name="context"></param>
public void Execute(IJobExecutionContext context)
{
Lind.DDD.Logger.LoggerFactory.Instance.SetPath(this.GetType().Name);
ExcuteJob();
Console.WriteLine(DateTime.Now.ToString() + "{0}这个Job开始执行", context.JobDetail.Key.Name);
} #endregion /// <summary>
/// Job具体类去实现自己的逻辑
/// </summary>
protected abstract void ExcuteJob();
}
日志组件中的字段使用了ThreadStatic
对日志文件分文件夹存储,主要在日志组件中使用ThreadStatic来实现的,代码主要如下
/// <summary>
/// 每个子类初始时都执行基类这个构造,初始化当前路径
/// </summary>
public LoggerBase()
{
FileUrl = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LoggerDir");
}
/// <summary>
/// 日志文件地址
/// 优化级为mvc方案地址,网站方案地址,console程序地址
/// </summary>
[ThreadStatic]
static protected string FileUrl;
#region ILogger 成员
public void SetPath(string path)
{
if (!string.IsNullOrWhiteSpace(path))
{
FileUrl = FileUrl + "\\" + path;
}
}
#endregion
对于FileLogger这个文件日志实现类来说,它要做的是,在写完文件流之后,要把FileUrl这个字段从新赋值,因为我们不知道这个字符串什么时候被清空!
lock (objLock)//防治多线程读写冲突
{
using (System.IO.StreamWriter srFile = new System.IO.StreamWriter(filePath, true))
{
srFile.WriteLine(string.Format("{0}{1}{2}"
, DateTime.Now.ToString().PadRight()
, ("[ThreadID:" + Thread.CurrentThread.ManagedThreadId.ToString() + "]").PadRight()
, message));
srFile.Close();
srFile.Dispose();
}
}
//清除当前的路径
FileUrl = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LoggerDir");
上面的问题,我也是找了很久,因为总是找不到测试不成功的原因,最后想到了ThreadStatic特性的声明周期,算是找到根源了,呵呵!
建议大家看看C#的《对象的生与死》!