muduo网络库与服务模型介绍

目录

一、muduo网络库简介

1、特点

2·、代码结构

(1)公共接口

(2)内部实现

二、muduo线程模型

1、单线程Reactor

2、Reactor+线程池

3、one loop per thread

4、one loop per thread + 线程池


 muduo是陈硕个人使用C++开发的一款网络库,代码写的很有学习价值,总结的内容来自书籍《Linux 多线程服务器端编程》,也是由陈硕编写,可以配合github代码一起使用。

muduo github网址:https://github.com/chenshuo/muduo

一、muduo网络库简介

1、特点

(1)线程安全,原生支持多核多线程;

(2)不跨平台,只支持Linux;

(3)只要支持x86-64,兼顾IA32,也可以运行在ARM上;

(4)不支持UDP,只支持TCP

(5)不支持IPv6,只支持IPv4;

(6)支持的使用模式:非阻塞IO+one event loop per thread,不支持阻塞IO。

2·、代码结构

muduo 是基于Reactor模式的网络库,核心是事件循环EventLoop,用于响应计时器和IO事件,muduo采用基于对象的设计风格,事件的回调接口多以boost::function+boost::bind表达。muduo源码的目录结构为:

muduo网络库的核心位于muduo/net和muduo/net/poller,其中灰底表示用户不可见的内部类:

其中主要的公共接口和内部实现如下:

(1)公共接口

  1. Buffer:是TcpConnection的成员,仿照了Netty ChannelBuffer的buffer class,数据的读写通过buffer进行,用户代码不需要调用read/write,只需要处理收到的数据和准备好要发送的数据;
  2. EventLoop事件循环:每个线程只能有一个EventLoop实体,负责IO和定时器事件的分派,使用eventfd()来异步唤醒,用TimerQueue作为计时器管理,使用Poller作为IO多路复用;
  3. EventLoopThread:启动一个线程,在其中运行EventLoop::loop();
  4. TcpConnection:网络库的核心,封装一次TCP连接,但不能发起连接;
  5. TcpClient:用于编写网络客户端,能发起连接,并且有重试的功能;
  6. TcpServer:用于编写网络服务端,接受客户端的连接。

(2)内部实现

  1. Channel负责注册与响应IO事件,注意它不拥有file descriptor。它是Acceptor、Connector、EventLoop、TimerQueue、TcpConnection的成员,生命期由后者控制;
  2. Socket是一个RAII handle,封装一个file descriptor,并在析构时关闭fd。它是Acceptor、TcpConnection的成员,生命期由后者控制。EventLoop、TimerQueue也拥有fd,但是不封装为Socket class;
  3. SocketsOps 封装各种 Sockets 系统调用;
  4. Poller是PollPoller和EPollPoller的基类。它是EventLoop的成员,生命期由后者控制;
  5. PollPoller和 EPollPoller封装poll()和epoll()两种 IO multiplexing后端。poll 的存在价值是便于调试,因为 poll()调用是上下文无关的,用strace很容易知道库的行为是否正确;
  6. Connector用于发起 TCP连接,它是Tcpclient的成员,生命期由后者控制;
  7. Acceptor 用于接受TCP连接,它是TcpServer的成员,生命期由后者控制;
  8. TimerQueue用timerfd实现定时,这有别于传统的设置poll/epoll_wait的等待时长的办法。TimerQueue 用std::map来管理Timer,常用操作的复杂度是O(log N),N为定时器数目。它是EventLoop的成员,生命期由后者控制;
  9. EventLoopThreadPool用于创建IO线程池,用于把TcpConnection分派到某个EventLoop线程上。它是TcpServer的成员,生命期由后者控制。

二、muduo线程模型

muduo的线程模型符合one loop per thread+thread pool模型,每个线程最多只有一个EventLoop,一个文件描述符只能由一个线程读写, TcpConnection所在的线程由其所属的EventLoop决定,TcpServer支持多线程,有两种模式:

  1. 单线程,accept与TcpConnection用同一个线程做IO;
  2. 多线程,accept与EventLoop在同一个线程,另外创建一个EventLoopThreadPool,新到的连接会以轮询调度的方式分配到线程池中。

每种服务模型都有对应的例子。

1、单线程Reactor

计算和IO在同一个线程,没有事件的时候,线程等待在select/poll/epoll等函数上,事件到达后由网络库处理IO,再把消息通知客户端代码,网络库负责读写Socket,用户代码负责解码、计算、编码。事件顺序处理,无法保证优先级。这种模式适用于IO密集的应用,不太适合CPU密集的应用;

实际项目应用中,这种模型应该并不常用。

代码目录:muduo/examples/sudoku/server_basic.cc

Server_basic.cc是一个并发服务器,可以同时服务多个客户端连接,但是是单线程的。

其中最关键的是onMessage()函数,主要用来从缓冲区读取数据,并调用processRequest()去处理请求,其中全部的IO和计算任务都在同一个线程中进行。

2、Reactor+线程池

主线程负责IO,工作线程负责计算,使用固定大小的线程池,全部的IO工作都在一个Reactor线程完成,而计算任务交给线程池,这种模式适用于计算任务彼此独立,而且IO压力不大的场景,有乱序返回的可能,客户端要根据id来匹配响应。

代码目录:muduo/examples/sudoku/server_threadpool.cc

与方案1的区别是多了ThreadPool对象,线程池大小由numThreads_指定,然后processRequest()中计算的部分由ThreadPool去执行。这种方案有乱序返回的可能,所以要根据id来匹配响应。

3、one loop per thread

一个main Reactor负责accept连接,然后把连接挂在某个sub Reactor(muduo采用轮询方式选择sub Reactor)中,该连接的所有操作都在那个sub Reactor所处的线程中完成。优点是能保证请求的顺序性,程序的总体处理能力不会随着连接增加而下降,适应性强,所以是muduo的默认多线程模型。

代码目录:muduo/examples/sudoku/server_multiloop.cc

这种模式下只需要设置server_.setThreadNum(numThreads_)即可。TcpServer在这种模式下用自己的EventLoop接受新连接,然后用event loop pool里的EventLoop去执行IO;

4、one loop per thread + 线程池

既有多个Reactor来处理IO,也使用线程池来处理计算,这种模式适合既有突发IO,又有突发计算的应用。

如何确定使用多少个EventLoop呢?

根据ZeroMQ手册的建议,按照每千兆比特每秒的吞吐量配一个event loop的比例来设置event loop的数目(即muduo::TcpServer::setThreadNum()的数量),所以在编写运行于千兆以太网上的网络程序时,用一个event loop就足以应付网络IO。如果TCP连接有优先级之分,那使用一个event loop不太合适,最好是把高优先级的连接用单独的event loop来处理。

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值