muduo的高性能异步日志

2023-11-13

1、一个日志库大体可分为前端(frontend)与后端(backend)。前端是供应用程序使用的接口(API),并生成日志信息;后端则是负责将日志信息写到目的地。每个线程都有自己的前端,而整个程序共用一个后端。对于生产者(前端)而言,要尽量做到低延迟、低CPU开销、无阻塞;对消费者(后端)而言,要尽量做到足够大的吞吐量,并占用较少的资源。

    对C++程序而言,最好整个程序(包括主程序和程序库)都使用相同的日志库,程序有一个整体的日志输出,而不要每个组件都有各自的日志输出。从这个意义上讲,日志库是个singleton。

    异步日志:每个【进程】最好只写一个日志文件,这样分析日志文件更容易,不必再多个文件中跳来跳去。实现方法是,用一个背景线程负责收集日志消息,并写入日志文件,其他业务线程只管往这个“日志线程”发送日志消息,这称为“异步日志”。

    muduo日志库采用的是双缓冲技术,基本思路是准备两块缓冲:A与B,前端负责往buffer A中填数据(日志消息),后端负责将buffer B中的数据写入文件。当buffer A写满之后,交换A与B,让后端将buffer A中的数据写入文件,而前端负责往buffer B中填入新的日志文件。如此往复。用两个buffer的好处是在新建日志消息的时候不必等待磁盘文件操作,也避免每条消息都触发(唤醒)了后端日志线程。换言之,前端不是将一条条消息分别传送给后端,而是将多个日志消息拼成一个大的buffer传送给后端,相当于批处理,减少了线程唤醒的频率,降低了开销。另外,为了及时将消息写入文件,即使前端的buffer A未写满,日志库也会每三秒执行一次上述交换写入操作。

【.h】

#ifndef MUDUO_BASE_ASYNCLOGGING_H
#define MUDUO_BASE_ASYNCLOGGING_H

#include <muduo/base/BlockingQueue.h>
#include <muduo/base/BoundedBlockingQueue.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/base/Mutex.h>
#include <muduo/base/Thread.h>
#include <muduo/base/LogStream.h>

#include <atomic>
#include <vector>

namespace muduo
{

class AsyncLogging : noncopyable
{
 public:

  AsyncLogging(const string& basename,
               off_t rollSize,
               int flushInterval = 3);

  ~AsyncLogging()
  {
    if (running_)
    {
      stop();
    }
  }

  void append(const char* logline, int len);

  void start()
  {
    running_ = true;
    thread_.start();
    latch_.wait();
  }

  void stop()
  {
    running_ = false;
    cond_.notify();
    thread_.join();
  }

 private:

  void threadFunc();

  typedef muduo::detail::FixedBuffer<muduo::detail::kLargeBuffer> Buffer;
  typedef std::vector<std::unique_ptr<Buffer>> BufferVector;
  typedef BufferVector::value_type BufferPtr;

  const int flushInterval_;
  std::atomic<bool> running_;
  string basename_;
  off_t rollSize_;
  muduo::Thread thread_;
  muduo::CountDownLatch latch_;
  muduo::MutexLock mutex_;
  muduo::Condition cond_;
  BufferPtr currentBuffer_;
  BufferPtr nextBuffer_;
  BufferVector buffers_;
};

}
#endif  // MUDUO_BASE_ASYNCLOGGING_H

【.cpp】

#include <muduo/base/AsyncLogging.h>
#include <muduo/base/LogFile.h>
#include <muduo/base/Timestamp.h>

#include <stdio.h>

using namespace muduo;

AsyncLogging::AsyncLogging(const string& basename,
                           off_t rollSize,
                           int flushInterval)
  : flushInterval_(flushInterval),
    running_(false),
    basename_(basename),
    rollSize_(rollSize),
    thread_(std::bind(&AsyncLogging::threadFunc, this), "Logging"),
    latch_(1),
    mutex_(),
    cond_(mutex_),
    currentBuffer_(new Buffer),
    nextBuffer_(new Buffer),
    buffers_()
{
  currentBuffer_->bzero();
  nextBuffer_->bzero();
  buffers_.reserve(16);
}

void AsyncLogging::append(const char* logline, int len)
{
  muduo::MutexLockGuard lock(mutex_);
  if (currentBuffer_->avail() > len)
  {
    currentBuffer_->append(logline, len);
  }
  else
  {
    buffers_.push_back(std::move(currentBuffer_));

    if (nextBuffer_)
    {
      currentBuffer_ = std::move(nextBuffer_);
    }
    else
    {
      currentBuffer_.reset(new Buffer); // Rarely happens
    }
    currentBuffer_->append(logline, len);
    cond_.notify();
  }
}

void AsyncLogging::threadFunc()
{
  assert(running_ == true);
  latch_.countDown();
  LogFile output(basename_, rollSize_, false);
  BufferPtr newBuffer1(new Buffer);
  BufferPtr newBuffer2(new Buffer);
  newBuffer1->bzero();
  newBuffer2->bzero();
  BufferVector buffersToWrite;
  buffersToWrite.reserve(16);
  while (running_)
  {
    assert(newBuffer1 && newBuffer1->length() == 0);
    assert(newBuffer2 && newBuffer2->length() == 0);
    assert(buffersToWrite.empty());

    {
      muduo::MutexLockGuard lock(mutex_);
      if (buffers_.empty())  // unusual usage!
      {
        cond_.waitForSeconds(flushInterval_);
      }
      buffers_.push_back(std::move(currentBuffer_));
      currentBuffer_ = std::move(newBuffer1);
      buffersToWrite.swap(buffers_);
      if (!nextBuffer_)
      {
        nextBuffer_ = std::move(newBuffer2);
      }
    }

    assert(!buffersToWrite.empty());

    if (buffersToWrite.size() > 25)
    {
      char buf[256];
      snprintf(buf, sizeof buf, "Dropped log messages at %s, %zd larger buffers\n",
               Timestamp::now().toFormattedString().c_str(),
               buffersToWrite.size()-2);
      fputs(buf, stderr);
      output.append(buf, static_cast<int>(strlen(buf)));
      buffersToWrite.erase(buffersToWrite.begin()+2, buffersToWrite.end());
    }

    for (size_t i = 0; i < buffersToWrite.size(); ++i)
    {
      // FIXME: use unbuffered stdio FILE ? or use ::writev ?
      output.append(buffersToWrite[i]->data(), buffersToWrite[i]->length());
    }

    if (buffersToWrite.size() > 2)
    {
      // drop non-bzero-ed buffers, avoid trashing
      buffersToWrite.resize(2);
    }

    if (!newBuffer1)
    {
      assert(!buffersToWrite.empty());
      newBuffer1 = std::move(buffersToWrite.back());
      buffersToWrite.pop_back();
      newBuffer1->reset();
    }

    if (!newBuffer2)
    {
      assert(!buffersToWrite.empty());
      newBuffer2 = std::move(buffersToWrite.back());
      buffersToWrite.pop_back();
      newBuffer2->reset();
    }

    buffersToWrite.clear();
    output.flush();
  }
  output.flush();
}

在 陈硕所著《Linux多线程服务端编程中》,第117~119页,阐述了异步日志前端与后端交互的四种情形。在此不再详述。



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

muduo的高性能异步日志 的相关文章

  • html与python后端交互,python后端中取表单

    参考 http www manongjc com detail 13 owqqwhqvsqworkh html 前端
  • CDZSC_2022寒假个人训练赛21级(1)

    A 题意 略 题解 将n个数加起来的总和除以n即可 include
  • 红帽Linux系统管理员学习哪些内容?

    开源技术现在越来越火 无论是从事DBA 网络运维还是开发 云计算 人工智能等岗位 都需要具备些Linux基础知识 本文主要介绍Redhat Linux系统管理员一般学习哪些内容 Redhat Linux系统管理学习内容 课程概述 一 红帽系
  • 计算机考研经验分享:一战暨南大学(死亡计专),调剂七天上岸华侨大学

    计算机考研经验分享 一战暨南大学 死亡计专 调剂七天上岸华侨大学 前言 这篇文章我本来很早就打算写了 调剂过程只有过来人才懂吧 因此 我希望自己的这篇文章能对看到的人考研有所帮助 我是12号晚上11点左右收到的录取通知 然后13号太兴奋了
  • 使用Element-UI中的Upload控件上传文件 (Vue + Flask)

    知识点 前端 使用 http request 覆盖默认的上传行为 可以自定义上传的实现 使用 DataForm 携带需要上传的文件 需要将http request 的 headers中的content type 设置为 content ty
  • 微信小程序简介

    一 了解微信小程序微信小程序 小程序的一种 英文名Wechat Mini Program 是一种不需要下载安装即可使用的应用 张小龙 发布时间2017年1月9日 二 微信小程序和普通H5的区别1 微信小程序用开发者工具来查看预览页面 H5用
  • R中的统计模型

    R中的统计模型 这一部分假定读者已经对统计方法 特别是回归分析和方差分析有一定的了解 后面我们还会假定读者对广义线性模型和非线性模型也有所了解 R已经很好地定义了统计模型拟合中的一些前提条件 因此我们能构建出一些通用的方法以用于各种问题 R
  • 股票资金建仓分仓补仓计算器

    软件演示图 百度网盘下载地址 http pan baidu com s 1o8Prq6A 软件功能原理与应用价值 我们每个人买股票基本很难做到一买就涨的 买了后可能会下跌一波段再涨 则此就会另到我们时常赚不到钱而纠结卖出一分钱都不能获利而离
  • grep常用需要转义字符汇总

    最近用grep的时候发现转义非常恶心 干脆做个测试 统计一下表示特殊语意时 需要转义的字符 这里的特殊语意是指非匹配自己本身 有特殊含义的时候
  • 神经网络参数理解与设置

    一 超参数 1 学习率 每次迭代的步长 决定着目标函数能否收敛到局部最小值以及何时收敛到最小值 学习率越高 步长越大 2 batch 当训练数据过多时 无法一次将所有的数据送入计算 所以需要将数据分成几个部分 多个batch 逐一地送入计算
  • excel重复的数据只计数一次_你还在加班核对重复数据?3个Excel技巧教你快速进行数据查重...

    相信使用Excel办公的同学 绝大多数都会碰到一个问题 它就是数据重复值的问题 因为数据里面有重复内容 经常会让我们的工作变得非常的棘手 如上图所示 里面是我们仓库发出的单号 我们需要里面就有包含重复发货的单号 如果我们单凭肉眼去看基本是不
  • 联想电脑安装虚拟机出现不可恢复的错误

    VMware Workstation 不可恢复错误 vcpu 0 vcpu 0 VERIFY vmcore vmm main cpuid c 376 bugNr 1036521 日志文件位于 F centos vmware log 中 您可
  • websocket协议与实现原理

    文章目录 一 websocket 二 websocket的协议实现 websocket的协议格式 websocket如何验证客户端合法 websocket传输的明文和密文的传输 websocket如何断开 实现 一 websocket we
  • __attribute__((__aligned__(n)))对结构体对齐的影响

    1 attribute 是什么 attribute 是GCC里的编译参数 用法有很多种 感兴趣可以阅读一下gcc的相关文档 这里说一下 attribute 对变量和结构体对齐的影响 这里的影响大概分为两个方面 对齐和本身占用的字节数的大小
  • android ndk常见的问题及解决的方法

    原文 http blog csdn net fangyuanseu article details 6857911 在ndk编译的过程中遇到的一些问题 1 在用ndk build编译的时候 被编译文件的路径中不能包含空格 如果包含有空格将会
  • Content-Type的几种常用数据编码格式

    Content Type 内容类型 一般是指网页中存在的Content Type ContentType属性指定请求和响应的HTTP内容类型 如果未指定 ContentType 默认为text html 1 text html 文本方式的网
  • Ubuntu 环境下使用中文输入法

    Ubuntu 环境下使用中文输入法 安装fcitx 1 进入系统设置 gt 语言支持 将汉语 中国 拖到最上面 如果列表中没有 选择 添加或删除语言 来添加 2 切换键盘输入法系统 将其修改为fcitx 如果下拉框中没有显示fcitx 则需
  • java poi导入Excel、导出excel

    java poi导入Excel 导出excel 导出meven架包
  • hive中判断一个字符串是否包含另一个子串的四种方法,sql中也可用

    hive中判断一个字符串是否包含另一个子串的四种方法 如果你有一个数据需求 需要从一个字段中 判断是否有一个字符串 你该怎么做 一 方法1 like和rlike 最能想到的方法 用like或者rlike select i want to t
  • Python使用pytorch深度学习框架构造Transformer神经网络模型预测红酒分类例子

    1 红酒数据介绍 经典的红酒分类数据集是指UCI机器学习库中的Wine数据集 该数据集包含178个样本 每个样本有13个特征 可以用于分类任务 具体每个字段的含义如下 alcohol 酒精含量百分比 malic acid 苹果酸含量 克 升

随机推荐