日志系统——异步缓冲区

news2024/9/26 1:28:57

生产者——消费者模型:

多线程场景中最常见的模型之一,异步写日志时负责产生日志消息的业务线程充当生产者,负责写日志的线程充当消费者,两种角色进行数据交互需要依靠一块缓冲区。
单缓冲区的缺点
传统的单缓冲区PC模型一般是基于循环队列的方式进行实现的,对于读写位置的指针管理较为复杂,并且由于PC模型中任意两者之间都处于竞争关系,只有一个缓冲区意味着更加激烈的锁竞争,写日志线程在写日志的时候众多业务线程无法将日志推入缓冲区造成业务线程阻塞,这显然是不合理的。
改进使用双缓冲区
因此考虑将单缓冲区升级为双缓冲区,为业务线程和写日志线程单独分配一块缓冲区,在满足一定的条件下交换二者的缓冲区,这样就能将生产者和消费者的锁竞争降低到只有交换缓冲区的那一次,并且由于日志系统中写日志线程只有1个,消费者之间不存在竞争关系,效率就得到很大程度上的提升。
在这里插入图片描述
在这里插入图片描述
异步缓冲区实现

class Buffer
{
#define DEFAULT_BUFFERSIZE 1024 * 1024 //默认缓冲区大小
#define THRES 8 * DEFAULT_BUFFERSIZE   //缓冲区能够二倍扩容的阈值
#define LINEAR_INCREMENT 1024 * 1024   //缓冲区大小达到阈值后每一次线性增长的大小

/*极端情况下业务线程产生日志的速度可能非常快,在写日志线程还没有处理完对应缓冲区时,业务线程会由于写入空间不足而阻塞,当然有方法可以比避免这种情况,那就是扩容,但是扩容的缺点是会导致缓冲区不断增长导致崩溃*/

private:
    std::vector<char> _buffer; 
    size_t _w = 0; // 写入位置
    size_t _r = 0; // 读取位置
public:
    Buffer(size_t buffersize = DEFAULT_BUFFERSIZE) : _buffer(buffersize) {}
    bool empty();                            // 判断缓冲区是否为空
    void swap(Buffer &buf);                  // 交换2个缓冲区
    bool push(const char *data, size_t len); // 向缓冲区写入数据
    const char *readBegin();                 // 获取可读数据起始地址
    void moveReader(size_t len);             // 移动读下标
    
    size_t writeAbleSize();                  // 获取可写长度
    size_t readAbleSize();                   // 获取可读长度
    //writeAbleSize和readAbleSize保证缓冲区的读写不会越界
    void reset();                            // 归置读写位置
private:
    void moveWriter(size_t len); // 移动写下标
    void resize(size_t len);     // 扩容
};
void Buffer::resize(size_t len)
{
    //*对_buffer进行扩容,采用2倍扩容+线性扩容
    if (_buffer.size() < THRES) _buffer.resize(_buffer.size() * 2 + len);
    else  _buffer.resize(_buffer.size() + LINEAR_INCREMENT + len);
}

void Buffer::reset() 
{ _w = _r = 0;}

bool Buffer::empty()
{return _w == _r;}

void Buffer::swap(Buffer &buf)
{
	_buffer.swap(buf._buffer);
    std::swap(_w, buf._w);
    std::swap(_r, buf._r);
}

size_t Buffer::readAbleSize()
{return _w - _r;}
size_t Buffer::writeAbleSize()
{return _buffer.size() - _w;}

void Buffer::moveReader(size_t len)
{
    assert(_r + len <= _w);
    _r += len;
}
void Buffer::moveWriter(size_t len)
{
    assert(_w + len <= _buffer.size());
    _w += len;
}
const char *Buffer::readBegin()
{return &_buffer[_r];}

bool Buffer::push(const char *data, size_t len)
{
    //*判断len是否大于可写入的最大空间
    if (len > writeAbleSize()) // 条件成立则2种处理方式:扩容与阻塞
    {
#ifdef RESIZE
        resize(len);
#else
        return false;
#endif
    }
    std::copy(data, data + len, &_buffer[_w]);
    moveWriter(len);
    return true;
}

异步日志器中异步工作器的实现

异步日志器中只需要管理一个异步工作器对象

class AsynLooper
{
    using Function = std::function<void(Buffer &)>;
private:
    std::atomic<bool> _stop;
    std::thread _thread; // 异步工作器工作线程
    Buffer _pro_buf; //提供给业务线程的缓冲区
    Buffer _con_buf; //提供给写日志线程的缓冲区
    std::mutex _mtx;
    std::condition_variable _con_cond;
    std::condition_variable _pro_cond; //条件变量,避免无效的缓冲区交换

private:
    void threadEntry(); //写日志线程入口函数
    Function _callBack; // 对缓冲区数据进行处理的回调函数,具体如何读取缓冲区数据由上层决定
public:
    AsynLooper(Function callback);
    ~AsynLooper();
    void stop();
    void push(const char *data, size_t len); // 将日志消息载入缓冲区
};

AsynLooper::AsynLooper(Function callback)
    : _callBack(callback), _stop(false)
{
    _thread = std::thread(&AsynLooper::threadEntry, this);
}

AsynLooper::~AsynLooper()
{
    stop();
}
void AsynLooper::stop()
{
    _stop = true;
    _con_cond.notify_all();
    _thread.join();
    /*当异步工作器被叫停之前需要先处理完所有日志才能真正销毁,因此_stop置为真后需要马上唤醒写日志线程将残余日志都进行落地*/
}

void AsynLooper::push(const char *data, size_t len)
{
    if (!_stop)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        if (!_pro_buf.push(data, len)) // 写入缓冲区不足
        {
            _pro_cond.wait(lock, [&](){ return _pro_buf.empty(); });
            // 挂起等待至缓冲区可写入
            _pro_buf.push(data, len);
        }
        _con_cond.notify_all(); //缓冲区有数据后可以告知写日志线程已经满足可交换条件之一(另一个条件是写日志现场的缓冲区为空)
    }
}

void AsynLooper::threadEntry()
{
    while (1)
    {
        {
            std::unique_lock<std::mutex> lock(_mtx);
            if (_stop && _pro_buf.empty()) break;
            _con_cond.wait(lock, [&](){ return !_pro_buf.empty() || _stop; });
            _con_buf.swap(_pro_buf);
            _pro_cond.notify_all(); //告知阻塞的业务线程缓冲区已有足够的空间供写入
        }
        //!!!务必在回调函数_callBack之前把锁释放,写日志线程进行落地是不应该影响业务线程运行
        _callBack(_con_buf);
        _con_buf.reset();
    }
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1988955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【工具推荐】强大的图形化“社工密码生成器”

下载地址&#xff1a; 关注WX“光剑安全”公众号&#xff0c;发送“20240807社工”即可获得工具包&#xff01; 工具一&#xff1a;安全牛&#xff0c;java运行的一款社工密码生成器。 1、工具页面如下&#xff0c;可以根据对方信息、对方配偶信息、特殊符号进行排列组合生成…

c++的STL库stack、queue的使用

1.stack 在STL库中stack是一个模版类&#xff0c;第一个模版参数为存储的数据类型&#xff0c;第二个模版参数为实现stack的容器&#xff08;可缺省&#xff09;。 常用的成员函数如下 2.queue 如stack一样queue是一个模版类&#xff0c;第一个模版参数为存储的数据类型&#…

终于解决了ubuntu在高清屏上 chromium界面dpi低,界面模糊的问题

终于解决了ubuntu在高清屏上 chromium界面dpi低&#xff0c;界面模糊的问题 说明&#xff1a; 系统是ubuntu24.04&#xff0c;用firefox dpi正常&#xff0c;界面和文字都很细腻&#xff0c;用chrome,chromium,edge就很模糊&#xff0c;网上一大片的都是说字体问题&#xff0…

(21)Spring基础

Spring 需要导入的包 <!-- https://mvnrepository.com/artifact/org.springframework/spring-core --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.26</ve…

Go 1.19.4 结构体-Day 09

1. 结构体介绍 1.1 什么是结构体 结构体&#xff08;struct&#xff09;是一种用户定义的类型&#xff0c;它由一系列的字段组成&#xff0c;每个字段都有自己的名称和类型。 结构体也是值类型的&#xff0c;就算加了指针也是&#xff0c;只不过是复制的内存地址。 1.2 为什么…

【数据结构】二叉搜索树(Java + 链表实现)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构、LeetCode专栏 &#x1f4da;本系…

活动回顾|首次 Cloudberry Database Meetup · 北京站成功举办

8 月 3 日&#xff0c;由酷克数据 HashData 主办的 Cloudberry Database Meetup 北京站活动圆满结束。本次 Meetup 以“以开源应对 Greenplum 闭源&#xff0c;原厂开发者再聚首”为主题&#xff0c;深入探讨了 Greenplum 闭源所带来的影响&#xff0c;并聚焦于 Cloudberry Dat…

std::list里面的push_back和resize效率问题

2024年8月6日&#xff1a; 向list里面循环添加数据&#xff0c;两种写法 for(;;){myList.resize(myList.size()1);MyObject &obj *myList.rbegin();obj.a itervalue; } 第二种写法是push_back: for(;;){MyObject obj&#xff1b;obj.a itervalue;myList.push_back(obj)…

并发编程CompletableFuture

1. CompletableFuture简介 1.1 异步编程的概念 异步编程是一种编程范式&#xff0c;允许程序在等待某些操作完成时&#xff0c;继续执行其他任务。这在处理I/O密集型任务&#xff0c;如网络请求或文件读写时尤其有用。异步编程可以提高程序的响应性和效率&#xff0c;避免在等…

【docker】docker容器部署常用服务

1、容器部署nginx&#xff0c;并且新增一个页面 docker run -d -p 81:80 --name nginx2 nginx docker exec -it nginx2 /bin/bashcd /usr/share/nginx/html/ echo "hello world">>hello.html2、容器部署redis&#xff0c;成功部署后向redis中添加一条数据 do…

Spoon——数据库clickhouse驱动问题解决

问题 使用Spoon 软件连接clickhouse数据库&#xff0c;报错“Driver class ‘ru.yandex.clickhouse.ClickHouseDriver’ could not be found” 原因 错误消息表明Spoon无法找到ru.yandex.clickhouse.ClickHouseDriver驱动程序。这通常是因为ClickHouse的JDBC驱动程序没有正确…

JS操作dom修改Element中DatePicker的数据

let start document.querySelector(input[placeholder"请选择开始日期"]); start.value 2024-04-25 start.dispatchEvent(new Event(input)) var enterKeyEvent new KeyboardEvent(keydown, { key: Enter, code: Enter, keyCode: 13, // keyCode属性已被废弃&a…

linux 6.10.0 CXL/reg.c 详解

文章目录 前言Ref正文1. cxl_setup_regs2. cxl_probe_regs()3. cxl_probe_component_regs()4. cxl_probe_device_regs()5. cxl_map_device_regs()6. cxl_count_regblock() / cxl_find_regblock_instance() 前言 CXL 是一个比较新的技术&#xff0c;内核版本迭代太快&#xff0…

【秋招笔试】24-07-31-影石insta-秋招笔试题

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 💻 第一题 题目描述 给定一个字符串矩…

Mathtype安装教程/常见使用问题及快捷键大全

一、软件介绍 Mathtype目前来说还是最好用&#xff0c;最兼容的文本公式编辑器&#xff01;而且MathType已经被普遍应用于教育教学、科研机构、工程学、论文写作、期刊排版、编辑理科试卷等领域。 在目前很多学术期刊中&#xff0c;对文章的文本有一定要求&#xff0c;SCI检索…

k8s—Prometheus原理

一、Prometheus 1.Prometheus介绍 Prometheus 是一个开源的系统监控和报警系统&#xff0c;现在已经加入到 CNCF 基金会&#xff0c;成为继k8s 之后第二个在 CNCF 托管的项目&#xff0c;在 kubernetes 容器管理系统中&#xff0c;通常会搭配prometheus 进行监控&#xff0c;同…

PXE 批量安装Linux系统

目录 一、 实验环境准备 1、一台红帽版本7的主机 2、开启主机图形 3、配置网络可用 4、关闭VMware dhcp 功能 ​编辑​编辑 5、配置好本地仓库&#xff0c;方便后续下载 二、配置kickstart自动安装脚本的工具 1、 安装图形化生成kickstart自动安装脚本的工具 2、启动图…

动手学深度学习7.2 使用块的网络(VGG)-笔记练习(PyTorch)

以下内容为结合李沐老师的课程和教材补充的学习笔记&#xff0c;以及对课后练习的一些思考&#xff0c;自留回顾&#xff0c;也供同学之人交流参考。 本节课程地址&#xff1a;25 使用块的网络 VGG【动手学深度学习v2】_哔哩哔哩_bilibili 本节教材地址&#xff1a;7.2. 使用…

软考-软件设计师 (计算机组成和体系结构习题)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

如何提前预防网络威胁

一、引言 随着信息技术的迅猛进步&#xff0c;网络安全议题愈发凸显&#xff0c;成为社会各界不可忽视的重大挑战。近年来&#xff0c;一系列网络安全事件的爆发&#xff0c;如同惊雷般震撼着个人、企业及国家的安全防线&#xff0c;揭示了信息安全保护的紧迫性与复杂性。每一…