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

 

posted @ 2017-03-19 12:55  tiandong  阅读(2300)  评论(9编辑  收藏  举报