当前位置:   article > 正文

muduo源码学习(2):异步日志——异步日志的实现

异步日志

目录

什么是异步日志

异步日志的实现

前端与后端

前端与后端的交互

资源回收

后端与日志文件

滚动日志

自动flush缓冲区

开启异步日志功能

总结


        在前文中分析了日志消息的存储和输出,不过并没有涉及到异步日志,下面就来分析一下异步日志是如何实现的。

什么是异步日志

        在默认的情况下,日志消息都是直接打印到终端屏幕上,但是实际应用中,日志消息都应该写到本地文件,方便记录以及查询。

        最简单的方式就是每产生一条日志消息,都将其写到相应的文件中,然而这种方式效率低下,如果很多线程在某一段时间内需要输出大量日志,那么显然日志输出的效率是很低的。之所以效率低,就是因为每条日志消息都需要通过write这类的函数写出到本地磁盘,这就导致频繁调用IO函数,而磁盘操作本身就比较费时,这样一来后面的代码就只能阻塞住,直到前一条日志写出成功

        为了优化上述问题,一个比较好的办法就是:当日志消息积累到一定量的时候再写到磁盘中,这样就可以显著减少IO操作的次数,从而提高效率

        换句话说,当日志消息需要输出时,并不会立即将其写出到磁盘上,而是先把日志消息存储,直到达到”写出时机“才会将存储的日志消息写出到磁盘,这样一来,当日志消息生成时,只需要将其进行存储而不需要写出,后续代码也不会被阻塞,相对于前面的那种阻塞式日志,这种就是非阻塞式日志

        muduo的异步日志核心思想正是如此。当需要输出日志的时候,会先将日志存下来,日志消息存储达到某个阈值时将这些日志消息全部写到磁盘。需要考虑的是,如果日志消息产生比较慢,可能很长一段时间都达不到这个阈值,那就相当于这些日志消息一直无法写出到磁盘,因此,还应当设置一个超时值如3s,每过3s不管日志消息存储量是否达到阈值,都会将已经存储的日志消息写出到磁盘中。即日志写出到磁盘的两个时机:1、日志消息存储量达到写出阈值;2、每过3秒自动将存储的日志消息全部写出。

        这种非阻塞式日志也是异步的,因为产生日志的线程只负责产生日志,并不需要去管它产生的这条日志何时写出,写往何处...

       

异步日志的实现

       muduo中通过AsyncLogging类来实现异步日志。

       异步日志分为前端和后端两部分,前端负责存储生成的日志消息,而后端则负责将日志消息写出到磁盘,因此整个异步日志的过程可以看做如下所示:

       先来看看前端和后端分别指的是什么。

前端与后端

  1. class AsyncLogging : noncopyable
  2. {
  3. public:
  4. AsyncLogging(const string& basename,
  5. off_t rollSize,
  6. int flushInterval = 3);
  7. ~AsyncLogging()
  8. {
  9. if (running_)
  10. {
  11. stop();
  12. }
  13. }
  14. void append(const char* logline, int len);
  15. void start()
  16. {
  17. running_ = true;
  18. thread_.start();
  19. latch_.wait(); //等待,直到异步日志线程启动,才能继续往下执行
  20. }
  21. void stop() NO_THREAD_SAFETY_ANALYSIS
  22. {
  23. running_ = false;
  24. cond_.notify();
  25. thread_.join();
  26. }
  27. private:
  28. void threadFunc();
  29. typedef muduo::detail::FixedBuffer<muduo::detail::kLargeBuffer> Buffer;
  30. typedef std::vector<std::unique_ptr<Buffer>> BufferVector;
  31. typedef BufferVector::value_type BufferPtr;
  32. const int flushInterval_; //前端缓冲区定期向后端写入的时间(冲刷间隔)
  33. std::atomic<bool> running_; //标识线程函数是否正在运行
  34. const string basename_; //
  35. const off_t rollSize_;
  36. muduo::Thread thread_;
  37. muduo::CountDownLatch latch_;
  38. muduo::MutexLock mutex_;
  39. muduo::Condition cond_ GUARDED_BY(mutex_); //条件变量,主要用于前端缓冲区队列中没有数据时的休眠和唤醒
  40. BufferPtr currentBuffer_ GUARDED_BY(mutex_); //当前缓冲区 4M大小
  41. BufferPtr nextBuffer_ GUARDED_BY(mutex_); //预备缓冲区,主要是在调用append向当前缓冲添加日志消息时,如果当前缓冲放不下,当前缓冲就会被移动到前端缓冲队列中国,此时预备缓冲区用来作为新的当前缓冲
  42. BufferVector buffers_ GUARDED_BY(mutex_);//前端缓冲区队列
  43. };

        注意到这里typedef了一个新类型为Buffer类型,根据其定义可知,它就是前文所说的FixedBuffer缓冲区类型,而这个缓冲区大小由kLargeBuffer指定,大小为4M,因此,Buffer就是大小为4M的缓冲区类型。

        这里定义了currentBuffer_和nextBuffer_,这两个缓冲区就是上面所说的”前端“,用来暂时存储生成的日志消息,只不过nextBuffer_用作预备缓冲区,当currentBuffer_不够用时用nextBuffer_来补充currentBuffer_。

        然后就是buffers_,

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/954235
推荐阅读
相关标签
  

闽ICP备14008679号