Posted on: Nov 26 2015 Categories: muduo C++ Tags: muduo

一般写服务端程序都需要有一个称手的网络库来帮我们处理琐碎的网络通信细节,比如连接的建立、关闭,读取数据,发送数据,接收、发送缓冲区的管理等,常用的C/C++网络库有libevent,asio,libev,我们项目组使用的是muduo网络库。muduo是陈硕写的,基于非阻塞IO和事件驱动的现代C++网络库,原生支持one loop per thread模型(即reactor模型),它适合开发Linux下的面向业务的多线程服务端网络应用程序。在Linux上muduo的性能(吞吐量、高并发下的事件处理效率)比其它网络库都要好,编程接口也很友好,它使用了很多Boost C++的现代特性(比如用RAII来管理资源,用function/bind来代替虚函数作为库的回调接口,借助shared_ptr实现线程安全的对象回调等),代码写得很精练简洁,而且源代码中附带有很多例子程序,是学习网络编程的很好范例,值得细细品读。

我从进项目组开始,花了6个月时间,把muduo相关的代码、例子程序、书都研究了一遍,感觉受益非浅,尤其是对处理网络通信的细节、异常处理了然于胸,写起代码来很顺溜。现在大概总结下阅读muduo网络库的顺序。

Step 0 熟悉boost智能指针、function/bind的使用

在muduo库内部使用了boost::scoped_ptr,weak_ptr,shared_ptr来管理各个网络对象(channel,tcpconn,socket,acceptor, connector)的生命期,尤其是TcpConnection,是网络库和用户代码共有的,当某个连接不需要时,不能在用户代码中持有这个shared_ptr,否则会造成资源泄漏。更重要的是,在写代码时要学习、融入这种思想:用智能指针来管理动态分配的资源。如果能正确使用智能指针,在C++代码中一般不需要出现delete语句。muduo中另一个重要的思想是用function/bind来封装回调函数。function对象类似于函数指针(不过它可以带状态,即绑定一个类对象的成员函数),在发生某些事件时,执行相关的回调函数。比如它连接断开、建立时会回调OnConnection(),数据读完后会回调OnMessage。在用户代码中,也可以模仿这种思想。

Step 1 熟悉muduo库的编译、安装,使用

这个阶段主要摸熟muduo库的使用,即使用它封装的接口来编写网络应用程序。多敲敲代码,看书,把example目录下的所有例子程序都做一遍,这样差不多能处理常见的网络通信业务。然后考虑写更专业一点的网络服务程序,这时候会考虑:网络通信的消息格式怎么定?心跳协议怎么定?系统中 各个server如何分工,如何交互、连接?遇到些底层的网络bug怎么排查?如何做性能调优?

Step 2 阅读muduo网络相关的代码,理解网络通信的各个细节

这个阶段主要研读muduo实现网络通信的各个细节(即net目录下的代码)。阅读时,多参考陈硕著的《Linux多线程服务端编程》第8章。跟着书、代码,走一遍,基本就能理解muduo网络通信的原理了。这个时候基本上解答了所有step 1产生的疑问,比如:从建立连接、读数据、写数据、关闭连接的流程是怎么样的?读写缓冲是是全局放一个,还是每个连接放一个?它的IO模型(即网络IO线程模型,谁来epoll_wait,谁来accept,如何分配连接到子线程)是什么样的?各个回调函数是处于主线程,还是子线程的语镜中?Connector主动发起连接要考虑哪些细节?

这个阶段就两个任务:

  • 理解muduo处理网络通信的流程,各种事件发生的时机。
  • muduo对各种网络异常的处理。

最后要能够达到:在各种环境下,遇到网络问题,能够定位问题出在哪里。

Step 3 阅读muduo线程库,定时器,runInLoop实现

muduo的线程库封装了一些常用的多线程设施,比如thread,条件变量,锁,countDownLatch,线程池,线程私有变量,BlockingQueue等。没有boost thread库提供得多,但是也足够用了。有些东西封装得挺有特色,可以看看。定时器和runInLoop几乎是所有网络库的标配。muduo中定时器实现得较简单(用stl::set来存放所有timer),用timerfd来获取定时事件的到来。具体可参考《Linux多线程服务端编程》第8.2节。runInLoop很有特色、很重要,它可以将一个线程的某些操作 转到 另一个线程来做,这样可以使很多非线程安全的函数变成线程安全的(比如定时器操作),也可以将某些耗时的操作异步化(比如可以将数据库写操作放到另一个线程处理)。具体实现中,会将一个function对象绑定到另一个loop io线程的待执行列表中,loop io线程处理完所有网络事件、定时器事件后,就会执行所有绑定的function。里面用到了eventfd,来通知那个线程有待执行的funcion了。

可以看出,muduo使用了socket fd(网络IO事件), timerfd(定时器到来事件), eventfd(通知事件), signal fd(信号到来事件)来处理相关的所有事件,只需一个epoll循环,整个程序的处理流程很清晰简洁。

Step 4 阅读muduo日志库

muduo日志库的代码不多,很容易阅读。读的时候可参考《Linux C++多线程服务端编程》第5章,走一遍,大概能理解它的流程。阅读的时候,主要学习两点:

  1. 从需求、现有的硬件等限制,学习如何设计并实现一个合格的日志库?
    需求分为功能需求、性能需求。

    • 功能需求主要考虑: 接口设计是否合理、易用?需要支持哪些功能?muduo日志库的设计原则是:尽量提供最精简的日志设施,不必要的就不提供。封闭的接口尽量便于程序员阅读、查错。
    • 性能需求考虑单位时间内可写入的最大日志量,且要求它不会阻塞正常的业务处理流程。muduo的设计原则:性能只需要“足够好”,即能达到现代硬盘的最大写入带宽即可。且在实现时,要考虑减少多线程的锁争用,尽量不阻塞正常的业务处理逻辑。
  2. 如何做异常处理、性能调优?
    日志库的实现逻辑基本大同小异,逻辑基本是:在各个业务线程中拼装日志串,然后将日志串存入一个Buffer(访问时需要加锁),另外有一个日志线程不停地从Buffer中取数据,然后将它输出到日志文件中。实现的时候会考虑:

    • Buffer如何设计?什么时候唤醒日志线程从Buffer中取数据?
    • 如何减少 业务线程、日志线程 访问Buffer时的锁竞争?
    • 日志串如何组装,才能使它组装速度足够快、且要兼顾接口设计的易用性?
    • 要考虑线程间的竞争、写入速度等各种情况,保证不会丢失每条日志串。
    • 什么时候切换写到另一个日志文件?什么时候flush到日志文件?
    • 若日志串写入过多,日志线程来不及消费,怎么办?

    muduo日志库的性能很高,大概可以达到每秒200多万条,非常快。代码中做了很多性能调优,比如实现了一个memory output stream,来加快各种类型转换成字符串。利用线程私有变量缓存了一些变量值来加快日志串的组装。用双缓冲技术来减少线程之间的锁竞争、最大化一次性输出日志的吞吐量。阅读时注意每个优化细节,然后在平时做性能调优时 模仿它。

Step 5 定制开发,修改源码。借鉴某些组件的实现,应用到自己的代码中

muduo网络库有很多功能不提供,比如SSL加密,配置业务线程的个数,设置多个监听端口。在实际应用时,可能根据需要,来修改muduo库。我们项目组做的修改有:

  1. 将各业务线程的个数、网络IO线程的个数、心跳线程的个数 等可配置化。
    在实际应用中,会根据业务特点来决定线程模型。有的时候,会将一些耗时的操作(比如操作mysql)、压力大的操作单独配置一些线程来操作。

  2. 一个TcpServer支持多个监听端口。
    在实际高并发服务的项目中,一般都会有一个链接服务器,来接受并管理所有的外部链接。同时,它也需要与内部的server通信。这时候,就需要监听两个端口号。为了编程的方便,我们使muduo支持了一个TcpServer监听多个端口号。
    注:也可以不修改,可创建两个TcpServer,每个TcpServer监听一个端口号。

  3. TcpServer支持发起连接。
    在实际项目中,经常需要一个服务端程序作为一个server来接受别人的连接,也需要主动发起一些连接到别的server上。为了编程的方便,我们使支持了TcpServer既可以发起连接,也可以接受连接。
    注:也可以不修改,创建单独的TcpServer来接受连接,创建单独的TcpClient来发起连接。

  4. 在thirdpartServer中利用runInLoop、定时器的实现。
    在我们的项目中,有个server要与苹果的Apns服务通信,而Apns只接受Ssl加密通信,但是muduo不支持Ssl连接。当时考虑过,修改muduo使之支持Ssl通信,但是由于当时还不熟悉Ssl编程,直接修改muduo会带来一些不必要的风险,所以直接使用Linux上的原始函数、openSsl库来与Apns通信。通信时要用到定时器和runInLoop,我就把muduo的这部分实现代码借鉴进来,实现了这两个功能。

muduo网络库源码阅读Step by Step相关推荐

  1. muduo网络库源码复现笔记(十七):什么都不做的EventLoop

    Muduo网络库简介 muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕.它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程 ...

  2. 刚出锅的 Axios 网络请求源码阅读笔记

    项目中一直都有用到 Axios 作为网络请求工具,用它更要懂它,因此为了更好地发挥 Axios 在项目的价值,以及日后能够得心应手地使用它,笔者决定从源码层面好好欣赏一下它的美貌! Axios是一款基 ...

  3. python records库_你的第一份Python库源码阅读:records库

    基本介绍 records是kennethreitz的for Humans™系列,使用原生sql去操作大多数的关系型数据库(Postgresql, MySQL, SQLite, Oracle和 MS-S ...

  4. Swift标准库源码阅读笔记 - Array和ContiguousArray

    关于 ContiguousArray ,这边有喵神的文章介绍的很详细了,可以先看看这个文章. Array 接着喵神的思路,看一下 Array 以下是从源码中截取的代码片段. public struct ...

  5. pytorch下的lib库 源码阅读笔记(1)

    置顶:将pytorch clone到本地,查看initial commit,已经是麻雀虽小五脏俱全了,非常适合作为学习模板. 2017年12月7日01:24:15 2017-10-25 17:51 参 ...

  6. C-libev学习笔记-事件库源码阅读6-API-ev_default_loop(),ev_init()

    ev_default_loop() 声明: EV_API_DECL struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0)) ...

  7. 基于C++11的muduo网络库

    文章目录 写在前面 项目编译问题 库安装的问题 项目测试代码 关于压力测试 项目概述 muduo网络库的reactor模型 muduo的设计 muduo各个类 辅助类 NonCopyable Time ...

  8. muduo网络库使用入门

    muduo网络库介绍 muduo网络库是陈硕大神开发的基于主从Reactor模式的,事件驱动的高性能网络库. 网络编程中有很多是事务性的工作,使用muduo网络库,用户只需要填上关键的业务逻辑代码,并 ...

  9. 39 网络相关函数(七)——live555源码阅读(四)网络

    39 网络相关函数(七)--live555源码阅读(四)网络 39 网络相关函数(七)--live555源码阅读(四)网络 简介 14)readSocket从套接口读取数据 recv/recvfrom ...

最新文章

  1. Linux inode 之我见
  2. 由于代码已经过优化或者本机框架位于调用堆栈之上,无法计算表达式的值 解决方案...
  3. python爬虫简单的添加代理进行访问
  4. Scrapy框架的学习(7. 了解Scrapy中的debug信息以及Scrapy shell的使用)
  5. obs噪音抑制调多少合适_TVS瞬态抑制二极管的特性及应用
  6. python 系统架构_Python之优化系统架构的方案
  7. Unknown type name 'class'; did you mean 'Class'? 问题的解决
  8. 高等数学复习笔记(一)- 高等数学基础知识、数列与函数的极限
  9. 深度学习在图像语义分割中的应用
  10. 基于Java+SpringBoot+vue+elementui农产品物流系统详细设计实现
  11. PowerDesigner(数据库建模工具) 使用教程
  12. 四川速匠:抖音的用户类型和用户群体有哪些?
  13. Go关键字--chan
  14. url中出现“%22”等如何处理?如何判断url中是否有“%22等”?如何获取当前网址?传入多个参数在url上? encodeURL和(js)
  15. 用python画樱花、玫瑰和圣诞树
  16. SpringMVC框架 获取前台传过来的数组并解析
  17. avr单片机流水灯程序c语言,AVR单片机学习C语言的流水灯验证
  18. Linux上怎样安装gcc
  19. 基于stm32f103rct6(秉火mini板)的rc522RFID读卡例程
  20. 2019一级建造师《水利水电》每日一练

热门文章

  1. C# 方法返回值的个数
  2. kinect在openni下也能玩抠出人物换背景
  3. HDU2501_多米诺骨牌
  4. DataList控件分页
  5. python读取hdf5文件_Python处理HDF5文件
  6. 批量删除html网页,批量删除.html · panghuamama/Clearly Local - Gitee.com
  7. linux下mqm添加用户,Linux 下MQ的安装和配置亲测
  8. mysql数据库索引页号为什么从3开始_MySQL数据库快问快答
  9. python1~10阶乘while_Python3基础 while 阶乘
  10. python opencv旋转_Python opencv实现与rotatedrect类似的矩形旋转,pythonopencv,RotatedRect