第五章 高效的多线程日志

  • 日志有两种意思:

    • 诊断日志

    • 交易日志

  • 本章讲的是前一种日志,文本的供人阅读的日志,通常用于故障诊断和追踪,也可用于性能分析。

  • 日志通常要记录:

    • 收到的每条消息的id(关键字段,长度,hash等)

    • 收到的每条外部消息的全文

    • 发出每条消息的全文,每条消息都有全局唯一的id

    • 关键部分状态的变更,等等

5.1 功能需求

  • 日志库大体分为前端和后端两个部分

    • 前端负责提供应用程序使用的接口API,并生成日志消息
    • 后端负责把日志消息写到目的地
  • C++日志库的前端大体有两种API风格

    • printf的格式化输出风格
    • stream<<风格

    stream风格的好处是当输出日志级高于语句的日志级别时,打印日志操作时个空操作,运行时开销接近零

  • 分布式系统中的服务进程而言,日志的目的地只有一个:本地文件。往网络写日志消息时不靠谱的,因为诊断日志功能之一正是诊断网络故障,如果日志消息也是通过网络发到另一台机器就一损俱损…

  • 本地文件作为destination,日志文件的滚动时必须的,可以简化日志的归档实现

    • 文件大小(例如写满10GB就换下一个文件)
    • 时间(例如每天零点新建一个日志文件,不论上一个文件是否写满)

    日志文件压缩和归档,不应该是日志库应有的功能,应该交给专门的脚本去做

  • 日志重复利用空间的功能,只会帮倒忙

  • 往文件写日志的常见问题是,如果程序崩溃,最后几条日志信息就会丢失,因为日志库不能每条消息都flush硬盘,更不能每条消息都open/close文件,这样开销太大。

    • 定时(默认3秒)将缓冲区的日志消息flush到硬盘
    • 每条内存中的日志消息都带有cookie,其值为函数地址,通过core dump文件找到cookie就能找到尚未写到硬盘的消息

5.2 性能需求

  • 日志库的高性能体现在:

    • 每秒写几千上万条日志的时候没有明显的性能损失
    • 能应对一个进程产生大量日志数据的场景,1GB/min
    • 不阻塞正常的执行流程
    • 在多线程中不造成争用
  • muduo日志库的实现的优化措施:
    • 时间戳字符串中的日期和时间两部分是缓冲的,一秒之内的多条日志只需重新格式化微妙部分
    • 日志消息前4个字节时定长的,避免在运行期间求strlen,编译器认识memcpy()函数,对于定长的内存复制,会在编译期把它inline展开为高效的目标代码
    • 线程id时预先格式化为字符串,输出只需要拷贝几个字节
    • 每条日志消息的源文件名部分采用了编译期计算来获得basename,避免运行期strrchr()开销

5.3 多线程异步日志

多线程程序的每个进程最好写一个日志文件,这样分析日志最容易,不必再多个文件中跳来跳去

  • muduo日志库采用的时双缓冲技术

    准备两块buffer:A和B,前端负责往buffer A写入数据,后端负责将buffer A的数据写到文件中,之后前端往buffer B中填入新的数据,如此反复。

    用两个buffer的好处是在新建日志消息的时候不必等待磁盘文件操作,也避免每条新日志消息都触发后端日志线程,换句话就是前端不是将每一条日志消息分别送给后端,而是将多条日志消息平成一个大的buffer传给后端,相当于批处理,减少线程唤醒的开销,降低开销。

    为了及时将日志消息写道文件,即使buffer A没有满,日志库也要没3秒执行一次swap(buffer A, buffer B)

  • muduo采用前端后端,各两个buffer(curr buffer,next buffer)和一个buffer vector保存full buffer四种可能存在的情况

    • 前端写日志频率不高,后端3秒超时写入文件

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ny3JEoOc-1583147157747)(./img/5.3-buf1.png)]

      在第2.9s,curr使用了80%,第3秒后端线程醒过来,将curr送到buffers,把new1移动到curr,随后交换buffers和buffersToWrite,当文件写完之后,把new1重新填上,等待下次cond.waitForSeconds()返回

    • 3秒超时之前已经写满burr buffer,唤醒后端开始写入文件

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pY35lWXl-1583147157765)(./img/5.3-buf2.png)]

      在第1.5s,curr使用了80%,在1.8scurr写满送到buffers,将next替换到curr buff,唤醒后端线程。后端将curr加到buffers中,在把new1移动到curr,交换buffers和buffersToWrite,new2替换next。完成之后重新填充new1和new2

    • 前端短时间密集写入日志消息,用完了两个缓冲需要新分配缓冲

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1WTHFN2J-1583147157770)(./img/5.3-buf3.png)]

      在第1.8s,A已经写满,B接近满,已经notify()后端线程,但是后端线程由于一些原因没有立即响应,到1.9s线程B写满,前端线程新创建缓冲E。在1.8s+后端线程获得控制权,将C,D移动给前端,把当前的curr放到buffers,将A、B、E写到文件。使用A、B填充new1/2释放缓冲E

    • 文件写入速度较慢,导致前端耗尽了两个缓冲,并分配了新缓冲

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vz3gpLwR-1583147157772)(./img/5.3-buf4.png)]

      1.8s前和场景2相同,后端线程写入过慢,导致前端已经写满两个缓冲并且分配了一个新的缓冲,这期间notify已经丢失。当后端完成之后,发现buffers不为空,立刻进入下个循环

muduo学习笔记 - 第五章 高效的多线程日志相关推荐

  1. Programming Entity Framework-dbContext 学习笔记第五章

    ### Programming Entity Framework-dbContext 学习笔记 第五章 将图表添加到Context中的方式及容易出现的错误 方法 结果 警告 Add Root 图标中的 ...

  2. 《Go语言圣经》学习笔记 第五章函数

    <Go语言圣经>学习笔记 第五章 函数 目录 函数声明 递归 多返回值 匿名函数 可变参数 Deferred函数 Panic异常 Recover捕获异常 注:学习<Go语言圣经> ...

  3. 第5章 高效的多线程日志

    高效的多线程日志 功能需求 功能主要包括: 日志消息的级别: TRACE, DEBUG, INFO, WARN, ERROR, FATAL 日志消息有多个目的地(appender),如文件,socke ...

  4. 2022 最新 Android 基础教程,从开发入门到项目实战【b站动脑学院】学习笔记——第五章:中级控件

    第 5 章 中级控件 本章介绍App开发常见的几类中级控件的用法,主要包括:如何定制几种简单的图形.如何使用几种选择按钮.如何高效地输入文本.如何利用对话框获取交互信息等,然后结合本章所学的知识,演示 ...

  5. 《Android深度探究HAL与驱动开发》学习笔记----第五章

    第五章 搭建S3C6410开发板的测试环境 开发板是开发和学习嵌入式技术的主要硬件设备. 主要学习了搭建S3C6410开发板的测试环境.首先要了解到S3C6410是一款低功耗.高性价比的RISC处理器 ...

  6. javascript高级程序设计 学习笔记 第五章 上

      第五章   引用类型的值(对象)是引用类型的一个实例.在 ECMAScript 中,引用类型是一种数据结构, 用于将数据和功能组织在一起.它也常被称为类,但这种称呼并不妥当.尽管 ECMAScri ...

  7. css层叠样式表基础学习笔记--第五章 文本属性

    第五章 文本属性 5-01 字间距 5-02 行高 5-03 首行缩进 5-04 水平排列方式 5-05 垂直对齐方式 5-06 文本修饰 5-07 文本阴影 5-08 文本属性重置 5-01 字间距 ...

  8. 程序设计与算法三~C++面向对象程序设计~北大郭炜MOOC学习笔记~第五章:继承与派生(新标准C++程序设计)

    以下内容为笔者手打,望读者珍惜,如有转载还请注明. 第五章 继承与派生 $5.1 继承与派生的概念 $5.1.1 基本概念     在C++中,当定义一个新的类B时,如果发现类B拥有某个已经写好的类A ...

  9. 《谁说菜鸟不会数据分析》学习笔记 第五章数据分析

    第五章 数据分析 5.1 数据分析方法 现状分析 5.1.1 对比分析法 5.1.2 分组分析法 5.1.3 结构分析法 5.1.4 分布分析法 5.1.5 交叉分析法 5.1.6 RFM分析法 5. ...

最新文章

  1. WPF第一章(XAML前台标记语言(Chapter02代码讲解))
  2. Nginx配置文件nginx.conf详解(转)
  3. vite打包编译后类名混淆冲突问题处理
  4. [原创]FineUI秘密花园(二十七) — 窗体控件概述(上)
  5. java jive歌词_Java Jive_Manhattan Transfer with Phil Collins_高音质在线试听_Java Jive歌词|歌曲下载_酷狗音乐...
  6. php实现凯撒密码加密算法,Python实现的凯撒密码算法示例
  7. Leetcode每日一题:134.gas-station(加油站)
  8. Mac终端文件类型显示不同颜色
  9. DPDK设备驱动的匹配和初始化
  10. GDI+学习及代码总结之------画刷Brush .
  11. Javascript遍历map集合以及map对象
  12. eplan 电箱布局_Eplan 的从头到尾完成3D布局步骤说明
  13. 猜名人读心术作业C语言答案,读心术游戏,读心术游戏猜人物
  14. Mac Mini - 一个深坑
  15. 第一章 阿里大数据产品体系
  16. 一篇文章搞懂MongoDB数据库
  17. 这341句话,这么美,那么伤。
  18. 虚拟直播需要哪些设备?如何搭建虚拟直播团队?
  19. 诸葛亮司马懿大战房市
  20. 鼠标移动到图片上实现图片的放大缩小

热门文章

  1. 自由群,外代数和泛包络代数
  2. 从 0 到 1 实现 React 系列 —— 2.组件和 state|props
  3. 几款流行的HTML5 UI 框架比较
  4. 《训练指南》——8.5
  5. C++ Primer 第十六章 模板与范型编程
  6. 牛客网(剑指offer) 第三题 输入一个链表,从尾到头打印链表每个节点的值。
  7. HarmonyOS之深入解析服务卡片的使用
  8. 根据使用频率为 5 个字符设计的哈夫曼编码不可能是( )
  9. hive -e ,hive -f 用法
  10. LeetCode Algorithm 3. 无重复字符的最长子串