muduo中的日志是指诊断日志,即通常用于故障诊断和追踪的日志,便于服务器发生故障时的线索追踪,是网络库中很重要的一个部分。
在总结异步日志之前,首先应该清楚什么是异步日志?与同步日志又有什么区别?
同步日志与异步日志
同步日志:网络IO线程或业务线程直接向磁盘文件中写日志信息,只有等一条日志消息写完之后才能执行后续的程序。同步日志容易阻塞在磁盘IO上,效率较低且影响服务器性能,应尽量避免在服务器中多次使用磁盘IO。
异步日志:网络IO线程或业务线程产生日志消息时,用一个缓冲区储存起来,等到合适的时机,用一个后台线程统一将日志消息写入磁盘文件中。异步日志避免了在网络IO线程或业务线程中阻塞在磁盘IO中,因此也称为非阻塞日志。
看了同步日志和异步日志的概念之后,使用异步日志的原因也很清晰了。异步日志避免了网络IO线程和业务线程中的磁盘IO,只在后台线程中使用了一次磁盘IO,大大提升了服务器的响应性能和日志信息的处理效率。
muduo日志库的实现思想
muduo日志库分为前端和后端两个部分,前端负责将生成的日志消息储存到缓冲区中,后端负责将缓冲区的日志消息写入到磁盘文件中。
为了实现前端、后端的异步操作,同时避免前端每次生成日志消息都唤醒后端线程,从而提高日志处理效率,muduo日志库采用的是双缓冲技术,其实现思想为:准备两块缓冲区(记为buffer A、buffer B),前端负责往buffer A中写日志消息,后端负责将buffer B中的日志消息写入磁盘文件。当buffer A写满之后,后端线程中会交换buffer A和buffer B,让前端往buffer B中写入日志消息,后端将buffer A中的日志消息写到磁盘文件中,如此往复。同时,为了及时将生成的日志消息写入文件,便于管理者分析日志消息,即使buffer A未满,日志库也会每3秒执行交换写入操作。
总结,muduo日志库的优点为:避免了前端每生成一条日志消息就传送给后端,而是将多条日志消息拼成一个大buffer传送给后端线程,相当于批量处理,减少了后端线程的唤醒频率,降低了服务器开销。
muduo日志库关键代码分析
muduo实现异步日志的类是AsyncLogging.h,声明的相关变量如下:
typedef muduo::FixedBuffer<LargeBuffer> Buffer; //大小为4MB的缓冲区
typedef std::unique_ptr<Buffer> BufferPtr; //缓冲区指针
typedef std::vector<BufferPtr> BufferVector;
muduo::MutexLock mutex_; //互斥锁,用于保证前端、后端的线程安全
muduo::Condition cond_; //条件变量
BufferPtr currentBuffer_; //当前缓冲指针
BufferPtr nextBuffer_; //预备缓冲指针
BufferVector buffers_; //储存已填满的缓冲,并移交给后端
前端的关键实现代码如下:
void AsyncLogging::append(const char* logline, int len)//向当前缓冲区中添加日志消息,如果当前缓冲区放不下了,那么就把当前缓冲区放到前端缓冲区队列中
{
muduo::MutexLockGuard lock(mutex_);//用锁来保持同步
if (currentBuffer_->avail() > len)//如果当前缓冲区还能放下当前日志消息
{
currentBuffer_->append(logline, len);