【原创】有关Buffer使用,让你的日志类库解决IO高并发写
【本人原创】,欢迎交流和分享技术,转载请附上如下内容:
如果你觉得这篇文章对你有帮助,请记得帮我点赞, 谢谢!
作者:itshare 【转自】http://www.cnblogs.com/itshare/
通常我们知道,当一个日志接口被外部程序多个线程请求后,如果没有使用Buffer和异步写入IO的处理。
CPU就会一直处于加锁和解锁的频繁切换,这样加上等待每次线程锁中的IO处理完毕的时间,
高并发调用日志接口的整个过程中响应和处理速度就会严重变慢,并且让CPU一直居高不下。
这里,Coding First,先看对比结果,最后我再附上LogBufferPool的代码实现。
下面我们分别做两组实验:时间单位为秒
100个线程,并发消费100000个请求。看下面每组实验的结果。
实验1:普通的日志接口,无Buffer缓冲池和异步写入IO的处理。



实验2:改造后日志接口,有Buffer缓冲池和异步写入IO的处理。



最后,在这里贴上我的代码实现:LogBufferPool
namespace ITS.Base.Comm.Logger_FastIO
{
/// <summary>
/// 日志缓冲池
/// </summary>
public class LogBufferPool :IDisposable
{
/// <summary>
/// 工作日期
/// </summary>
private DateTime worktime
{
get;
set;
} /// <summary>
/// 上一次写入时间
/// </summary>
private DateTime LastWriteTime
{
get;
set;
} /// <summary>
/// 日志的文件路径
/// </summary>
private string fpath
{
get;
set;
} /// <summary>
/// 最大行数 - write buffer
/// </summary>
public long MaxRows
{
get;
private set;
} /// <summary>
/// 最大字数 - write buffer
/// </summary>
public long MaxSize
{
get;
private set;
} /// <summary>
/// 当前字数 - write buffer
/// </summary>
public long CurrnetSize
{
get;
private set;
}
/// <summary>
/// 当前行数 - write buffer
/// </summary>
public long CurrnetRows
{
get;
private set;
} /// <summary>
/// Lock of Buffer write
/// </summary>
private static object Lock_Write_IO = ;
/// <summary>
/// Flag of last write Buffter
/// </summary>
private static bool IsLastWriteCompleted = true;
/// <summary>
/// Time of Buffer write
/// </summary>
static System.Threading.Timer timer_work = null; /// <summary>
/// 文件流
/// </summary>
private Dictionary<int, FileStream> Dic_Stream = null; /// <summary>
/// 日志消息
/// </summary>
private Dictionary<int, List<string>> Dic_MesgData = null; /// <summary>
/// 构造函数 - 日志缓冲池
/// </summary>
/// <param name="fpath">日志文件路径:logType</param>
/// <param name="max_size">最大字数 - write buffer</param>
/// <param name="max_rows">最大行数 - write buffer</param>
public LogBufferPool(string fpath, long max_size = , long max_rows = )
{
this.worktime = DateTime.Now;
this.LastWriteTime = DateTime.Now;
this.fpath = fpath;
this.MaxSize = max_size;
this.MaxRows = max_rows;
this.Dic_Stream = new Dictionary<int, FileStream>();
this.Dic_MesgData = new Dictionary<int, List<string>>(); IsLastWriteCompleted = true;
if (timer_work == null)
{
// 1*1000: 1秒后启动计时器
// 执行计划:每隔开秒执行一次
timer_work = new System.Threading.Timer(new System.Threading.TimerCallback(WriteBufferToFile), this, * , * );
}
} /// <summary>
/// 写完日志后,再释放资源
/// </summary>
public void Dispose()
{
try
{
Dictionary<int, List<string>> dic = this.Dic_MesgData;
if (dic != null && dic.Count > )
{
foreach (KeyValuePair<int, List<string>> pair in this.Dic_MesgData)
{
WriteFile((LogTypeEnum)pair.Key, pair.Value);
}
}
}
catch (Exception ex)
{
// log
}
finally
{
GC.Collect();
}
} /// <summary>
/// 添加日志消息
/// </summary>
/// <param name="logType">日志类别</param>
/// <param name="msg">日志内容</param>
/// <returns></returns>
public bool AddLog(LogTypeEnum logType, string msg)
{
bool flag = false;
List<string> list = null;
int n = ; // 写入IO的buffer文件个数
List<int> keys = null; // 写入IO的buffer文件个数 //long size_text = 0, row_text = 0;
long index_row = , count_row = list != null ? list.Count : ; try
{
if (!this.Dic_MesgData.TryGetValue((int)logType, out list))
{
list = new List<string>();
this.Dic_MesgData.Add((int)logType, list); //FileStream fs = File.Open(string.Format(fpath, logType), FileMode.Append, FileAccess.Write);
//// fs.WriteTimeout = 24 * 60 * 60;
//this.Dic_Stream.Add((int)logType, fs);
} // 添加日志消息
list.Add(msg);
index_row++; this.CurrnetSize += msg.Length;
this.CurrnetRows++; // 根据缓冲池大小,定期清理和写入IO文件
//if ((this.CurrnetSize >= this.MaxSize
// || this.CurrnetRows >= this.MaxRows)
// || (DateTime.Now - this.LastWriteTime).TotalSeconds > 10)
//{
// this.CurrnetSize = 0;
// this.CurrnetRows = 0; // keys = Dic_MesgData.Keys.ToList();
// foreach (int key in keys)
// {
// List<string> list_old = this.Dic_MesgData[key];
// this.Dic_MesgData[key] = new List<string>(); // bool flag_write = this.WriteFile((LogTypeEnum)key, list_old);
// if (flag_write)
// {
// n++;
// }
// }
//} //WriteBufferToFile(null); flag = true; // flag = keys != null && keys.Count > 0 ? n == keys.Count : true;
}
catch (Exception ex)
{
// log info
} return flag;
} private void WriteBufferToFile(object obj)
{
List<string> list = null;
int n = ; // 写入IO的buffer文件个数
List<int> keys = null; // 写入IO的buffer文件个数 lock (Lock_Write_IO)
{
if (IsLastWriteCompleted) // 判读上一次写入IO是否完成
{
// 根据缓冲池大小,定期清理和写入IO文件
if ((this.CurrnetSize >= this.MaxSize
|| this.CurrnetRows >= this.MaxRows))
{
IsLastWriteCompleted = false; this.CurrnetSize = ;
this.CurrnetRows = ; keys = Dic_MesgData.Keys.ToList();
foreach (int key in keys)
{
List<string> list_old = this.Dic_MesgData[key];
this.Dic_MesgData[key] = new List<string>(); bool flag_write = this.WriteFile((LogTypeEnum)key, list_old);
if (flag_write)
{
n++;
}
} IsLastWriteCompleted = true;
}
}
}
} /// <summary>
/// 异步写入日志文件
/// </summary>
/// <param name="logType">日志类别</param>
/// <param name="list">日志内容</param>
/// <returns></returns>
public bool WriteFile(LogTypeEnum logType, List<string> list)
{
{
bool flag = false; FileStream fs = null;
StringBuilder sb = new StringBuilder();
byte[] data = null; long size_text = , row_text = ;
long index_row = , count_row = list != null ? list.Count : ; //if (!this.Dic_Stream.TryGetValue((int)logType, out fs))
//{
// return false;
//} try
{
fs = File.Open(string.Format(fpath, logType), FileMode.Append, FileAccess.Write); foreach (string item in list)
{
sb.Append(item);
index_row++; // 当前位置 size_text += item.Length;
row_text++; if ((size_text >= * || row_text >= )
|| (index_row == count_row && sb.Length > ))
{
size_text = ;
row_text = ; // wrire file
data = Encoding.UTF8.GetBytes(sb.ToString()); #region 异步写入
//IAsyncResult asyc = fs.BeginWrite(data, 0, data.Length, (o) =>
// {
// object ret = o;
// }, null);
//asyc.AsyncWaitHandle.WaitOne();
//fs.EndWrite(asyc);
#endregion #region 同步写入
fs.Write(data, , data.Length);
#endregion fs.Flush(); // test code size_text = ;
row_text = ; data = null;
sb = null;
sb = new StringBuilder();
}
} flag = index_row == count_row;
}
catch (Exception ex)
{
// log info
}
finally
{
if (sb != null)
sb = null;
if (data != null)
data = null;
if (list != null)
list = null; if (fs != null)
fs.Dispose();
} return flag;
}
}
}
}
【原创】有关Buffer使用,让你的日志类库解决IO高并发写的更多相关文章
- 通过编写一个简单的日志类库来加深了解C#的文件访问控制
在程序的开发调试过程及发布运行后的状态监控中,日志都有着极其重要的分量,通过在关键逻辑节点将关键数据记录到日志文件当中能帮助我们尽快找到程序问题所在.网上有不少专业成熟的日志组件可用,比如log4ne ...
- 使用windows服务和MSMQ和进行日志管理(解决高并发问题)
首先,建立一个windows服务项目 然后进行设计视图 在工作区空白处右属,添加一个安装项目 然后就可以写我们的代码了,我们的服务需要实时监视MSMQ的队列中有没有记录,如果有,就向数据库中插入 核心 ...
- WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作)
WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作) 好吧,还是那个社区APP,非管理系统,用户行为日志感觉不是很必要的,但是,错误日 ...
- 【分享】我们用了不到200行代码实现的文件日志系统,极佳的IO性能和高并发支持,附压力测试数据
很多项目都配置了日志记录的功能,但是,却只有很少的项目组会经常去看日志.原因就是日志文件生成规则设置不合理,将严重的错误日志跟普通的错误日志混在一起,分析起来很麻烦. 其实,我们想要的一个日志系统核心 ...
- 结构化日志类库 ---- Serilog库
在过去的几年中,结构化日志已经大受欢迎.而Serilog是 .NET 中最著名的结构化日志类库 ,我们提供了这份的精简指南来帮助你快速了解并运用它. 0. 内容 设定目标 认识Serilog 事件和级 ...
- GG_Logs 日志类库封装使用说明
3.6.GG_Logs 日志类库封装使用说明 GG_Logs类库项目,Nuget安装log4net 添加代码配置代码: [assembly: log4net.Config.XmlConfigurato ...
- 大幅度改变celery日志外观,修改成日志可点击跳转和鲜艳五彩日志,解决脚本中已添加handler的logger和框架日志重复记录问题。
大幅度改变celery日志外观,修改成日志可点击跳转和鲜艳五彩日志,解决脚本中已添加handler的logger和框架日志重复记录问题.打猴子补丁. 先把脚本中的所有logger的handlers全部 ...
- Go项目实战:打造高并发日志采集系统(六)
前情回顾 前文我们完成了日志采集系统的日志文件监控,配置文件热更新,协程异常检测和保活机制. 本节目标 本节加入kafka消息队列,kafka前文也介绍过了,可以对消息进行排队,解耦合和流量控制的作用 ...
- Go项目实战:打造高并发日志采集系统(二)
日志统计系统的整体思路就是监控各个文件夹下的日志,实时获取日志写入内容并写入kafka队列,写入kafka队列可以在高并发时排队,而且达到了逻辑解耦合的目的.然后从kafka队列中读出数据,根据实际需 ...
随机推荐
- 在windows下安装运行disconf
一.环境准备 1.下载disconf源码 (本文以2.6.36为例) https://github.com/knightliao/disconf 2.下载nginx1.10.2(windows版) h ...
- python enhanced generator - coroutine
本文主要介绍python中Enhanced generator即coroutine相关内容,包括基本语法.使用场景.注意事项,以及与其他语言协程实现的异同. enhanced generator 在上 ...
- 用Spark学习FP Tree算法和PrefixSpan算法
在FP Tree算法原理总结和PrefixSpan算法原理总结中,我们对FP Tree和PrefixSpan这两种关联算法的原理做了总结,这里就从实践的角度介绍如何使用这两个算法.由于scikit-l ...
- 我的音乐盒子(nodejs7 + koa2 + vue + vuex + vue-router)
你们知道的,nodejs对jser来说,是个好东西,快快的,自从接触nodejs后,总想弄点东西. 这弄个啥了,一天打开百度音乐盒,哟,自己弄一个如何了,好啊好啊. 后台: nodejs 7 + ko ...
- pycharm 修改新建文件时的头部模板(默认为__author__='...')
pycharm 修改新建文件时的头部模板 默认为__author__='...' [省略号是默认你的计算机名] 修改这个作者名的步骤: 依次点击:File->Settings->Ed ...
- js面试题-2
// 1.截取字符串 var aa = "abcd"; console.log(aa.substr(,)); var str = "qweda"; consol ...
- java中File类的常用所有方法及其应用
创建:createNewFile()在指定位置创建一个空文件,成功就返回true,如果已存在就不创建,然后返回false.mkdir() 在指定位置创建一个单级文件夹.mkdirs() 在指定位置 ...
- GitHub 上下载代码运行报错 :'The sandbox is not sync with the Podfile.lock\'
问题描述: github下载的Demo,很多时候使用到CocoaPods,有的时候因为依赖关系或者版本问题不能编译运行.出现例如The sandbox is not sync with the Pod ...
- 税号输入框 将input框中的输入自动转化成半角大写
这两天出了这么一个需求,输入税号的时候,需要自动将其转化为半角大写,并且阻止标点符号中文汉字的输入.(下面会有:全半角转换.文本框选中.光标位置判断.设置光标位置 这些内容) 然后我就开始了慢慢查找资 ...
- 《Oracle 从头来过》--第一篇
ps:最近被领导找谈话,让在数据库方面要加强自身的学习(那叫一个尴尬(@﹏@)~(@﹏@)~),打算重新拾起... 下面相当于学习的一个记录吧,也为以后查找方便O(∩_∩)O 咱们从最基本的创建表开始 ...