muduo网路库——高效的多线程日志

在服务端编程中,日志是必不可少的,通常用于故障诊断和追踪,也可用于性能分析。在生产环境中应该做到 "Log Everything All The Time"。

 

一个日志库大体可分为前端(frontend)和后端(backend)两部分。前端是供应用程序使用的接口(API),并生成日志消息;后端则负责把日志消息写到目的地。

在多线程程序中,每个线程有自己的前端,整个程序共用一个后端。对于C++程序而言,最好整个程序都使用相同的日志库,程序有一个整体的日志输出。

 

C++日志库的前端大体上有两种API风格:

C/Java 的printf(fmt,...) 风格,例如:

log_info("Received %d bytes from %s",len,getClientName().c_str());

C++ 的stream << 风格, 例如:

LOG_INFO << "Received " << len << " bytes from " << getClientName(); 

 

muduo日志库是C++ stream风格。

 

 

5.1  功能需求

日志的目的地只有一个:本地文件

往网络写日志消息不靠谱,因为诊断日志的功能之一正是诊断网络故障,比如连接断开,网络拥塞等。如果日志消息也是通过网络发到另一台机器上,如果接受网络日志消息的服务器发生故障或者出现进程死锁,通常会导致发送日志的多个服务进程阻塞或者内存暴涨。另一个坏处是增加网络带宽消耗。

 

以本地文件为日志的destination,那么日志的滚动(rolling)是必需的,这样可以简化日志归档的实现。

rolling的条件通常有两个:

文件大小(例如每写满1GB就换下一个文件)和 时间(例如每天零点新建一个日志文件,不论前一个文件有没有写满)。

 

muduo网路库——高效的多线程日志_第1张图片

 

 

muduo日志库的默认消息格式:

muduo网路库——高效的多线程日志_第2张图片

 

 

5.2  性能需求

日志库的高效性体现在几方面:

1.每秒写几千上万条日志的时候没有明显的性能损失。

2.能应对一个进程产生大量日志数据的场景,例如 1GB/min。

3.不阻塞正常的执行流程。

4.在多线程程序中,不造成争用。

 

 

 

5.3  多线程异步日志

多线程程序对日志库提出了新的需求:线程安全,即多个线程可以并发写日志,两个线程的日志消息不会出现交织。

解决方法:用一个背景线程负责收集日志消息,并写入日志文件,其他业务线程只管往这个 “日志线程” 发送日志消息,这称为 ”异步日志“

 

 

多线程服务程序中,异步日志是必需的,因为如果在网络IO线程或业务线程中直接往磁盘写数据的话,写操作偶尔可能阻塞长达数秒之久。这可能导致请求方超时或者耽误发送心跳消息。因此在正常的 实时业务中应该彻底避免磁盘IO

需要一个”队列“来将日志前端的数据传送到后端(日志线程)。

muduo日志库采用的是双缓冲技术,基本思路是准备两块buffer:

A和B,前端负责往buffer A填数据(日志消息),后端负责将buffer B的数据写入文件。当buffer A写满之后,交换A和B,让后端将buffer A的数据写入文件,而前端则往buffer B填入新的日志消息,如此往复。用两个buffer的好处是在新建日志消息的时候不必等待磁盘文件操作,也避免每条新日志消息都出发(唤醒)后端日志线程。换言之,前端不是将一条条日志消息分别传送给后端,而是将多条日志消息拼成一个大的buffer传送给后端,相当于批处理,减少了线程唤醒的频率,降低开销。为了及时将日志消息写入文件,即便buffer A未满,日志库也会每3秒执行一次上述交换写入操作。

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(moduo网络库,muduo源码分析)