目录
Muduo是什么?
muduo是陈硕大神个人开发的C++的TCP网络编程库。muduo基于Reactor模式实现。Reactor模式也是目前大多数Linux端高性能网络编程框架和网络应用所选择的主要架构,例如内存数据库Redis和Java的Netty库等。
陈硕的《Linux多线程服务器端编程》一书对muduo整个架构进行了非常详尽的介绍和分析,可以说是学习muduo源码和设计理念最好的资料了。这本书也非常推荐大家购买阅读,感觉是后台开发的必读书目了。
而本文则主要是从源码角度辅助理解整个muduo的实现,同时也姑且算是对muduo的一个小小的补充。
同时提供了一个muduo注释版,以辅助大家参考学习。
muduo的架构和概念
muduo中类的职责和概念划分的非常清晰,在《Linux多线程服务器端编程》一书的6.3.1章节有详细的介绍。实际上目前很多网络库的接口设计也都受到了muduo的影响,例如360的evpp等。
经典的服务器设计模式Reactor模式
大多数人学习Linux网络编程的起点可能都是从《UNP》开始的,书中描述的服务端程序架构基本上是一个大的while循环,程序阻塞在accept或poll函数上,等待被监控的socket描述符上出现预期的事件。事件到达后,accept或poll函数的阻塞解除,程序向下执行,根据socket描述符上出现的事件,执行read、write或错误处理。
整体架构如下图所示:
而muduo的整体风格受到netty的影响,整个架构依照Reactor模式,基本与如下图所示相符:
single_thread_reactor.png
所谓Reactor模式,是有一个循环的过程,监听对应事件是否触发,触发时调用对应的callback进行处理。
这里的事件在muduo中包括Socket可读写事件、定时器事件。在其他网络库中如libevent也包括了signal、用户自定义事件等。
负责事件循环的部分在muduo命名为EventLoop
,其他库如netty、libevent也都有对应的组件。
负责监听事件是否触发的部分,在muduo中叫做Poller
。muduo提供了epoll和poll两种来实现,默认是epoll实现。
通过环境变量MUDUO_USE_POLL
来决定是否使用poll:
Poller* Poller::newDefaultPoller(EventLoop* loop)
{
// 通过此环境变量来决定使用poll还是epoll
if (::getenv("MUDUO_USE_POLL"))
{
return new PollPoller(loop);
}
else
{
return new EPollPoller(loop);
}
}
此外,图中的acceptor负责accept新连接,并将新连接分发到subReactor。这个组件在muduo中也叫做Acceptor
。
关于图中的其他部分,会在muduo的线程模型一节有详细介绍。
一个简单的例子
本文首先从最简单的echo server入手,来介绍muduo的基本使用,同时也方便后面概念的理解。
void onMessage(const muduo::net::TcpConnectionPtr& conn,
muduo::net::Buffer* buf,
muduo::Timestamp time)
{
conn->send(buf);
}
int main()
{
muduo::net::EventLoop loop;//建立一个事件循环器EventLoop
muduo::net::InetAddress listenAddr(2007);
TcpServer server(&loop, listenAddr);//建立对应的业务服务器TcpServer
server.setMessageCallback(onMessage);//设置TcpServer的Callback
server.start();//启动server
loop.loop();//开启事件循环
}
echo-server的代码量非常简洁。一个典型的muduo的TcpServer工作流程如下:
- 建立一个事件循环器EventLoop
- 建立对应的业务服务器TcpServer
- 设置TcpServer的Callback
- 启动server
- 开启事件循环
陈硕认为,TCP网络编程的本质是处理三个半事件,即:
- 连接的建立
- 连接的断开:包括主动断开和被动断开
- 消息到达,文件描述符可读。
- 消息发送完毕。这个算半个事件。