【原创】有关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队列中读出数据,根据实际需 ...
随机推荐
- nginx安装配置
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,由俄罗斯的程序设计师Igor Sysoev所开发,其特点是占有内存少,并发能力强 1. apache ...
- 实现一个类 Vue 的 MVVM 框架
Vue 一个 MVVM 框架.一个响应式的组件系统,通过把页面抽象成一个个组件来增加复用性.降低复杂性 主要特色就是数据操纵视图变化,一旦数据变化自动更新所有关联组件~ 所以它的一大特性就是一个数据响 ...
- iOS 插件化开发汇总 Small框架
应用插件化背景 目前很多应用功能越来越多,软件显得越来越臃肿.因此插件化就成了很多软件发展的必经之路,比如支付宝这种平台级别的软件: 页上密密麻麻的功能,而且还在增多,照这个趋势发展下去,软件包的大小 ...
- Wireshark网络抓包(二)——过滤器
一.捕获过滤器 选中捕获选项后,就会弹出下面这个框,在红色输入框中就可以编写过滤规则. 1)捕获单个IP地址 2)捕获IP地址范围 3)捕获广播或多播地址 4)捕获MAC地址 5)捕获所有端口号 6) ...
- [CSS3]学习笔记-CSS基本样式讲解
1.CSS样式-背景 CSS运行应用纯色作背景,也允许使用背景图像创建相当复杂的效果 <!DOCTYPE html> <html> <head lang="en ...
- shift、unshift、 push、pop用法--JavaScript参考手册
转自http://www.blogbus.com/kingslay-logs/216353709.html shift()定义和用法shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元 ...
- angular实现跨域
angular.js 自带jsonp,实现跨域,下面来实搜索框的下拉列表,使用百度和360分别尝试一下 百度:url截取之后红色部分需替换 :https://sp0.baidu.com/5a1Fazu ...
- C语言 动态创建二维数组
/*C语言 如何动态创建二维数组 转化为一维数组申请数组,创建和释放都比较简单 */ #include <stdlib.h> #include <stdio.h> #inclu ...
- 使用node-inspector调试nodejs程序<nodejs>
1.npm install -g node-inspector // -g 导入安装路径到环境变量 一般是c盘下AppData目录下 2.node-inspector & //启动node- ...
- windows下vue+webpack前端开发环境搭建及nginx部署
一.开发环境搭建 1.前端框架一般都依赖nodejs,我们首先要安装node.js.请参考http://www.cnblogs.com/wuac/p/6381819.html. 2.由于许多npm的源 ...