zoukankan      html  css  js  c++  java
  • 【原创】有关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 }
  • 相关阅读:
    【Java并发基础】安全性、活跃性与性能问题
    【Java并发基础】使用“等待—通知”机制优化死锁中占用且等待解决方案
    【NS-3学习】ns3-模拟基础:关键概念,日志,命令行参数
    【Java并发基础】死锁
    【Java并发基础】加锁机制解决原子性问题
    【Java并发基础】Java内存模型解决有序性和可见性问题
    【Java并发基础】并发编程bug源头:可见性、原子性和有序性
    【NS-3学习】ns-3模拟基础:目录结构,模块,仿真流程
    TCP和UDP的优缺点及区别
    七层协议与网络配置
  • 原文地址:https://www.cnblogs.com/itshare/p/6580165.html
Copyright © 2011-2022 走看看