高性能C++网络库libtnet实现:IOLoop

标签: 性能 网络 libtnet | 发表时间:2013-12-06 07:10 | 作者:siddontang
出处:http://blog.csdn.net

IOLoop

libtnet采用的是prefork + event loop的架构方式,prefork就是server在启动的时候预先fork多个子进程同时工作,而event loop则是基于epoll的事件处理机制。

在最新的linux系统中,提供了timerfd,eventfd,signalfd,加上原先的socket,大部分功能都可以抽象成io事件来处理了。而在libtnet中,这一切的基础就是IOLoop。

类似于tornado,libtnet的IOLoop也提供了相似的接口。其中最核心的就是以下三个:

  typedef std::function<void (IOLoop*, int)> IOHandler_t;

int addHandler(int fd, int events, const IOHandler_t& handler);
int updateHandler(int fd, int events);
int removeHandler(int fd);  

对于任意的IO,我们可以注册感兴趣的事件(TNET_READ和TNET_WRITE),并绑定一个对应的callback回调。

callback的回调采用的是std::function的方式,这也就意味着,你可以在外部通过std::bind绑定任意不同的实现,再加上shared_ptr技术模拟闭包。

假设现在我们需要创建了一个socket对象,并将其添加到IOLoop中,我们可以这么做:

  std::shared_ptr<Connection> conn = std::make_shared<Connection>(socketfd);

ioloop->addHandler(socketfd, TNET_READ, std::bind(&Connection::onHandler, conn, _1, _2));

这样,当该socket有读事件发生的时候,对应的onHandler就会被调用。在这里,我是用了shared_ptr技术,主要是为了方便进行对象生命周期的管理。

在上面的例子中,因为std::bind的时候引用了conn,只要不将socketfd进行removeHandler,conn对象就会一直存在。所以libtnet在IOLoop内部,自行维护了conn对象的生命周期。外面不需要在将其保存到另一个地方(如果真保存了该shared_ptr的conn,反而会引起内存泄露)。在libtnet的基础模块中,我都使用的是weak_ptr来保存相关对象,每次使用都通过lock来判定是否该对象存活。

在IOLoop内部,我使用一个vector来存放注册的handler,vector的索引就是io的fd。这样,我们通过io的fd就可以非常快速的查找到对应的handler了。为什么可以这样设计,是因为在linux系统中,进程中新建文件的file descriptor都是系统当前最小的可用整数。譬如,我创建了一个socket,fd为10,然后我关闭了该socket,再次新建一个socket,这时候新的socket的fd仍然为最小可用的整数,也就是10。

EPoll

提到linux下面的高性能网络编程,epoll是一个铁定绕不开的话题,关于epoll的使用,网上有太多的讲解,这里就不展开了。

libtnet在Poller中集成了epoll,参考了libev的实现。epoll有两种工作模式,水平触发和边沿触发,各有利弊。libtnet使用的是水平触发方式,主要原因在于水平触发方式在有消息但是没处理的时候会一直通知你处理,实现起来不容易出错,也比较简单。

fork and epoll_create

这里顺便记录一下我在实现prefork模型的时候遇到的一个坑。这个问题就是epoll fd应该在fork之前还是之后创建?

大家都知道,linux fork的时候采用COW(copy on write)方式复制父进程的内容,然后我想当然的以为各个子进程会拥有独立的epoll内核空间,于是在fork之前创建了epoll fd。但是后面我却惊奇的发现一个子进程对epoll的操作竟然会影响另一个子进程。也就是说,各个子进程共享了父进程的epoll内核空间。

所以,epoll fd的创建应该在fork之后,各个子进程独立创建。

Example

Timer

IOLoop提供了一个简单的runAfter函数,用以实现定时器功能,使用非常简单:

  void func(IOLoop* loop)
{
    cout << "hello world" << endl;
    loop->stop();
}

IOLoop loop;
loop.runAfter(10 * 1000, std::bind(&func, &loop));
loop.start();

loop启动十秒之后,会打印hello world,然后整个loop退出。更多定制化的timer使用,可以使用libtnet提供的Timer class。

Callback

libtnet是一个单线程单ioloop的模型,但是不排除仍然会有其他线程希望与IOLoop进行通信,所以IOLoop提供了addCallback功能,这是libtnet唯一一个线程安全的函数。因为加入callback是一个很快速的操作,IOLoop使用了spinlock。在IOLoop每次循环的末尾,会将全部的callback取出,依次执行。

  void callback(IOLoop* loop)
{
    cout << "tell to exit" << endl;
    loop->stop();
}

IOLoop loop;
loop.addCallback(std::bind(&func, &loop));
loop.start();

最后,再次列出libtnet的地址 https://github.com/siddontang/libtnet,欢迎围观。

  
作者:siddontang 发表于2013-12-5 23:10:57 原文链接
阅读:172 评论:2 查看评论

相关 [性能 网络 libtnet] 推荐:

高性能C++网络库libtnet实现:IOLoop

- - CSDN博客互联网推荐文章
libtnet采用的是prefork + event loop的架构方式,prefork就是server在启动的时候预先fork多个子进程同时工作,而event loop则是基于epoll的事件处理机制. 在最新的linux系统中,提供了timerfd,eventfd,signalfd,加上原先的socket,大部分功能都可以抽象成io事件来处理了.

[译]Google Chrome中的高性能网络

- - UC技术博客
Google Chrome的历史和指导原则 【译注】这部分不再详细翻译,只列出核心意思. 驱动Chrome继续前进的核心原则包括:. Speed: 做最快的(fastest)的浏览器. Security:为用户提供最为安全的(most secure)的上网环境. Stability: 提供一个健壮且稳定的(resilient and stable)的Web应用平台.

[转]使用 libevent 和 libev 提高网络应用性能

- Sonic - heiyeluren的blog(黑夜路人的开源世界)
Brown, 作家, Freelance. 许多服务器部署(尤其是 web 服务器部署)面对的最大问题之一是必须能够处理大量连接. 无论是通过构建基于云的服务来处理网络通信流,还是把应用程序分布在 IBM Amazon EC 实例上,还是为网站提供高性能组件,都需要能够处理大量并发连接. 一个好例子是,web 应用程序最近越来越动态了,尤其是使用 AJAX 技术的应用程序.

【高性能web开发】 网络传输环节

- Bloger - 博客园-首页原创精华区
【高性能web开发】将会是一个系列.. 从html,js,css等前端,到HTTP/TCP等网络传输环节.. 从Asp.net等应用服务器语言到数据库优化.. 从架构设计到第三方组件和解决方案的应用.. 之后大约还有4-5篇Blog和高性能web开发有关.. 【高性能web开发】 网络传输环节. 缓存,使用Expires 等设置过期时间;如果内容没有过期就不发送请求.

天猫11.11:移动端网络性能提升两倍

- - 博客园_新闻
据阿里巴巴提供的数据显示,在双十一开始后的三分钟内,天猫平台的销售额就超过 10 亿元,其中移动端占比超 70%. 惊人的数据背后需要有强大的技术做支撑,移动客户端需要保证在高并发场景以及不同的网络环境下为用户提供顺畅的购物体验. 在双十一当天,InfoQ 有幸采访到了阿里巴巴无线事业部技术总监南天,并与他探讨了阿里巴巴在移动端方面的技术突破.

提升 Linux 网络性能,应付 100 GB的网卡

- - 极客521 | 极客521
贾斯玻.布鲁勒在2015年澳大利亚Linux研讨会(LCA)的有关内核的小型研讨会上提到:100GB的网卡即将来临( 见幻灯片,PDF格式的). 对Linux内核来说,要以最大的速度驱动这样的适配器将是巨大的挑战. 应对这一挑战是目前和未来一段时间内工作的重心. 好消息是Linux网络通信速度已经有了很大的提高-不过还有一些问题有待解决.

高性能网络编程7--tcp连接的内存使用

- - CSDN博客互联网推荐文章
当服务器的并发TCP连接数以十万计时,我们就会对一个TCP连接在操作系统内核上消耗的内存多少感兴趣. socket编程方法提供了SO_SNDBUF、SO_RCVBUF这样的接口来设置连接的读写缓存,linux上还提供了以下系统级的配置来整体设置服务器上的TCP内存使用,但这些配置看名字却有些互相冲突、概念模糊的感觉,如下(sysctl -a命令可以查看这些配置):.

高性能网络编程5--IO复用与并发编程

- - CSDN博客云计算推荐文章
对于服务器的并发处理能力,我们需要的是:每一毫秒服务器都能及时处理这一毫秒内收到的数百个不同TCP连接上的报文,与此同时,可能服务器上还有数以十万计的最近几秒没有收发任何报文的相对不活跃连接. 同时处理多个并行发生事件的连接,简称为并发;同时处理万计、十万计的连接,则是高并发. 服务器的并发编程所追求的就是处理的并发连接数目无限大,同时维持着高效率使用CPU等资源,直至物理资源首先耗尽.

携程App的网络性能优化实践

- - 博客园_知识库
  首先介绍一下携程App的网络服务架构. 由于携程业务众多,开发资源导致无法全部使用Native来实现业务逻辑,因此有相当一部分频道基于Hybrid实现. 网络通讯属于基础&业务框架层中基础设施的一部分,为App提供统一的网络服务:. Native端的网络服务.   Native模块是携程的核心业务模块(酒店、机票、火车票、攻略等),Native模块的网络服务主要通过TCP连接实现,而非常见的Restful HTTP API那种HTTP连接,只有少数轻量级服务使用HTTP接口作为补充.

移动网络下的性能优化之省电篇

- - ITeye资讯频道
随着3G和4G网络的普及,用户使用APP的场景更多地集中在移动网络下. 同时也带来了手机电量消耗更快和网络延迟更高的问题. 想开发出用户体验更好的应用,就需要对移动网络有更深入的了解. 本系列文章分为上下两篇,分别介绍如何开发出更省电和网络延迟更低的移动应用程序. 本篇文章主要介绍移动网络的一些基本工作原理以及降低手机耗电的优化方案.