muduo日志库特点
- 日志批量写入
- 批量唤醒写线程
- 写日志用notify+wait_timeout 方式触发日志的写入
- 锁的粒度,双缓冲,双队列
- buffer默认 4M 缓冲区, buffers 是 buffer 队列, push 、 pop 时使用 move 语义 减少内存拷贝
muduo的这些特点使得其作为高性能日志库被广泛使用。
muduo日志库异步机制
muduo为了保证日志的写入速度快,采用异步机制处理日志信息。多个线程将日志信息保存在数据buffer中,等buffer装满了再将buffer放入日志队列,通过锁和条件变量保证日志队列的数据安全,最后由日志落盘线程将日志队列中的数据写入磁盘中。
日志写入固定缓冲区程序
void AsyncLogging::append(const char* logline, int len)
{
// if(cnt++ == 50000)abort();
MutexLockGuard lock(mutex_); // 多线程加锁
if (currentBuffer_->avail() > len) // 判断buffer还有没有空间写入这条日志
{
currentBuffer_->append(logline, len); // 直接写入
}
else
{
buffers_.push_back(std::move(currentBuffer_)); // buffers_是vector,把buffer入队列
// printf("push_back append_cnt:%d, size:%d\n", ++append_cnt, buffers_.size());
if (nextBuffer_) // 用了双缓存
{
currentBuffer_ = std::move(nextBuffer_); // 如果不为空则将buffer转移到currentBuffer_
}
else
{
// 重新分配buffer
currentBuffer_.reset(new Buffer); // Rarely happens如果后端写入线程没有及时读取数据,那要再分配buffer
}
currentBuffer_->append(logline, len); // buffer写满了
cond_.notify(); // 唤醒写入线程
}
}
日志写入磁盘程序
void AsyncLogging::threadFunc()
{
assert(running_ == true);
latch_.countDown();
LogFile output(basename_, rollSize_, false);
BufferPtr newBuffer1(new Buffer); // 是给currentBuffer_
BufferPtr newBuffer2(new Buffer); // 是给nextBuffer_
newBuffer1->bzero();
newBuffer2->bzero();
BufferVector buffersToWrite; // 保存要写入的日志
buffersToWrite.