第五章 高效的多线程日志

  • 日志有两种意思:

    • 诊断日志

    • 交易日志

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

  • 日志通常要记录:

    • 收到的每条消息的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. 简明docker教程
  2. SpreadJS 类Excel表格控件 - V12 新特性详解
  3. 正式迁移博客到osc
  4. 微软Office Online服务安装部署及wopi代码实现--------域控制服务器安装
  5. 什么是分布式锁?redis、zookeeper、etcd实现分布式锁有什么不同之处?
  6. 【面试相关】非计算机专业如何1年内自学拿到算法offer
  7. iOS之NSAttributedString-------字符属性
  8. /opt/hbase/conf 中不能启动hbase_Hbase从入门到入坑
  9. 【10】jQuery学习——入门jQuery选择器之表单选择器
  10. 从汇编代码的角度观察switch与if...else,乘除与移位的差别
  11. 10.React中的组件、父子组件、React props父组件给子组件传值、子组件给父组件传值、父组件中通过refs获取子组件属性和方法...
  12. 基于Metronic的Bootstrap开发框架--工作流模块功能介绍(2)
  13. vs2008 SP1 安装问题小解决方案
  14. 如何快速取消大量的合并单元格并向下填充数据
  15. 手机输入法哪家好用?用户目前最喜爱这4款,有你正在使用的吗
  16. Linux CentOS 系统实战笔记-基础篇
  17. 初学verilog必看
  18. Writing an LLVM Pass
  19. HDU 1686 [KMP]
  20. 指针之野指针系列(2):如何规避野指针

热门文章

  1. 七、数据库技术基础(一)
  2. 转帖Jmeter中的几个重要测试指标释义
  3. 图---Dijstra
  4. hdu 3047 Zjnu Stadium(并查集)
  5. 基于C#在WinCE6.0系统SQLCE3.5的安装开发使用
  6. 1463. Happiness to People!
  7. fatal error C1083: Cannot open include file: 'ceconfig.h': No such file or directory
  8. ABAP--如何实现合计字段分单位合计
  9. 【数据结构与算法】之深入解析“石子游戏III”的求解思路与算法示例
  10. 2020年第十一届蓝桥杯 - 国赛 - Python大学组 - H.答疑