技术中沉思的时候最快乐,问题得到完美解决的时候最有成就感!
分类: C/C++
2013-02-22 20:20:47
日志或者说log系统对于一个server的重要性是不言而喻的;开源库c++中用的最多的是 logc4plus,可配置性强;python也有自己的log模块;
不考虑可配置性,其实一个简单的日志模块实现是很简单的;
我见过的日志log系统基本都是实时刷新日志到磁盘的,因为日志系统绝对不能丢日志,否则一旦server down掉,日志没及时保存,那查问
题就两眼一抹黑了;比如c++ server开发中最常见的 SIGSEGV core down;这导致一个比较严重的问题,日志系统往往成为一个server的瓶颈,
实时大量的磁盘写导致IO飙升,碰到这种情况,一般只能选择关闭一些日志;
在开发yifei项目的时候,我在想怎么能让日志子系统尽量不要干扰正常的业务流程,两点:1,缓冲,就像c中的FILE;2,由独立的后台线程
负责刷新日志到磁盘;只要这两点都做到了,那日志子系统将比较完美的达到理想效果;
第二点比较简单,当log缓冲满了的时候,通过线程条件锁通知即可,平时后台线程只是简单的等待即可(wait_cond...)
第一点要实现比较麻烦,而且涉及到好几处比较难以解决的细节问题:
1,因为log带缓冲,所以log的安全性的保证就要做足了,必须要确保程序在正常或者非正常退出(比如core down,被外面kill,或者ctr+c中断)
等等情况下,都要确保缓冲的log能安全的保存回磁盘;posix c 中的 atexit 这一函数使用可以保证程序在退出的时候可以调用我们预设的log刷新函
数;非正常情况下,能做的就是要catch住所有的信号,然后在信号处理函数中尝试刷新,然后再退出;信号的引入致使日志系统变的复杂,再加上
在多线程的环境下,更加显得麻烦;
在为yifei项目开发缓冲log前,对多线程和信号等等做了充足的研究,最后才完成了这一工作,详情见:多线程信号屏蔽测试
2,另外一个问题是,log 缓冲和c的FILE还有点点不一样,缓冲是为了保证在大量log的情况下性能依旧理想,但在log非常少的情况下,如果日志
被缓冲,迟迟不能刷到磁盘上,会让人感觉非常恶心和愤怒(比如有时自己做测试,tail住日志的情况下),所以定时的检查缓冲最后一次刷新时间
确保log在一定的时间内一定被刷到磁盘显得非常有必要了;于是在这个子系统里面还需要有定时器;这个定时器必须由业务线程提供运转,每隔一定
时长就检查一遍所有的log数据,如果某log日志被缓冲了太久时间且未刷到磁盘,则通知后台log线程强制刷这一log;
3,另外就是锁的问题,以及数据交互的问题;缓冲buf由后台线程刷往磁盘的过程中是被后台线程锁住的,这一过程有可能很快,也有可能很慢(
取决于磁盘IO);这样会导致其业务线程写log写不进对应的缓冲buf(因为log的保护锁被后台log线程把持住了),也就是说,业务线程还是得等待后
台IO,那整套log系统实际就没任何意义;
为解决此问题,yifei log 系统采用了双缓冲buf;开始两buf都是闲置的,当业务线程将A buf写满后,swap一下两buf的指针,B buf成了当前log写入
地址,A buf 则转交给了后台子系统;后台子线程获得通知后,只要将 A buf中的数据刷往磁盘即可;这样做的目的其实就是:在刷磁盘的过程中,其他
线程还是可以不继续写入log到缓冲中;当然,如果后面这个buf也被写满了,而前面的buf因为磁盘IO阻塞还没有被释放,那就只能等了;这种情况说明
磁盘太慢,或者日志太猛...
从上面可以看出,带缓冲的log系统实现固然麻烦,但要实现出来也不复杂,但很明显,这一子系统不能单独剥离出来作为一个独立的库或者模块;涉
及到的东西太多:线程,信号,定时器等等,这些都不是一个简单的库能提供的;只有依赖整个系统,然后和整个系统结合才能获得想要的效果;
btw:log 子系统年前就在准备开发,春节闲暇在家里花了两天写的,5,6百行代码,以及配合的改动;最后效果挺好的,等待压测,以及和其他现有
系统对比测试;相信这一系统一定完爆他们;
最新的代码已经提交到 github: