zoukankan      html  css  js  c++  java
  • C++ 高性能无锁日志系统

    服务器编程中,日志系统需要满足几个条件

    .高效,日志系统不应占用太多资源

    .简洁,为了一个简单的日志功能引入大量第三方代码未必值得

    .线程安全,服务器中各个线程都能同时写出日志

    .轮替,服务器不出故障是不重启的,半年一年的日志放到一个文件会导致文件过大

    .及时保存,程序故障导致异常退出,此时需要通过日志诊断问题,不缓冲的日志系统更易用


    著名的日志库有log4xxx系列,提供了非常灵活的功能,当然随之而来的代价就是庞大的库。在大多数服务器应用中,所需的功能不多,我偏向于选择一个支持按时间轮替的简洁的日志库。

    为了同时做到线程安全和支持轮替,大多数日志系统都使用锁。写出日志时,首先获取锁,如果需要轮替,则进行轮替操作,否则写到现有文件,最后释放锁。

    google开源的leveldb的日志系统中,同时做到了“线程安全”和轮替,但是没有用锁,这引发了我的兴趣。

    仔细阅读发现它的运作原理是

    .每个log操作,都会生成相关的字符串,最终调用write,写出到日志系统的文件描述符fd。

    .进行rotate操作时,重新命名旧文件,保持旧文件的打开状态,然后打开新文件,将fd设置为新文件。

    .接下来sleep 200ms,然后把close旧文件

    那么轮替过程中,fd的值为fd_old或者fd_new,只要fd的读写是原子的,不会读取到非fd_old和fd_new的其他值即可(fd是int,这点可以做到)。write操作就没有问题

    如果由于系统繁忙,fd读取为fd_old之后,走到操作系统的write之前,线程被切换,并且经过了200ms,那么fd_old就有可能会在sleep 200ms之后被关闭,那么write就可能失败。

    因此这种做法是简洁的,能够应对绝大多数情况,但并非安全,而且切换时需要sleep 200ms也是个让人头疼的问题。


    借鉴leveldb的做法,加上posix上的dup2调用则可以完美的解决这个问题。

    .轮替时,首先重命名旧文件,保持旧文件的打开状态,然后打开新文件。

    rename(oldname, newname);
    fd = open(oldname,...);

    .使用dup2系统函数把fd_new复制到fd_old上

    dup2(fd, fd_);

    .关闭fd_new

    close(fd);

    其中dup2是原子操作,它会关闭fd_old并且把fd_old也指向fd_new打开的文件。因此fd_old这个文件描述符总是保持打开状态,并且值不变,但是前后指向了不同的文件。另一边write也是个原子操作,它与dup2不会交叉进行,因此保证了日志系统的正确性。


    详情参见开源库handy中的logging.h和logging.cc,里面一部分代码采用了C++11的语法

    https://github.com/yedf/handy/tree/master/handy

    handy的日志系统中,日志要做的内容就是使用snprintf格式化要输出的内容,然后调用write,没有多余的工作,因此做到了简洁高效

    通过前面介绍的原理同时实现了无锁的线程安全,和日志轮替

    每次日志的输出都write,即使程序崩溃,日志也已经到了操作系统层,不会丢失,易于调试问题

    当然高效与及时保存有一定的冲突,如果缓存多条数据然后合并write能够提升一定的性能,但这里我选择简洁与易用

    handy的日志系统性能测试可以参见项目examples下的log-bench.cc,在我笔记本电脑上的虚拟机的压力测试中,输出文件为/dev/null时,能够达到75w/s的qps

    PS:handy的日志轮替中,对lastRotate_的读取和修改并非原子类型,可能会导致多轮替一次,解决方法为使用C++11中的原子类型,或者就容忍了(多轮替一次会在后续的操作中失败,仅仅多输出了一条信息到标准错误)。

     本文用菊子曰发布
  • 相关阅读:
    (基本知识)Redis 字符串(String)相关函数
    (基本知识)Redis 键相关命令函数
    (基本知识)Redis连接与安全
    centos7 php开发环境安装-Redis
    centos7 php开发环境安装--配置SSL(Apache为例)
    centos7 php开发环境安装-Git
    centos7 php开发环境安装-Nginx
    串口发送数据
    七大查找算法
    基于STM32原子战舰板内存管理源码详解
  • 原文地址:https://www.cnblogs.com/dongfuye/p/3888128.html
Copyright © 2011-2022 走看看