【原创】有关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
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高并发写相关推荐
- Oracle 日志的核心意义(快速提交,写缓存,回滚)
这篇文章是参考甲骨论老相老师的教学视频 http://v.youku.com/v_show/id_XMzk1MDA3NjA4.html 所做的学习笔记 1.Oracle有1个重要的原则. ...
- 纯C语言日志类库 Zlog
转自: http://hardysimpson.github.io/zlog/UsersGuide-CN.html 难易 著23 Contents Chapter 1 zlog是什么? 1.1 兼 ...
- Netty(一)基础socketchannel,Buffer,selector黏包 半包解决 实战
NIO 基础 non-blockiong io:非阻塞 阻塞vs非阻塞 三大组件 1.channel & Buffer channel : 双向流 Buffer:暂存数据 缓冲区 常见chan ...
- 构建大规模分布式服务--高并发、高可用架构系列,高质量原创好文
当我们在谈论"服务治理"的时候,都在谈论些什么? 我从业之初接触到的便是一堆基于Webservice.Hessain等实现的跨语言的分布式系统,那是SOA架构和理念十分盛行的时代, ...
- [原创]商城系统下单库存管控系列杂记(二)(并发安全和性能部分延伸)
商城系统下单库存管控系列杂记(二)(并发安全和性能部分延伸) 前言 参与过几个中小型商城系统的开发,随着时间的增长,以及对系统的深入研究和测试,发现确实有很多值得推敲和商榷的地方(总有 ...
- 01 手把手带你构建大规模分布式服务--高并发、高可用架构系列,高质量原创好文!...
作者:丁浪,目前在创业公司担任高级技术架构师.曾就职于阿里巴巴大文娱和蚂蚁金服.具有丰富的稳定性保障,全链路性能优化的经验.架构师社区特邀嘉宾! 阅读本(系列)文章,你将会收获: 全面.体系化的了解大 ...
- .NET WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作)
.NET WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作) 参考文章: (1).NET WebAPI 用ExceptionFilt ...
- kubernetes 清理日志命令_Kubernetes之容器数据写满磁盘解决方法
磁盘写满引发的后果 容器数据磁盘写满造成的后果: Pod 不能删除 (一直 Terminating) Pod 不能被创建 (一直 ContainerCreating) 磁盘写满分两种情况: 磁盘空间全 ...
- [原创]windows server 2012 AD架构 试验 系列 – 15解决AD复制冲突
[原创]windows server 2012 AD架构 试验 系列 – 15解决AD复制冲突 这节我详细说下 ADDS使用stamp来作为解决冲突的依据 Stamp由三块组成 Version, ti ...
- 日志对服务器压力,服务器一大堆错误日志的解决方法
服务器一大堆错误日志的解决方法 更新时间:2008年04月20日 22:34:37 作者: 服务器出现大量的错误的解决方法 这个服务器是这个月初租的,花了大半天的时间做系统安全设置,由于比较急着用 ...
最新文章
- Linux下查看系统信息
- 我删了这些训练数据…模型反而表现更好了!?
- OA实施成功率提升,流程梳理是关键
- 4个优化MongoDB的技巧
- mysql 配置多个数据库连接_总结MySQL修改最大连接数的两个方式
- Python魔术世界 1 如何使用Visual Studio在WIN10中一键安装Python3入门编程环境并测试Django...
- POJ 2828Buy Tickets
- 2021年你想转行吗?写给迷茫的你!
- jsp实现简易购物车
- ker矩阵是什么意思_基向量、标准正交基、对称矩阵、Hermite阵
- 庖丁解牛 - 图解MySQL 8.0优化器查询解析篇
- html实现iphone桌面,HTML_Html5实现iPhone开机界面示例代码,今天我突发其想,想到可以用H - phpStudy...
- 如何用word 2007在指定页插入页码和页眉页脚?
- Vert.x(vertx) Web开发-路由
- ES6 lterator迭代器是个什么东西?有什么用?
- B2B从销售到深度协作
- IBM获5896项专利连续18年蝉联年度冠军
- npm install 原理分析
- 银行卡怎么突然收不到短信信息服务器,银行卡开通了短信通知功能为什么没收到通知?十条原因看清楚...
- Tornadao—模板语法(函数)
热门文章
- scrapy 处理动态加载,使用phantomjs
- 谈软件开发项目管理之需求变更
- IE6-IE9使用JSON、table.innerHTML 问题
- printk与syslog(至少在Redhat中是这样的)+Ubuntu下用最简单的读到/proc/kmsg
- google搜索url参数总结
- WinCE学习系列(1)——在VS2008的环境下安装WinCE 5.0仿真模拟器
- EditPlus中有用的快捷键
- MySQL中视图的使用及多表LEFT JOIN的技巧分享
- JZOJ_3928. 射击 (Standard IO)
- 高性能相关----爬虫