【本人原创,欢迎交流和分享技术,转载请附上如下内容:

如果你觉得这篇文章对你有帮助,请记得帮我点赞, 谢谢!

作者: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高并发写的更多相关文章

  1. 通过编写一个简单的日志类库来加深了解C#的文件访问控制

    在程序的开发调试过程及发布运行后的状态监控中,日志都有着极其重要的分量,通过在关键逻辑节点将关键数据记录到日志文件当中能帮助我们尽快找到程序问题所在.网上有不少专业成熟的日志组件可用,比如log4ne ...

  2. 使用windows服务和MSMQ和进行日志管理(解决高并发问题)

    首先,建立一个windows服务项目 然后进行设计视图 在工作区空白处右属,添加一个安装项目 然后就可以写我们的代码了,我们的服务需要实时监视MSMQ的队列中有没有记录,如果有,就向数据库中插入 核心 ...

  3. WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作)

    WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作) 好吧,还是那个社区APP,非管理系统,用户行为日志感觉不是很必要的,但是,错误日 ...

  4. 【分享】我们用了不到200行代码实现的文件日志系统,极佳的IO性能和高并发支持,附压力测试数据

    很多项目都配置了日志记录的功能,但是,却只有很少的项目组会经常去看日志.原因就是日志文件生成规则设置不合理,将严重的错误日志跟普通的错误日志混在一起,分析起来很麻烦. 其实,我们想要的一个日志系统核心 ...

  5. 结构化日志类库 ---- Serilog库

    在过去的几年中,结构化日志已经大受欢迎.而Serilog是 .NET 中最著名的结构化日志类库 ,我们提供了这份的精简指南来帮助你快速了解并运用它. 0. 内容 设定目标 认识Serilog 事件和级 ...

  6. GG_Logs 日志类库封装使用说明

    3.6.GG_Logs 日志类库封装使用说明 GG_Logs类库项目,Nuget安装log4net 添加代码配置代码: [assembly: log4net.Config.XmlConfigurato ...

  7. 大幅度改变celery日志外观,修改成日志可点击跳转和鲜艳五彩日志,解决脚本中已添加handler的logger和框架日志重复记录问题。

    大幅度改变celery日志外观,修改成日志可点击跳转和鲜艳五彩日志,解决脚本中已添加handler的logger和框架日志重复记录问题.打猴子补丁. 先把脚本中的所有logger的handlers全部 ...

  8. Go项目实战:打造高并发日志采集系统(六)

    前情回顾 前文我们完成了日志采集系统的日志文件监控,配置文件热更新,协程异常检测和保活机制. 本节目标 本节加入kafka消息队列,kafka前文也介绍过了,可以对消息进行排队,解耦合和流量控制的作用 ...

  9. Go项目实战:打造高并发日志采集系统(二)

    日志统计系统的整体思路就是监控各个文件夹下的日志,实时获取日志写入内容并写入kafka队列,写入kafka队列可以在高并发时排队,而且达到了逻辑解耦合的目的.然后从kafka队列中读出数据,根据实际需 ...

随机推荐

  1. .md即markdown文件的基本常用编写语法(图文并茂)

    序言: 很久没有写博客了,感觉只要是不写博客,人就很变得很懒,学的知识点感觉还是记不住,渐渐地让我明白,看的越多,懂的越少(你这话不是有毛病吗?应该是看的越多,懂的越多才对),此话怎讲,当你在茫茫的前 ...

  2. 基于basys2驱动LCDQC12864B的verilog设计图片显示

    话不多说先上图 前言 在做这个实验的时候在网上找了许多资料,都是关于使用单片机驱动LCD显示,确实用单片机驱动是要简单不少,记得在FPGA学习交流群里问问题的时候,被前辈指教,说给我最好的指教便是别在 ...

  3. 【python基础】 Tkinter 之 几何管理器

    Tkinter支持三种几何管理器:网格管理器,包管理器,位置管理器 提示:由于每个管理器都有自己放置小构件的风格,最好不要在同一个容器中的小构件使用多个管理器.可以使用框架作为子容器以获取期望的布局. ...

  4. C++编程练习(5)----“实现简单的循环队列的顺序存储结构“

    队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表. 队列是一种先进先出(First In First Out)的线性表,简称FIFO.允许插入的一端称为队尾,允许删除的一端 ...

  5. How To Ask Questions The Smart Way 转

    先查后问多思考莫做伸手党. 原文链接 译文链接

  6. 自动化CodeReview - ASP.NET Core请求参数验证

    自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 参数验证实现 在做服务端开发 ...

  7. UIApplication 和 Appdelegate-----iOS

    正文 一 UIApplication 1.一个UIApplication代表是一个应用程序,而且是单例的.一个程序也只能有一个UIApplication对象 2.获取UIApplication对象: ...

  8. 数组&&函数数组

    数组:一次性定义多个同类型的变量,数组在 内存中存储空间必须是连续的(查询比较快)定义数组: int a[]; int[] a;分配空间: a=new int[5]; 自动为数组元素赋以默认值 a[0 ...

  9. HTML学习三

    今天主要学习的为JS和HTML标签的一起使用: 一.重定向: <html> <head> <title>JavaScript1</title> < ...

  10. Codeforces Good Bye 2016 题解

    好久没有fst题了...比赛先A了前4题然后发现room里有人已经X完题了没办法只能去打E题,结果差一点点打完...然后C题fst掉了结果就掉rating 了...下面放题解 ### [A. New ...