【原创】有关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队列中读出数据,根据实际需 ...
随机推荐
- CentOS7 ssh无密码登录
准备工作:给各个主机取个名字,如master(主节点),slave01(从节点01),slave02(从节点02) 1.修改主机名: hostname master hostname slave01 ...
- Biztalk AS2开发经验总结
一. 准备证书 4 1. 升级win2008 R2证书服务 4 2. 申请证书 6 二. 配置证书 13 1. 为AS2配置证书 13 2. ...
- SQL SERVER——CPU问题定位与解决
CPU问题定位基本流程: 性能计数器诊断 主要用到的性能计数器 %Process Time 全实例 (主要用于查看当前服务器的CPU 情况) %Process Time sqlservr (主 ...
- oracle 12c安装详细教程
数据库下载:www.orcale.com访问官网自行下载. 一,将下载好的两个压缩包在同一目录中进行解压(文件目录尽量用英文) 解压后如下使用管理员身份运行setup安装程序 注意:对于学习阶段,这一 ...
- mongodb终端指令
-h [--help]显示此使用信息 --version显示版本信息 -f [--config] arg配置文件指定 ...
- Java Web(二) Servlet中response、request乱码问题解决
三月不减肥,五月徒伤悲,这就是我现在的状态,哈哈~ 健身.博客坚持. --WH 一.request请求参数出现的乱码问题 get请求: get请求的参数是在url后面提交过来的,也就是在请求行中, M ...
- C语言——选择排序
---恢复内容开始--- 算法思想: n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果: ①初始状态:无序区为R[1..n],有序区为空. ②第1趟排序 在无序区R[1..n]中选出 ...
- Vue.js 系列教程 5:动画
原文:intro-to-vue-5-animations 译者:nzbin 译者的话:经过两周的努力,终于完成了这个系列的翻译,由于时间因素及个人水平有限,并没有详细的校对,其中仍然有很多不易理解的地 ...
- 将vim打造成php的IDE开发环境
将vim打造成IDE开发环境 本文主要介绍将vim打造成IDE开发环境,如代码补全,高亮显示,函数跳转,函数自动注释等 首先介绍2款VIM插件管理器:Vbundle,Pathogen 本文中的vim插 ...
- 学习笔记——Java核心技术之接口、继承与多态练习题
1.创建一个抽象类,验证它是否可以实例化对象. package com.lzw; public abstract class UseCase3 { abstract void doit(); publ ...