muduo日志库原理以及源码分析

article/2025/11/6 19:10:57

muduo日志库特点

  • 日志批量写入
  • 批量唤醒写线程
  • 写日志用notify+wait_timeout 方式触发日志的写入
  • 锁的粒度,双缓冲,双队列
  • buffer默认 4M 缓冲区, buffers 是 buffer 队列, push 、 pop 时使用 move 语义 减少内存拷贝

muduo的这些特点使得其作为高性能日志库被广泛使用。

muduo日志库异步机制

muduo为了保证日志的写入速度快,采用异步机制处理日志信息。多个线程将日志信息保存在数据buffer中,等buffer装满了再将buffer放入日志队列,通过锁和条件变量保证日志队列的数据安全,最后由日志落盘线程将日志队列中的数据写入磁盘中。

在这里插入图片描述

日志写入固定缓冲区程序

void AsyncLogging::append(const char* logline, int len)
{// if(cnt++ == 50000)abort();MutexLockGuard lock(mutex_);    // 多线程加锁if (currentBuffer_->avail() > len)    // 判断buffer还有没有空间写入这条日志{currentBuffer_->append(logline, len); // 直接写入}else{buffers_.push_back(std::move(currentBuffer_));    // buffers_是vector,把buffer入队列// printf("push_back append_cnt:%d, size:%d\n", ++append_cnt, buffers_.size());if (nextBuffer_)  // 用了双缓存{currentBuffer_ = std::move(nextBuffer_);    // 如果不为空则将buffer转移到currentBuffer_}else{// 重新分配buffercurrentBuffer_.reset(new Buffer); // Rarely happens如果后端写入线程没有及时读取数据,那要再分配buffer}currentBuffer_->append(logline, len);   // buffer写满了cond_.notify(); // 唤醒写入线程}
}

日志写入磁盘程序

void AsyncLogging::threadFunc()
{assert(running_ == true);latch_.countDown();LogFile output(basename_, rollSize_, false);BufferPtr newBuffer1(new Buffer); // 是给currentBuffer_BufferPtr newBuffer2(new Buffer); // 是给nextBuffer_newBuffer1->bzero();newBuffer2->bzero();BufferVector buffersToWrite;    // 保存要写入的日志buffersToWrite.reserve(16);while (running_){assert(newBuffer1 && newBuffer1->length() == 0);assert(newBuffer2 && newBuffer2->length() == 0);assert(buffersToWrite.empty());{ // 锁的作用域MutexLockGuard lock(mutex_);if (buffers_.empty())  // 没有数据可读取,休眠{// printf("waitForSeconds into\n");cond_.waitForSeconds(flushInterval_);   // 超时退出或者被唤醒(收到notify)// printf("waitForSeconds leave\n");}buffers_.push_back(std::move(currentBuffer_));  // currentBuffer_被锁住  currentBuffer_被置空// printf("push_back threadFunc:%d, size:%d\n", ++threadFunc_cnt, buffers_.size());currentBuffer_ = std::move(newBuffer1); // currentBuffer_ 需要内存空间buffersToWrite.swap(buffers_);          // 用了双队列,把前端日志的队列所有buffer都转移到buffersToWrite队列if (!nextBuffer_)     // newBuffer2是给nextBuffer_{nextBuffer_ = std::move(newBuffer2);  // 如果为空则使用newBuffer2的缓存空间}}// 从这里是没有锁,数据落盘的时候不要加锁assert(!buffersToWrite.empty());// fixme的操作 4M一个buffer *25 = 100Mif (buffersToWrite.size() > 25)  // 这里缓存的数据太多了,比如4M为一个buffer空间,25个buffer就是100M了。{printf("Dropped\n");char buf[256];snprintf(buf, sizeof buf, "Dropped log messages at %s, %zd larger buffers\n",Timestamp::now().toFormattedString().c_str(),buffersToWrite.size()-2);    // 只保留2个bufferfputs(buf, stderr);output.append(buf, static_cast<int>(strlen(buf)));buffersToWrite.erase(buffersToWrite.begin()+2, buffersToWrite.end());   // 只保留2个buffer(默认4M)}for (const auto& buffer : buffersToWrite)  // 遍历buffer{// FIXME: use unbuffered stdio FILE ? or use ::writev ?output.append(buffer->data(), buffer->length());    // 负责fwrite数据}output.flush();   // 保证数据落到磁盘了if (buffersToWrite.size() > 2){// drop non-bzero-ed buffers, avoid trashingbuffersToWrite.resize(2);   // 只保留2个buffer}if (!newBuffer1){assert(!buffersToWrite.empty());newBuffer1 = std::move(buffersToWrite.back());    // 复用buffer对象buffersToWrite.pop_back();newBuffer1->reset();    // 重置}if (!newBuffer2){assert(!buffersToWrite.empty());newBuffer2 = std::move(buffersToWrite.back());   // 复用buffer对象buffersToWrite.pop_back();newBuffer2->reset();   // 重置}buffersToWrite.clear(); }output.flush();
}

日志批量写入

日志写入固定缓冲区程序可以看到。muduo采用日志批量写入的形式将日志写入磁盘,具体实现方式是将日志写入一个固定大小的buffer中,等到buffer装满了,或者超时了,再将buffer放到队列中供写入线程写入到磁盘中。

如代码中将日志信息写入currentBuffer_

currentBuffer_->append(logline, len); // 日志内容,日志长度
  void append(const char* /*restrict*/ buf, size_t len){// FIXME: append partiallyif (implicit_cast<size_t>(avail()) > len){memcpy(cur_, buf, len);cur_ += len;}}

批量唤醒写线程

日志写入固定缓冲区程序可以看到。当currentBuffer_写满了,或者超时了,会通过条件变量唤醒写线程将数据写入磁盘。

cond_.notify(); // 唤醒写入线程

双缓冲区技术

日志写入磁盘程序中,新建了一个buffersToWrite的缓冲区用来用来保存日志队列buffers_中的信息。

 buffersToWrite.swap(buffers_);          // 用了双队列,把前端日志的队列所有buffer都转移到buffersToWrite队列

这样做到好处在于:

  • 线程安全;
  • 非阻塞。

这样,buffersToWrite写入磁盘时,日志写入固定缓冲区程序仍然可以将数据写入缓冲区,不需要等待前面日志的写入完成提醒。

muduo的使用

#define LOG_TRACE if (Logger::logLevel() <= Logger::TRACE) \Logger(__FILE__, __LINE__, Logger::TRACE, __func__).stream()
#define LOG_DEBUG if (Logger::logLevel() <= Logger::DEBUG) \Logger(__FILE__, __LINE__, Logger::DEBUG, __func__).stream()
#define LOG_INFO if (Logger::logLevel() <= Logger::INFO) \Logger(__FILE__, __LINE__).stream()
#define LOG_WARN Logger(__FILE__, __LINE__, Logger::WARN).stream()
#define LOG_ERROR Logger(__FILE__, __LINE__, Logger::ERROR).stream()
#define LOG_FATAL Logger(__FILE__, __LINE__, Logger::FATAL).stream()
#define LOG_SYSERR Logger(__FILE__, __LINE__, false).stream()
#define LOG_SYSFATAL Logger(__FILE__, __LINE__, true).stream()
inline LogStream& operator<<(LogStream& s, T v)
{s.append(v.str_, v.len_);return s;
}inline LogStream& operator<<(LogStream& s, const Logger::SourceFile& v)
{s.append(v.data_, v.size_);return s;
}

muduo在Logging.cc函数中,通过重载<<和宏定义,使得在写入日志时可以按以下形式操作:

LOG_INFO << "NO." << i << " Root Error Message!";

文章参考与<零声教育>的C/C++linux服务器高级架构系统教程学习


http://chatgpt.dhexx.cn/article/Y2pdBqJS.shtml

相关文章

muduo网络库与服务模型介绍

目录 一、muduo网络库简介 1、特点 2、代码结构 &#xff08;1&#xff09;公共接口 &#xff08;2&#xff09;内部实现 二、muduo线程模型 1、单线程Reactor 2、Reactor线程池 3、one loop per thread 4、one loop per thread 线程池 muduo是陈硕个人使用C开发的一…

muduo 架构解析

muduo是一个基于Reactor模式的C网络库。它采用非阻塞I/O模型&#xff0c;基于事件驱动和回调。我们不仅可以通过muduo来学习linux服务端多线程编程&#xff0c;还可以通过它来学习C11。     Reactor是网络编程的一般范式。我们这里从reactor模式为出发点&#xff0c;根据R…

muduo库介绍

muduo库是一个多线程服务器开发库 muduo 作者陈硕&#xff0c;现在在美国加州硅谷某互联网大公司工作&#xff0c;从事大规模分布式的可靠系统工程。这个库是作者多年工作的总结&#xff0c;可以说大家学通了这个库&#xff0c;找一份Linux服务器开发的工作是没问题的&#xf…

C++ muduo网络库知识分享01 - Linux平台下muduo网络库源码编译安装

Muduo is a multithreaded C network library based on the reactor pattern. muduo库的介绍就是&#xff1a;一个基于reactor反应堆模型的多线程C网络库。 muduo网络库是C语言开发的一个非常优秀的网络库&#xff0c;作者陈硕&#xff0c;muduo网络库在多线程环境下性能非常高…

遗传算法示例

遗传的概念&#xff1a; 遗传算法是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型&#xff0c;是一种通过模拟自然进化过程搜索最优解的方法。 遗传算法的特点&#xff1a; 对于搜索算法的共同特征有 首先组成一组候选解。依据某些使用性条件测算这些…

10分钟搞懂遗传算法

大自然有种神奇的力量&#xff0c;它能够将优良的基因保留下来&#xff0c;从而进化出更加强大、更加适合生存的基因。遗传算法便基于达尔文的进化论&#xff0c;模拟了自然选择&#xff0c;物竞天择、适者生存&#xff0c;通过N代的遗传、变异、交叉、复制&#xff0c;进化出问…

遗传算法简单实例

遗传算法的手工模拟计算示例 为更好地理解遗传算法的运算过程&#xff0c;下面用手工计算来简单地模拟遗传算法的各 个主要执行步骤。 例&#xff1a;求下述二元函数的最大值&#xff1a; (1) 个体编码 遗传算法的运算对象是表示个体的符号串&#xff0…

遗传算法(基础知识)

遗传算法&#xff08;基础知识&#xff09; 遗传算法简称GA&#xff08;Genetic Algorithms&#xff09;模拟自然界生物遗传学&#xff08;孟德尔&#xff09;和生物进化论&#xff08;达尔文&#xff09;通过人工方式所构造的一类 并行随机搜索最优化方法&#xff0c;是对生物…

遗传算法概念、步骤、应用解析(案例直白--黄含驰)

遗传算法 ①  在几十亿年的演化过程中&#xff0c;自然界中的生物体已经 形成了一种优化自身结构的内在机制&#xff0c;它们能够不 断地从环境中学习&#xff0c;以适应不断变化的环境  对于大多数生物体&#xff0c;这个过程是通过自然选择和有性生殖来完成的。自然选择…

遗传算法超详细图解

遗传算法&#xff08;Genetic Algorithm&#xff09;顾名思义&#xff0c;是一种基于自然选择原理和自然遗传机制的启发式搜索算法。该算法通过模拟自然界中生物遗传进化的自然机制&#xff08;选择、交叉和变异操作&#xff09;&#xff0c;将好的遗传基因&#xff08;最优目标…

遗传算法及其应用

一、遗传算法的定义 遗传算法的基本思想是参考生物学中物种“物竞天择&#xff0c;适者生存”的思想。在计算机中&#xff0c;通过给定约束条件&#xff0c;使初始参数不断迭代&#xff0c;从而接近最优解。 遗传算法可描述为&#xff1a; Initialize population process-chr…

详解遗传算法(含MATLAB代码)

目录 一、遗传算法概述 二、遗传算法的特点和应用 三、遗传算法的基本流程及实现技术 3.1 遗传算法的基本流程 3.2 遗传算法的实现技术 1.编码 2.适应度函数 3.选择算子 4.交叉算子 5.变异算子 6.运行参数 四、遗传算法的基本原理 4.1 模式定理 4.2 积木块假设 …

遗传算法步骤

遗传算法是一种模拟生物自然进化的一种算法&#xff0c;通话对生物进化的模拟&#xff0c;实现对数值函数的模拟计算。它主要分为四个步骤&#xff1a;初始化、杂交、变异和选择。相关实现可参考https://github.com/ShaquallLee/evolutionary-programming/tree/master/aEP 1、…

遗传算法原理+程序案例详解

注明&#xff1a;这篇遗传算法程序我在网上看到多次&#xff0c;很多人在转载时&#xff0c;都称已经修改了错误的地方&#xff0c;程序在matlab上能够运行。 当我在学习这段程序时&#xff0c;发现结果仍存在很大问题(不稳定、不准确)。我一行一行看时&#xff0c;发现不仅有少…

遗传算法、遗传算法库函数ga和gamultiobj、遗传算法工具箱GOT实例介绍

目录 前言 适应度函数和目标函数的关系 1. 常规遗传算法 2.结合非线性规划fmincon函数的遗传算法 2.1 fmincon非线性规划函数使用 2.2 结合非线性规划fmincon函数的遗传算法使用及示例 2.2.1 编码 2.2.2 选择 2.2.3交叉 2.2.4变异 2.2.5非线性规划fmincon函数 2.2.…

遗传算法原理与应用详解

遗传算法 ( GA , Genetic Algorithm ) &#xff0c;也称进化算法 。 遗传算法是受达尔文的进化论的启发&#xff0c;借鉴生物进化过程而提出的一种启发式搜索算法。因此在介绍遗传算法前有必要简单的介绍生物进化知识。 一.进化论知识 作为遗传算法生物背景的介绍&#xff0…

遗传算法(一) 遗传算法的基本原理

遗传算法&#xff08;一&#xff09;遗传算法的基本原理 1.概述 遗传算法&#xff08;Genetic Algorithm, GA&#xff09;起源于对生物系统所进行的计算机模拟研究。它是模仿自然界生物进化机制发展起来的随机全局搜索和优化方法&#xff0c;借鉴了达尔文的进化论和孟德尔的遗…

算法理解-遗传算法(Genetic Algorithm)(一个带计算过程的例子)

想要快速的了解一个算法&#xff0c;最好的方式便是拿个例子手动进行实现算一遍。这里借鉴了网络上的一个例子&#xff0c;求解如下的一个函数&#xff1a; f(x)x∗sin(10∗π∗x)2x∈[−1,2] f(x) = x*sin(10*\pi*x)+2 \\ x \in[-1, 2] 其函数图像为&#xff1a; 例子来源&a…

遗传算法及python实现

目录 一、遗传算法概念 二、遗传算法应用实例 基础概念&#xff1a; 1、种群和个体&#xff1a; 2、编码、解码与染色体&#xff1a; 3、适应度和选择&#xff1a; 4、 交叉、变异&#xff1a; 三、遗传算法python完整代码 “适者生存&#xff0c;不适者淘汰” …

遗传算法详解python代码实现以及实例分析

遗传算法 文章目录 遗传算法前言一、遗传算法是什么&#xff1f;二、实例讲解例题11.初始化种群2.优胜劣汰3.根据优胜劣汰的结果&#xff0c;交配生殖、变异5.生物遗传进化 例题21.初始化参数2.定义环境&#xff08;定义目标函数&#xff09;3.DNA解码&#xff08;计算x&#x…