【原创】有关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 }