公布一个基于 Reactor 模式的 C++ 网络库

陈硕 (giantchen_AT_gmail)

Blog.csdn.net/Solstice

2010 Aug 30

本文主要介绍 muduo 网络库的使用。其设计与实现将有另文解说。

文件夹

由来 1

下载与编译 2

样例 2

基本结构 3

公开接口 4

内部实现 4

线程模型 5

结语 5

由来

半年前我写了一篇《学之者生,用之者死——ACE历史与简评》,当中提到“我心目中理想的网络库”的样子:

  • 线程安全,支持多核多线程
  • 不考虑可移植性,不跨平台,仅仅支持 Linux,不支持 Windows。
  • 在不添加复杂度的前提下能够支持 FreeBSD/Darwin,方便将来用 Mac 作为开发用机,但不为它做性能优化。也就是说 IO multiplexing 使用 poll 和 epoll。
  • 主要支持 x86-64,兼顾 IA32
  • 不支持 UDP,仅仅支持 TCP
  • 不支持 IPv6,仅仅支持 IPv4
  • 不考虑广域网应用,仅仅考虑局域网
  • 仅仅支持一种使用模式:non-blocking IO + one event loop per thread,不考虑堵塞 IO
  • API 简单易用,仅仅暴露详细类和标准库里的类,不使用 non-trivial templates,也不使用虚函数
  • 仅仅满足经常使用需求的 90%,不面面俱到,必要的时候以 app 来适应 lib
  • 仅仅做 library,不做成 framework
  • 争取所有代码在 5000 行以内(不含測试)
  • 以上条件都满足时,能够考虑搭配 Google Protocol Buffers RPC

在想清楚这些目标之后,我開始第三次尝试编写自己的 C++ 网络库。与前两次不同,这次我一開始就想好了库的名字,叫 muduo (木铎),并在 Google code 上创建了项目: http://code.google.com/p/muduo/ 。muduo 的主体内容在 5 月底已经基本完毕,如今我把它开源。

本文主要介绍 muduo 网络库的使用,其设计与实现将有另文解说。

下载与编译

下载地址: http://muduo.googlecode.com/files/muduo-0.1.0-alpha.tar.gz

SHA1 Checksum: 5d3642e311177ded89ed0d15c10921738f8c984c

Muduo 使用了 Linux 较新的系统调用,要求 Linux 的内核版本号大于 2.6.28 (我自己用的是 2.6.32 )。在 Debian Squeeze / Ubuntu 10.04 LTS 上编译測试通过,32 位和 64 位系统都能使用。

Muduo 採用 CMake 为 build system,安装方法:

$ sudo apt-get install cmake

Muduo 依赖 Boost,非常easy安装:

$ sudo apt-get install libboost1.40-dev # 或 libboost1.42-dev

编译方法非常easy:

$ tar zxf muduo-0.1.0-alpha.tar.gz

$ cd muduo/

$ ./build.sh

# 编译生成的可运行文件和静态库文件分别位于 ../build/debug/{bin,lib}

假设要编译 release 版,可运行

$ BUILD_TYPE=release ./build.sh

# 编译生成的可运行文件和静态库文件分别位于 ../build/release/{bin,lib}

编译完毕之后请试执行当中的样例。比方 bin/inspector_test ,然后通过浏览器訪问 http://10.0.0.10:12345/ 或 http://10.0.0.10:12345/proc/status,当中 10.0.0.10 替换为你的 Linux box 的 IP。

样例

Muduo 附带了几十个小样例,位于 examples 文件夹。当中包含从 Boost.Asio、JBoss Netty、Python Twisted 等处移植过来的样例。

examples

|-- simple # 简单网络协议的实现

|   |-- allinone  # 在一个程序里同一时候实现以下 5 个协议

|   |-- chargen   # RFC 864,可測试带宽

|   |-- daytime # RFC 867

|   |-- discard # RFC 863

|   |-- echo # RFC 862

|   |-- time # RFC 868

|   `-- timeclient # time 协议的客户端

|-- hub # 一个简单的 pub/sub/hub 服务,演示应用级的广播

|-- roundtrip # 測试两台机器的网络延时与时间差

|-- asio # 从 Boost.Asio 移植的样例

|   |-- chat # 聊天服务

|   `-- tutorial # 一系列 timers

|-- netty # 从 JBoss Netty 移植的样例

|   |-- discard # 可用于測试带宽,server可多线程执行

|   |-- echo # 可用于測试带宽,server可多线程执行

|   `-- uptime # TCP 长连接

`-- twisted # 从 Python Twisted 移植的样例

`-- finger # finger01 ~ 07

基本结构

Muduo 的文件夹结构例如以下。

muduo

|-- base # 与网络无关的基础代码,已提前公布

`-- net # 网络库

|-- http # 一个简单的可嵌入的 web server

|-- inspect # 基于以上 web server的“窥探器”,用于报告进程的状态

`-- poller # poll(2) 和 epoll(4) 两种 IO multiplexing 后端

Muduo 是基于 Reactor 模式的网络库,其核心是个事件循环 EventLoop,用于响应计时器和 IO 事件。Muduo 採用基于对象(object based)而非面向对象(object oriented)的设计风格,其接口多以 boost::function + boost::bind 表达

Muduo 的头文件明白分为客户可见和客户不可见两类。客户可见的为白底,客户不可见的为灰底。

这里简介各个头文件及 class 的作用,具体的介绍留给以后的博客。

公开接口
  • Buffer 仿 Netty ChannelBuffer 的 buffer class,数据的读写透过 buffer 进行
  • InetAddress 封装 IPv4 地址 (end point),注意,muduo 眼下不能解析域名,仅仅认 IP
  • EventLoop 反应器 Reactor,用户能够注冊计时器回调
  • EventLoopThread 启动一个线程,在当中执行 EventLoop::loop()
  • TcpConnection 整个网络库的核心,封装一次 TCP 连接
  • TcpClient 用于编写网络client,能发起连接,而且有重试功能
  • TcpServer 用于编写网络server,接受客户的连接
  • 在这些类中,TcpConnection 的生命期依靠 shared_ptr 控制(即用户和库共同控制)。Buffer 的生命期由 TcpConnection 控制。其余类的生命期由用户控制。
  • HttpServer 和 Inspector,暴露出一个 http 界面,用于监控进程的状态,相似于 Java JMX。这么做的原因是,《程序猿修炼之道》第 6 章第 34 条提到“对于更大、更复杂的server代码,提供其操作的内部试图的一种美丽技术是使用内建的 Web server”,Jeff Dean 也说“(每一个 Google 的server进程)Export HTML-based status pages for easy diagnosis”。
内部实现
  • Channel 是 selectable IO channel,负责注冊与响应 IO 事件,它不拥有 file descriptor。它是 Acceptor、Connector、EventLoop、TimerQueue、TcpConnection 的成员,生命期由后者控制。
  • Socket 封装一个 file descriptor,并在析构时关闭 fd。它是 Acceptor、TcpConnection 的成员,生命期由后者控制。EventLoop、TimerQueue 也拥有 fd,可是不封装为 Socket。
  • SocketsOps 封装各种 sockets 系统调用。
  • EventLoop 封装事件循环,也是事件分派的中心。它用 eventfd(2) 来异步唤醒,这有别于传统的用一对 pipe(2) 的办法。它用 TimerQueue 作为计时器管理,用 Poller 作为 IO Multiplexing。
  • Poller 是 PollPoller 和 EPollPoller 的基类,採用“电平触发”的语意。它是 EventLoop 的成员,生命期由后者控制。
  • PollPoller 和 EPollPoller 封装 poll(2) 和 epoll(4) 两种 IO Multiplexing 后端。Poll 的存在价值是便于调试,由于 poll(2) 调用是上下文无关的,用 strace 非常easy知道库的行为是否正确。
  • Connector 用于发起 TCP 连接,它是 TcpClient 的成员,生命期由后者控制。
  • Acceptor 用于接受 TCP 连接,它是 TcpServer 的成员,生命期由后者控制。
  • TimerQueue 用 timerfd 实现定时,这有别于传统的设置 poll/epoll_wait 的等待时长的办法。为了简单起见,眼下用链表来管理 Timer,假设有必要可改为优先队列,这样复杂度可从 O(n) 降为O(ln n) (某些操作甚至是 O(1))。它是 EventLoop 的成员,生命期由后者控制。
  • EventLoopThreadPool 用于创建 IO 线程池,也就是说把 TcpConnection 分派到一组执行 EventLoop 的线程上。它是 TcpServer 的成员,生命期由后者控制。

类图

线程模型

Muduo 的线程模型符合我主张的 one loop per thread + thread pool 模型。每一个线程最多有一个 EventLoop。每一个 TcpConnection 必须归某个 EventLoop 管理,全部的 IO 会转移到这个线程,换句话说一个 file descriptor 仅仅能由一个线程读写。TcpConnection 所在的线程由其所属的 EventLoop 决定,这样我们能够非常方便地把不同的 TCP 连接放到不同的线程去,也能够把一些 TCP 连接放到一个线程里。TcpConnection 和 EventLoop 是线程安全的,能够跨线程调用。TcpServer 直接支持多线程,它有两种模式:

1. 单线程,accept 与 TcpConnection 用同一个线程做 IO。

2. 多线程,accept 与 EventLoop 在同一个线程,另外创建一个 EventLoopThreadPool,新到的连接会按 round-robin 方式分配到线程池中。

结语

Muduo 是我对常见网络编程任务的总结,用它我能非常easy地编写多线程的 TCP server和client。Muduo 是我业余时间的作品,代码预计还有非常多 bug,功能也不完好(比如不支持 signal 处理),待日后慢慢改进吧。

后记:性能測试:

1. muduo 与 boost asio 吞吐量对照

2. muduo 与 libevent2 吞吐量对照

3. 击鼓传花:对照 muduo 与 libevent2 的事件处理效率

公布一个基于 Reactor 模式的 C++ 网络库的更多相关文章

  1. Reactor模式解析——muduo网络库

    最近一段时间阅读了muduo源码,读完的感受有一个感受就是有点乱.当然不是说代码乱,是我可能还没有完全消化和理解.为了更好的学习这个库,还是要来写一些东西促进一下. 我一边读一边尝试在一些地方改用c+ ...

  2. [开源] gnet: 一个轻量级且高性能的 Golang 网络库

    Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于 Ev ...

  3. Stateless是一个基于C#创建状态机的简单库

    Stateless是一个基于C#创建状态机的简单库 .Net轻量状态机Stateless 很多业务系统开发中,不可避免的会出现状态变化,通常采用的情形可能是使用工作流去完成,但是对于简单场景下,用工作 ...

  4. 基于c++11新标准开发一个支持多线程高并发的网络库

    背景 新的c++11标准出后,c++语法得到了非常多的扩展,比起以往不论什么时候都要灵活和高效,提高了程序编码的效率,为软件开发者节省了不少的时间. 之前我也写过基于ACE的网络server框架,但A ...

  5. RSuite 一个基于 React.js 的 Web 组件库

    RSuite http://rsuite.github.io RSuite 是一个基于 React.js 开发的 Web 组件库,参考 Bootstrap 设计,提供其中常用组件,支持响应式布局. 我 ...

  6. 公布一个基于CSDN Code的学习測试仓库

    使用CSDN Code代码托管平台有一段时间了,今天新建立了一个公开的仓库https://code.csdn.net/smstong/learngit/tree/master,供大家測试合并请求等协作 ...

  7. 基于协程的Python网络库gevent

    import gevent def test1(): print 12 gevent.sleep(0) print 34 def test2(): print 56 gevent.sleep(0) p ...

  8. 发布一个参考tornado的高性能c++网络库:libtnet

    libtnet是一个用c++编写的高性能网络库,它在设计上面主要参考tornado,为服务端网络编程提供简洁而高效的接口,非常易于使用. Echo Server void onConnEvent(co ...

  9. AgileRepository - 一个基于接口的Repository快速开发库

    AgileRepository 这是一个可以帮助你快速开发Repository的lib.有点像SpringData JPA根据方法名.注解来自动生成查询方法的功能. 对于一些简单的查询,只需要定义接口 ...

随机推荐

  1. WPF 各种基础动画实现

    C# using System; using System.Collections.Generic; using System.Linq; using System.Text; using Syste ...

  2. Wind River Linux 6 Security Profile

    2692407267@qq.com,很多其它内容请关注http://user.qzone.qq.com/2692407267 Wind River Linux 6 Security Profile

  3. Ubuntu下安装vmware 9.0 + 注册码

    先附上一些注册码到时使用: NA0UF-DUH00-QZHM0-MU17K-CC824 4F469-F024Q-CZ8R9-DL1N0-13C6W HF261-0HL40-FZX21-F9AQ2-0C ...

  4. 【buildroot-2011.11】You may have to install 'g++' on your build machine

    buildroot - 2011.11 当进行交叉编译.例如像以下错误提及演示: "You may have to install 'g++' on your build machine&q ...

  5. java自学者的福音

    谈到自学对于程序员来说并不陌生,自从我们离开校门就开始了自学之路.这一路上绝大部分都是 百步止于九十 步, 不是因为他们不够坚持,而是没有找到学习的方法和资源.当然这一路上我也走得很辛苦,刚毕业后自学 ...

  6. C语言中的static 具体分析

    google了近三页的关于C语言中static的内容,发现可用的信息非常少,要么长篇大论不知所云要么在关键之处几个字略过,对于想挖掘底层原理的刚開始学习的人来说參考性不是非常大.所以,我这篇博文博採众 ...

  7. ORACLE Install (10g r2) FOR Red Hat Enterprise Linux Server release 5.5 (64 bit) (转)

    OS Info----------# cat /etc/redhat-releaseRed Hat Enterprise Linux Server release 5.5 (Tikanga)# cat ...

  8. Harry Potter and the Prisoner of Azkaban

    称号:Harry Potter and the Prisoner of Azkaban 作者:J.K. Rowling 篇幅: 448页 蓝思值:880L 用时:    11天 工具:  有道词典 [ ...

  9. [Servlet3.0新功能]注释替代配置文件

    作者信息 作者名称:金云龙 个人网站:http://www.longestory.com 个人公众帐号:搜索"longestory"或"龙哥有话说" 须要注意的 ...

  10. JSplitPane demo

    package example; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; imp ...