C++-基于多设计模式下的同步&异步日志系统
全是通俗易懂的讲解,如果你本节之前的知识都掌握清楚,那就速速来看我的项目笔记吧~
(全文手敲,受益良多) 本人也是做到项目了,后面会日更项目笔记。
我们要实现日志系统,先了解
什么是日志?
程序运行过程中所记录的程序运行的状态信息。为什么要这些信息呢?便于程序员对程序运行状况判断,进而分析,修改代码子类的。所以
日志作用:记录了程序运行状态信息,便于程序员能够随时根据状态信息,对系统的运行状态,进行分析
项目功能:能够让用户非常简便的进行日志的输出,及其控制。
一.项目介绍
本项目主要实现一个日志系统,其主要支持以下功能:
- 支持多级别日志消息:(级别有:调试,提示,警告,错误,致命,不同级别对应不同场景:例如调试级别的日志不输出…)
- 支持同步日志和异步日志:(同步指的是该工作由自己干,异步指的是该工作由别人干,自己等结果就行)同步日志指的是将日志写入到文件或数据库中去,这个操作由业务线程自己完成。异步日志指的是,将日志放到内存中去,由于担心磁盘满了,或者数据库有问题导致程序卡住,业务无法做了,所以自己并不写入到文件或数据库中。而是让其他工作线程专门把日志进行实际输出。
- 支持可靠写入日志到控制台、文件以及滚动文件中:(也就是支持日志的不同落地方向,除了将日志标准输出打印,还支持将日志写入指定文件中去。还有一种叫滚动文件:如果我们将日志一直向一个文件写入,文件将变得很大,会占用大量的磁盘空间,把该文件删除又担心有重要信息。所以提出一种思想:文件的切换输入,具体实现例子:如果一个文件大小达到 1GB,就切换下一个文件,根据大小区分文件,当然,也可以根据日期进行切换。这样清理起来就方便多了 (可以支持控制台、文件以及滚动文件三种同时进行,落地方向提供给你,想怎么输出,由用户自己决定)
- 支持多线程程序并发写日志:(线程安全的日志系统,当多个线程同时向同一个文件进行写入的时候,会引发线程安全问题,我们的项目解决了这个问题)
- 支持扩展不同的日志落地目的地:(如果你想让日志写入数据库,服务器都是可以的,支持扩展,进行创建)
开发环境
- CentOS7
- vscode/vim
- g++/gdb
- Makefile
核心技术
- 类层次设计 (继承和多态的应用)
- C++11 (多线程、auto、智能指针、右值引用等)
- 双缓冲区
- 生产消费模型
- 多线程
- 设计模式 (单例、工厂、代理、建造者等)
我们的项目是功能型的,而不是业务型的。我们来看看性能:
,云服务器问题,实际更高
详细了解日志系统能干什么?为什么要有日志系统?实现日志系统的技术?思想有何不同?
为什么需要日志系统?当出现以下情况时
日志指的是系统在运行过程中所记录的状态信息,日志系统则把日志信息记录到指定的位置。
- 生产环境的产品为了保证其稳定性及安全性是不允许开发人员附加调试器去排查问题,(在实际开发环境中,我们调试的时候,不可能整天去看运行信息,而且信息刷新的很快,根本看不过来。所以我们不能通过打印来调试,其次 gdb 调试是不被允许的,任何的产品都有隐私的,gdb 调试会看到所有的东西,没有安全。所有我们只能借助日志系统,将关键节点的状态记录在日志系统里面去,如果项目出问题了,打开文件分析哪些文件导致的(从普通错误变为了致命错误)可以借助日志系统来打印一些日志帮助开发人员解决问题。
- 上线客户端的产品出现 bug 无法复现并解决,可以借助日志系统打印日志并上传到服务端帮助开发人员进行分析。
- 对于一些高频操作(如定时器、心跳包)在少量调试次数下可能无法触发我们想要的行为,通过断点的暂停方式,我们不得不重复操作几十次、上百次甚至更多,导致排查问题效率是非常低下,可以借助打印日志的方式查问题。
- 在分布式、多线程 / 多进程代码中,出现 bug 比较难以定位,可以借助日志系统打印 log 帮助定位 bug。
- 帮助首次接触项目代码的新开发人员理解代码的运行流程。
日志系统的实现思想有哪些?
日志系统的技术实现主要包括三种类型:
- 利用 printf、std::cout 等输出函数将日志信息打印到控制台。(学习阶段的使用方式)
- 对于大型商业化项目,为了方便排查问题,我们一般会将日志输出到文件或者是数据库系统方便查询和分析日志,主要分为同步日志和异步日志方式
- 同步写日志
- 异步写日志
同步写日志
同步日志是指当输出日志时,必须等待日志输出语句执行完毕后,才能执行后面的业务逻辑语句(必须完成了,才可以进行下一步),日志输出语句与程序的业务逻辑语句将在同一个线程运行。每次调用一次打印日志 API 就对应一次系统调用 write 写日志文件。
缺陷:在高并发场景下,随着日志数量不断增加,同步日志系统容易产生系统瓶颈:
(写给文件时可能会有阻塞,发送给服务器可能有拥塞,这是会卡在写日志这里,无法向后运行啦)
- 一方面,大量的日志打印陷入等量的 write 系统调用,有一定系统开销.
- 另一方面,使得打印日志的进程附带了大量同步的磁盘 IO,影响程序性能
异步写日志
异步日志是指在进行日志输出时,日志输出语句与业务逻辑语句并不是在同一个线程中运行,而是有专门的线程用于进行日志输出操作。
在上述关于异步日志的描述中,以下方面体现了生产消费者模型:
- 生产者角色:业务线程在执行过程中产生需要记录的日志信息,它将这些日志信息放到内存缓冲区中。就如同生产线上制造产品的环节,业务线程不断 “生产” 出日志数据,而不需要等待日志落地等后续操作,直接继续执行后续业务逻辑,所以业务线程充当了生产者的角色。
- 消费者角色:有专门的日志线程负责从内存缓冲区中读取日志信息,并将其进行落地操作(比如写入文件等)。这类似于在生产消费体系中,消费者从特定的存储区域获取产品并进行使用或处理,日志线程在这里就是消费者,它从内存缓冲区(相当于存储区域)中获取日志数据(产品)并进行处理(落地操作)。
- 缓冲区:内存缓冲区起到了类似于生产消费者模型中产品暂存区域的作用。业务线程生产的日志数据先放到这里,日志线程再从这里获取数据,它解耦了生产者(业务线程)和消费者(日志线程)的直接联系,使得两者可以以不同的速度和节奏工作,符合生产消费者模型中通过缓冲区协调生产和消费速度差异的特点。
综上所述,通过业务线程、日志线程以及内存缓冲区之间的这种关系,清晰地体现了生产消费者模型。
这样做的好处是即使日志没有真的地完成输出也不会影响程序的主业务,可以提高程序的性能:
- 主线程调用日志打印接口成为非阻塞操作
- 同步的磁盘 IO 从主线程中剥离出来交给单独的线程完成
如果你对日志系统感到兴趣,欢迎关注我👉【A charmer】
后续我将继续带你实现日志系统~