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

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

作者:itshare 【转自】http://www.cnblogs.com/itshare/

通常我们知道,当一个日志接口被外部程序多个线程请求后,如果没有使用Buffer和异步写入IO的处理。
CPU就会一直处于加锁和解锁的频繁切换,这样加上等待每次线程锁中的IO处理完毕的时间,
高并发调用日志接口的整个过程中响应和处理速度就会严重变慢,并且让CPU一直居高不下。

这里,Coding First,先看对比结果,最后我再附上LogBufferPool的代码实现。

下面我们分别做两组实验:时间单位为秒
100个线程,并发消费100000个请求。看下面每组实验的结果。

实验1:普通的日志接口,无Buffer缓冲池和异步写入IO的处理。

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

最后,在这里贴上我的代码实现:LogBufferPool

  1 namespace ITS.Base.Comm.Logger_FastIO
  2 {
  3     /// <summary>
  4     /// 日志缓冲池
  5     /// </summary>
  6     public class LogBufferPool :IDisposable
  7     {
  8         /// <summary>
  9         /// 工作日期
 10         /// </summary>
 11         private DateTime worktime
 12         {
 13             get;
 14             set;
 15         }
 16
 17         /// <summary>
 18         /// 上一次写入时间
 19         /// </summary>
 20         private DateTime LastWriteTime
 21         {
 22             get;
 23             set;
 24         }
 25
 26         /// <summary>
 27         /// 日志的文件路径
 28         /// </summary>
 29         private string fpath
 30         {
 31             get;
 32             set;
 33         }
 34
 35         /// <summary>
 36         /// 最大行数 - write buffer
 37         /// </summary>
 38         public long MaxRows
 39         {
 40             get;
 41             private set;
 42         }
 43
 44         /// <summary>
 45         /// 最大字数 - write buffer
 46         /// </summary>
 47         public long MaxSize
 48         {
 49             get;
 50             private set;
 51         }
 52
 53         /// <summary>
 54         /// 当前字数 - write buffer
 55         /// </summary>
 56         public long CurrnetSize
 57         {
 58             get;
 59             private set;
 60         }
 61         /// <summary>
 62         /// 当前行数 - write buffer
 63         /// </summary>
 64         public long CurrnetRows
 65         {
 66             get;
 67             private set;
 68         }
 69
 70         /// <summary>
 71         /// Lock of Buffer write
 72         /// </summary>
 73         private static object Lock_Write_IO = 1;
 74         /// <summary>
 75         /// Flag of last write Buffter
 76         /// </summary>
 77         private static bool IsLastWriteCompleted = true;
 78         /// <summary>
 79         /// Time of Buffer write
 80         /// </summary>
 81         static System.Threading.Timer timer_work = null;
 82
 83
 84         /// <summary>
 85         /// 文件流
 86         /// </summary>
 87         private Dictionary<int, FileStream> Dic_Stream = null;
 88
 89         /// <summary>
 90         /// 日志消息
 91         /// </summary>
 92         private Dictionary<int, List<string>> Dic_MesgData = null;
 93
 94         /// <summary>
 95         /// 构造函数 - 日志缓冲池
 96         /// </summary>
 97         /// <param name="fpath">日志文件路径:logType</param>
 98         /// <param name="max_size">最大字数 - write buffer</param>
 99         /// <param name="max_rows">最大行数 - write buffer</param>
100         public LogBufferPool(string fpath, long max_size = 50000, long max_rows = 1000)
101         {
102             this.worktime = DateTime.Now;
103             this.LastWriteTime = DateTime.Now;
104             this.fpath = fpath;
105             this.MaxSize = max_size;
106             this.MaxRows = max_rows;
107             this.Dic_Stream = new Dictionary<int, FileStream>();
108             this.Dic_MesgData = new Dictionary<int, List<string>>();
109
110             IsLastWriteCompleted = true;
111             if (timer_work == null)
112             {
113                 // 1*1000: 1秒后启动计时器
114                 // 执行计划:每隔开秒执行一次
115                 timer_work = new System.Threading.Timer(new System.Threading.TimerCallback(WriteBufferToFile), this, 1 * 1000, 5 * 1000);
116             }
117         }
118
119         /// <summary>
120         /// 写完日志后,再释放资源
121         /// </summary>
122         public void Dispose()
123         {
124             try
125             {
126                 Dictionary<int, List<string>> dic = this.Dic_MesgData;
127                 if (dic != null && dic.Count > 0)
128                 {
129                     foreach (KeyValuePair<int, List<string>> pair in this.Dic_MesgData)
130                     {
131                         WriteFile((LogTypeEnum)pair.Key, pair.Value);
132                     }
133                 }
134             }
135             catch (Exception ex)
136             {
137                 // log
138             }
139             finally
140             {
141                 GC.Collect();
142             }
143         }
144
145         /// <summary>
146         /// 添加日志消息
147         /// </summary>
148         /// <param name="logType">日志类别</param>
149         /// <param name="msg">日志内容</param>
150         /// <returns></returns>
151         public bool AddLog(LogTypeEnum logType, string msg)
152         {
153             bool flag = false;
154             List<string> list = null;
155             int n = 0; // 写入IO的buffer文件个数
156             List<int> keys = null; // 写入IO的buffer文件个数
157
158
159             //long size_text = 0, row_text = 0;
160             long index_row = 0, count_row = list != null ? list.Count : 0;
161
162
163             try
164             {
165                 if (!this.Dic_MesgData.TryGetValue((int)logType, out list))
166                 {
167                     list = new List<string>();
168                     this.Dic_MesgData.Add((int)logType, list);
169
170                     //FileStream fs = File.Open(string.Format(fpath, logType), FileMode.Append, FileAccess.Write);
171                     //// fs.WriteTimeout = 24 * 60 * 60;
172                     //this.Dic_Stream.Add((int)logType, fs);
173                 }
174
175                 // 添加日志消息
176                 list.Add(msg);
177                 index_row++;
178
179                 this.CurrnetSize += msg.Length;
180                 this.CurrnetRows++;
181
182                 // 根据缓冲池大小,定期清理和写入IO文件
183                 //if ((this.CurrnetSize >= this.MaxSize
184                 //    || this.CurrnetRows >= this.MaxRows)
185                 //    || (DateTime.Now - this.LastWriteTime).TotalSeconds > 10)
186                 //{
187                 //    this.CurrnetSize = 0;
188                 //    this.CurrnetRows = 0;
189
190                 //    keys = Dic_MesgData.Keys.ToList();
191                 //    foreach (int key in keys)
192                 //    {
193                 //        List<string> list_old = this.Dic_MesgData[key];
194                 //        this.Dic_MesgData[key] = new List<string>();
195
196                 //        bool flag_write = this.WriteFile((LogTypeEnum)key, list_old);
197                 //        if (flag_write)
198                 //        {
199                 //            n++;
200                 //        }
201                 //    }
202                 //}
203
204                 //WriteBufferToFile(null);
205
206                 flag = true;
207
208                 // flag = keys != null && keys.Count > 0 ? n == keys.Count : true;
209             }
210             catch (Exception ex)
211             {
212                 // log info
213             }
214
215             return flag;
216         }
217
218         private void WriteBufferToFile(object obj)
219         {
220             List<string> list = null;
221             int n = 0; // 写入IO的buffer文件个数
222             List<int> keys = null; // 写入IO的buffer文件个数
223
224             lock (Lock_Write_IO)
225             {
226                 if (IsLastWriteCompleted) // 判读上一次写入IO是否完成
227                 {
228                     // 根据缓冲池大小,定期清理和写入IO文件
229                     if ((this.CurrnetSize >= this.MaxSize
230                         || this.CurrnetRows >= this.MaxRows))
231                     {
232                         IsLastWriteCompleted = false;
233
234                         this.CurrnetSize = 0;
235                         this.CurrnetRows = 0;
236
237                         keys = Dic_MesgData.Keys.ToList();
238                         foreach (int key in keys)
239                         {
240                             List<string> list_old = this.Dic_MesgData[key];
241                             this.Dic_MesgData[key] = new List<string>();
242
243                             bool flag_write = this.WriteFile((LogTypeEnum)key, list_old);
244                             if (flag_write)
245                             {
246                                 n++;
247                             }
248                         }
249
250                         IsLastWriteCompleted = true;
251                     }
252                 }
253             }
254         }
255
256         /// <summary>
257         /// 异步写入日志文件
258         /// </summary>
259         /// <param name="logType">日志类别</param>
260         /// <param name="list">日志内容</param>
261         /// <returns></returns>
262         public bool WriteFile(LogTypeEnum logType, List<string> list)
263         {
264             {
265                 bool flag = false;
266
267                 FileStream fs = null;
268                 StringBuilder sb = new StringBuilder();
269                 byte[] data = null;
270
271                 long size_text = 0, row_text = 0;
272                 long index_row = 0, count_row = list != null ? list.Count : 0;
273
274                 //if (!this.Dic_Stream.TryGetValue((int)logType, out fs))
275                 //{
276                 //    return false;
277                 //}
278
279
280
281                 try
282                 {
283                     fs = File.Open(string.Format(fpath, logType), FileMode.Append, FileAccess.Write);
284
285                     foreach (string item in list)
286                     {
287                         sb.Append(item);
288                         index_row++; // 当前位置
289
290                         size_text += item.Length;
291                         row_text++;
292
293                         if ((size_text >= 10 * 10000 || row_text >= 1000)
294                             || (index_row == count_row && sb.Length > 0))
295                         {
296                             size_text = 0;
297                             row_text = 0;
298
299                             // wrire file
300                             data = Encoding.UTF8.GetBytes(sb.ToString());
301
302                             #region 异步写入
303                             //IAsyncResult asyc = fs.BeginWrite(data, 0, data.Length, (o) =>
304                             //    {
305                             //        object ret = o;
306                             //    }, null);
307                             //asyc.AsyncWaitHandle.WaitOne();
308                             //fs.EndWrite(asyc);
309                             #endregion
310
311                             #region 同步写入
312                             fs.Write(data, 0, data.Length);
313                             #endregion
314
315                             fs.Flush(); // test code
316
317                             size_text = 0;
318                             row_text = 0;
319
320                             data = null;
321                             sb = null;
322                             sb = new StringBuilder();
323                         }
324                     }
325
326                     flag = index_row == count_row;
327                 }
328                 catch (Exception ex)
329                 {
330                     // log info
331                 }
332                 finally
333                 {
334                     if (sb != null)
335                         sb = null;
336                     if (data != null)
337                         data = null;
338                     if (list != null)
339                         list = null;
340
341                     if (fs != null)
342                         fs.Dispose();
343                 }
344
345                 return flag;
346             }
347         }
348     }
349 }

转载于:https://www.cnblogs.com/itshare/p/6580165.html

【原创】有关Buffer使用,让你的日志类库解决IO高并发写相关推荐

  1. Oracle 日志的核心意义(快速提交,写缓存,回滚)

    这篇文章是参考甲骨论老相老师的教学视频 http://v.youku.com/v_show/id_XMzk1MDA3NjA4.html 所做的学习笔记 1.Oracle有1个重要的原则.        ...

  2. 纯C语言日志类库 Zlog

    转自: http://hardysimpson.github.io/zlog/UsersGuide-CN.html 难易 著23 Contents Chapter 1  zlog是什么? 1.1  兼 ...

  3. Netty(一)基础socketchannel,Buffer,selector黏包 半包解决 实战

    NIO 基础 non-blockiong io:非阻塞 阻塞vs非阻塞 三大组件 1.channel & Buffer channel : 双向流 Buffer:暂存数据 缓冲区 常见chan ...

  4. 构建大规模分布式服务--高并发、高可用架构系列,高质量原创好文

    当我们在谈论"服务治理"的时候,都在谈论些什么? 我从业之初接触到的便是一堆基于Webservice.Hessain等实现的跨语言的分布式系统,那是SOA架构和理念十分盛行的时代, ...

  5. [原创]商城系统下单库存管控系列杂记(二)(并发安全和性能部分延伸)

      商城系统下单库存管控系列杂记(二)(并发安全和性能部分延伸)     前言   参与过几个中小型商城系统的开发,随着时间的增长,以及对系统的深入研究和测试,发现确实有很多值得推敲和商榷的地方(总有 ...

  6. 01 手把手带你构建大规模分布式服务--高并发、高可用架构系列,高质量原创好文!...

    作者:丁浪,目前在创业公司担任高级技术架构师.曾就职于阿里巴巴大文娱和蚂蚁金服.具有丰富的稳定性保障,全链路性能优化的经验.架构师社区特邀嘉宾! 阅读本(系列)文章,你将会收获: 全面.体系化的了解大 ...

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

    .NET WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作) 参考文章: (1).NET WebAPI 用ExceptionFilt ...

  8. kubernetes 清理日志命令_Kubernetes之容器数据写满磁盘解决方法

    磁盘写满引发的后果 容器数据磁盘写满造成的后果: Pod 不能删除 (一直 Terminating) Pod 不能被创建 (一直 ContainerCreating) 磁盘写满分两种情况: 磁盘空间全 ...

  9. [原创]windows server 2012 AD架构 试验 系列 – 15解决AD复制冲突

    [原创]windows server 2012 AD架构 试验 系列 – 15解决AD复制冲突 这节我详细说下 ADDS使用stamp来作为解决冲突的依据 Stamp由三块组成 Version, ti ...

  10. 日志对服务器压力,服务器一大堆错误日志的解决方法

    服务器一大堆错误日志的解决方法 更新时间:2008年04月20日 22:34:37   作者: 服务器出现大量的错误的解决方法 这个服务器是这个月初租的,花了大半天的时间做系统安全设置,由于比较急着用 ...

最新文章

  1. Linux下查看系统信息
  2. 我删了这些训练数据…模型反而表现更好了!?
  3. OA实施成功率提升,流程梳理是关键
  4. 4个优化MongoDB的技巧
  5. mysql 配置多个数据库连接_总结MySQL修改最大连接数的两个方式
  6. Python魔术世界 1 如何使用Visual Studio在WIN10中一键安装Python3入门编程环境并测试Django...
  7. POJ 2828Buy Tickets
  8. 2021年你想转行吗?写给迷茫的你!
  9. jsp实现简易购物车
  10. ker矩阵是什么意思_基向量、标准正交基、对称矩阵、Hermite阵
  11. 庖丁解牛 - 图解MySQL 8.0优化器查询解析篇
  12. html实现iphone桌面,HTML_Html5实现iPhone开机界面示例代码,今天我突发其想,想到可以用H - phpStudy...
  13. 如何用word 2007在指定页插入页码和页眉页脚?
  14. Vert.x(vertx) Web开发-路由
  15. ES6 lterator迭代器是个什么东西?有什么用?
  16. B2B从销售到深度协作
  17. IBM获5896项专利连续18年蝉联年度冠军
  18. npm install 原理分析
  19. 银行卡怎么突然收不到短信信息服务器,银行卡开通了短信通知功能为什么没收到通知?十条原因看清楚...
  20. Tornadao—模板语法(函数)

热门文章

  1. scrapy 处理动态加载,使用phantomjs
  2. 谈软件开发项目管理之需求变更
  3. IE6-IE9使用JSON、table.innerHTML 问题
  4. printk与syslog(至少在Redhat中是这样的)+Ubuntu下用最简单的读到/proc/kmsg
  5. google搜索url参数总结
  6. WinCE学习系列(1)——在VS2008的环境下安装WinCE 5.0仿真模拟器
  7. EditPlus中有用的快捷键
  8. MySQL中视图的使用及多表LEFT JOIN的技巧分享
  9. JZOJ_3928. 射击 (Standard IO)
  10. 高性能相关----爬虫