一、前言

对于任何一个服务器而言,日志系统的设计是非常重要的,尝试设计一个简易的同步异步日志系统来完成系统日志的记录。

二、基础知识

日志,由服务器自动创建,并记录运行状态,错误信息,访问数据的文件。

同步日志,日志写入函数与工作线程串行执行,由于涉及到I/O操作,当单条日志比较大的时候,同步模式会阻塞整个处理流程,服务器所能处理的并发能力将有所下降,尤其是在峰值的时候,写日志可能成为系统的瓶颈。

生产者-消费者模型,并发编程中的经典模型。以多线程为例,为了实现线程间数据同步,生产者线程与消费者线程共享一个缓冲区,其中生产者线程往缓冲区中push消息,消费者线程从缓冲区中pop消息。

任何时刻,只能有一个生产者或消费者可以访问缓冲区

阻塞队列,将生产者-消费者模型进行封装,使用循环数组实现队列,作为两者共享的缓冲区。

push成员是生产者,pop成员是消费者。

异步日志,将所写的日志内容先存入阻塞队列,写线程从阻塞队列中取出内容,写入日志。

单例模式,最简单也是被问到最多的设计模式之一,保证一个类只创建一个实例,同时提供全局访问的方法

三、 单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

单例模式有两种实现方法,分别是懒汉和饿汉模式。顾名思义,懒汉模式,即非常懒,不用的时候不去初始化,所以在第一次被使用时才进行初始化;饿汉模式,即迫不及待,在程序运行时立即初始化。

实现思路:私有化它的构造函数,以防止外界创建单例类的对象;使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例.

1 为什么要用双检测,只检测一次不行吗?

如果只检测一次,在每次调用获取实例的方法时,都需要加锁,这将严重影响程序性能。双层检测可以有效避免这种情况,仅在第一次创建单例的时候加锁,其他时候都不再符合NULL == p的情况,直接返回已创建好的实例。

2 局部静态变量之线程安全懒汉模式

前面的双检测锁模式,写起来不太优雅,《Effective C++》(Item 04)中的提出另一种更优雅的单例模式实现,使用函数内的局部静态对象,这种方法不用加锁和解锁操作。

3 为什么要把调用线程放入条件变量的请求队列后再解锁?

线程是并发执行的,如果在把调用线程A放在等待队列之前,就释放了互斥锁,这就意味着其他线程比如线程B可以获得互斥锁去访问公有资源,这时候线程A所等待的条件改变了,但是它没有被放在等待队列上,导致A忽略了等待条件被满足的信号。

倘若在线程A调用pthread_cond_wait开始,到把A放在等待队列的过程中,都持有互斥锁,其他线程无法得到互斥锁,就不能改变公有资源。

四、日志系统的运行机制

步骤:

1:单例模式(局部静态变量懒汉方法)获取实例

2:主程序一开始Log::get_instance()->init()初始化实例。初始化后:服务器启动按当前时刻创建日志(前缀为时间,后缀为自定义log文件名,并记录创建日志的时间day和行数count)。如果是异步(通过是否设置队列大小判断是否异步,0为同步),工作线程将要写的内容放进阻塞队列,还创建了写线程用于在阻塞队列里取出一个内容(指针),写入日志。

3:其他功能模块调用write_log()函数写日志。(write_log:实现日志分级、分文件、按天分类,超行分类的格式化输出内容。)里面会根据异步、同步实现不同的写方式。

  • 日志文件

    • 局部变量的懒汉模式获取实例
    • 生成日志文件,并判断同步和异步写入方式
  • 同步
    • 判断是否分文件
    • 直接格式化输出内容,将信息写入日志文件
  • 异步
    • 判断是否分文件(通过队列的大小来决定)
    • 格式化输出内容,将内容写入阻塞队列,创建一个写线程,从阻塞队列取出内容写入日志文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SHMGi15j-1654089038207)(https://mmbiz.qpic.cn/mmbiz_jpg/6OkibcrXVmBEOjicsa8vpoLAlODicrC7AoM1h2eq9sDMdQY8TNYQoVckCRDd0m8SDH1myuB4gEJfejvznfZuJ3cpQ/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1)]

同步和异步日志的处理代码

    // 若异步,则将日志信息加入阻塞队列,同步则加锁向文件中写if (m_is_async && !m_log_queue->full()){m_log_queue->push(log_str);}else{m_mutex.lock();fputs(log_str.c_str(), m_fp);m_mutex.unlock();}

五、重点知识

1 同步异步日志是怎么实现的?(CVTE)

在C++编写服务器的时候,涉及到Io操作的时候,会阻塞整个线程,同步日志可能比较简单,但是异步日志的话就需要注意一下,我们将所写的内容存入阻塞队列,创建写线程从阻塞队列中读取出内容,写入日志。

将消费者和生产者模式封装成阻塞队列。

2 日志的分级和分文件

  • Debug,调试代码时的输出,在系统实际运行时,一般不使用。
  • Warn,这种警告与调试时终端的warning类似,同样是调试代码时使用。
  • Info,报告系统当前的状态,当前执行的流程或接收的信息等。
  • Erro,输出系统的错误信息

超行、按天分文件逻辑,具体的,

  • 日志写入前会判断当前day是否为创建日志的时间,行数是否超过最大行限制
    • 若为创建日志时间,写入日志,否则按当前时间创建新log,更新创建时间和行数
    • 若行数超过最大行限制,在当前日志的末尾加count/max_lines为后缀创建新log

3 针对高并发情况下,写线程数量不足,如何处理

之前被问到的一个很好的问题,现在还没有一个很好的解决办法,后期如果有新的思路,会补一补

如果大佬们有好的建议,非常期待你们的答疑,麻烦在下方评论区中解答,非常感谢!

六、总结

同步异步日志系统,其中异步日志系统主要是解决单条日志过大造成的问题,日志系统设计模块的学习还需要不断的进行,这对于服务器开发者来说是非常重要的。

七、参考资料

  • 《两猿社》
  • 《Linux高性能服务器》
  • 最新版Web服务器项目详解 - 09 日志系统(上)
  • 最新版Web服务器项目详解 - 10 日志系统(下)
  • muduo第五章:高效的多线程日志

C++ LinuxWebServer项目(5)同步异步日志系统相关推荐

  1. 多线程异步日志系统,高效、强悍的实现方式-双缓冲

    作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++.嵌入式.Linux. 目录 文章目录 单片机中常用的环形缓冲区 多线程异步日志:双缓冲机制 双缓冲机制为什么高效 尽可能的降低 Lock 的时 ...

  2. 项目准备三之日志系统使用

    项目准备三之日志系统使用 前言 一.Linux日志 1.1 系统常用的日志 1.2 日志管理服务 rsyslog 1.3 日志级别 二.zlog安装 三.zlog简单使用 3.1 全局参数[globa ...

  3. 仿牛客网项目第五,六章:异步消息系统和分布式搜索引擎(详细步骤和思路)

    目录 1. Kafka:构建TB级异步消息系统 1.0 同步/异步消息的区别 1.1 项目的目的 1. 2 阻塞队列实现异步消息系统 1.4 Kafka入门 1.5 Spring整合Kafka 1.6 ...

  4. linux日志系统的实现,一个同步日志系统的简单实现 log for c (linux 平台)

    在一个项目中需要使用日志记录,网上也有很多开源代码,自己也尝试着写了一个!异步日志系统正在进行中. //mylog.h 头文件 #ifndef _MYLOG_HEADER_ #define _MYLO ...

  5. cx_oracle写日志信息_日志系统的设计

    笔者在写作本章节的时候,并不敢把此章节的标题叫做<高性能日志系统的设计>,之所以不敢加上"高性能"三个字的原因是: 第一,我对于日志系统设计知识和经验都来自于学习和工作 ...

  6. 使用XLog、Spring-Boot、And-Design-Pro搭建日志系统

    一.前言:移动端为什么要三方日志系统 日志系统用于记录用户行为和数据以及崩溃时的线程调用栈,以帮助程序员解决问题,优化用户体验. iOS系统就有自带Crash收集应用程序"ReportCra ...

  7. 04. 项目博客之日志

    项目博客之日志 系统没有日志,就等于人没有眼睛 第一,访问日志 access log(server 端最重要的日志) 第二,自定义日志(包括自定义事件.错误记录等) 1. nodejs 文件操作 文件 ...

  8. C++系统日志库精选:深入剖析glog与log4cplus,轻松搭建高效日志系统

    目录标题 引言 日志系统的重要性 glog与log4cplus简介与应用场景 C++日志系统库的选择依据 glog基础知识 glog库的关键功能 glog库的安装与使用 glog库的基本日志功能与级别 ...

  9. 异步日志系统设计demo

    目录 简单版本1 优化版本1 优化版本2 对于QPS要求很高或者对性能有一定要求的服务器程序,同步写日志会对服务的关键性逻辑的快速执行和及时响应带来一定的性能损失,因为写日志时等待磁盘IO完成工作也需 ...

最新文章

  1. Tomcat 和 JVM 的性能调优总结
  2. 非常值得一看—九种滤波算法C语言实现
  3. ThinkPHP 的URL重写时遇到No input file specified的解决方法
  4. 模式窗体中调用父页面Javascript
  5. 15 个第三方Web 表单资源
  6. Hadoop学习之路(十三)MapReduce的初识
  7. 6个web前端核心技术,你学到哪个了呢?
  8. 闪退没由报错_使命召唤:(cod16)出现的闪退问题以及解决办法
  9. 开启了ADB,就等于配了一把家门钥匙给别人!
  10. 大数据Hadoop学习记录(4)----基于JAVA的HDFS文件操作
  11. jQuery基础之jQuery和原生js实现tab选项卡和电影排行榜
  12. python分组求和_利用pandas进行分组求和
  13. local class incompatible: stream classdesc serialVersionUID = -4601057296962918535, local class seri
  14. Mac设置右键用VSCode打开文件和文件夹
  15. 植被覆盖指数计算教程(ENVI)
  16. Wincc服务器右下RT状态,关于wincc rt
  17. matlab矩阵赋未知数,matlab设未知数
  18. 组合数据类型练习,英文词频统计实例上
  19. vue H5页面调用手机相机拍照/图库上传
  20. Weblogic Server打补丁方法步骤

热门文章

  1. 【简历优化】项目经验问什么怎么写怎么答
  2. 第35课时_电荷泵电路
  3. 吴军 阅读与写作50讲 发刊词 读后感
  4. 日语学习资料-考研日语203历年真题合集(1993-2012年)
  5. 追溯系统溯源二维码防伪码生成图片源代码
  6. The server time zone value '?й???????' is unrecognized or represents more than one time zone. You mu
  7. Atom 扩展包 手动 安装
  8. 每天10分钟——10.18
  9. 传入收据提报年月,某个时间段内哪些时间没有提报收据,传入list存入的格式(2017.01-2019.05)
  10. Linux端NDS模拟器DeSmuME添加金手指