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

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

作者: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. Jsoup后台解析html、jsp网页

    在一些网络爬虫或者从第三方网站抓取信息的程序都面临1个问题,如何从网页中把所需的信息提取出来,Jsoup是个比较好的选择,它能把网站内容解析成Document,再从document中取element就 ...

  2. API网关Ocelot 使用Polly 处理部分失败问题

    在实现API Gateway过程中,另外一个需要考虑的问题就是部分失败.这个问题发生在分布式系统中当一个服务调用另外一个服务超时或者不可用的情况.API Gateway不应该被阻断并处于无限期等待下游 ...

  3. (@WhiteTaken)设计模式学习——工厂方法模式

    这个工厂方法模式,是简单工厂的延伸,不同点在于,将某个具体的类继续细分,将核心部分抽象成一个接口.而简单工厂,把核心写在了一个类上,不利于拓展. 举个例子,简单工厂中有苹果类,香蕉类,我们创建了一个F ...

  4. C语言一维数组转换为二维数组

    一维转二维代码示例: #include <stdio.h> #include <stdlib.h> #define ROW 3 #define COL 2 int main(i ...

  5. matlab 中max函数用法

    Matlab中max函数在矩阵中求函数大小的实例如下:(1)C = max(A)返回一个数组各不同维中的最大元素.如果A是一个向量,max(A)返回A中的最大元素.如果A是一个矩阵,max(A)将A的 ...

  6. Apache 的安装

    1.获取源码包 cd /usr/local/src/ ls wget http://mirrors.cnnic.cn/apache/httpd/httpd-2.4.25.tar.gz 2.解压.编译. ...

  7. 初探 discuz

    测试: vim /etc/hosts       ##ip地址转换 修改windows 的配置文件,写字板打开 vim /usr/local/apache/conf/httpd.conf vim /u ...

  8. OSGI框架中通过BundleContext对象对服务的注册与引用

    BundleActivator 在每个Bundle新建时都会默认生成Activator类,该类实现了BundleActivator类,实现了其start()和stop()两个方法 BundleCont ...

  9. cloudbase-init 自动扩盘的副作用 - 每天5分钟玩转 OpenStack(154)

    这是 OpenStack 实施经验分享系列的第 4 篇. cloudbase-init 的一项功能是自动扩展 windows 的 C 盘.比如 windows 镜像是 20G,在部署 instance ...

  10. 4G最快网速相当于30M宽带

    [导读]据北京移动方面介绍,目前其4G网络的覆盖范围包括:东西北三环.南至两广路以内的地区:清华北大.国贸CBD及园博会等地区. 在4G年内发牌已成定局的背景下,各运营商都在加快布局,北京移动近期就推 ...