高性能C++网络库libtnet实现:IOLoop
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,欢迎围观。
高性能C++网络库libtnet实现:IOLoop的更多相关文章
- 高性能C++网络库libtnet实现:Connection
Connection libtnet只支持IPv4 TCP Connection,之所以这么做都是为了使得实现尽可能的简单.我们主要在Connection类中封装了对tcp连接的操作. Connect ...
- 高性能C++网络库libtnet实现:http
HTTP libtnet提供了简单的http支持,使用也很简单. 一个简单的http server: void onHandler(const HttpConnectionPtr_t& con ...
- 高性能C++网络库libtnet实践:comet单机百万连接挂载测试
最近在用go语言做一个挂载大量长连接的推送服务器,虽然已经完成,但是内存占用情况让我不怎么满意,于是考虑使用libtnet来重新实现一个.后续我会使用comet来表明推送服务器. 对于comet来说, ...
- 发布一个参考tornado的高性能c++网络库:libtnet
libtnet是一个用c++编写的高性能网络库,它在设计上面主要参考tornado,为服务端网络编程提供简洁而高效的接口,非常易于使用. Echo Server void onConnEvent(co ...
- 专注于HTTP的高性能高易用性网络库:Fslib.network库
博客列表页:http://blog.fishlee.net/tag/fslib-network/ 原创FSLib.Network库(目前专注于HTTP的高性能高易用性网络库) FSLib.Networ ...
- [开源] gnet: 一个轻量级且高性能的 Golang 网络库
Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于 Ev ...
- Cowboy 开源 WebSocket 网络库
Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Pro ...
- 开源免费的C/C++网络库(c/c++ sockets library)
(1)ACE 庞大.复杂,适合大型项目.开源.免费,不依赖第三方库,支持跨平台. http://www.cs.wustl.edu/~schmidt/ACE.html (2)Asio Asio基于Boo ...
- poco网络库分析,教你如何学习使用开源库
Poco::Net库中有 FTPClient HTML HTTP HTTPClient HTTPServer ICMP Logging Mail Messages NetCore NTP OAuth ...
随机推荐
- Spring错误之org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'bookService' is expected to be of type 'pw.fengya.tx.BookService' but was actually of type 'com.sun.proxy.$Proxy1
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cas ...
- Docker 联合文件系统
联合文件系统(UnionFS)是一种分层.轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several dir ...
- MySQL系列教程(四)
文件打开数(open_files) 我们现在处理MySQL故障时,发现当Open_files大于open_files_limit值时,MySQL数据库就会发生卡住的现象,导致Nginx服务器打不开相应 ...
- AWS EC2 CentOS release 6.5 部署redis
AWS EC2 CentOS release 6.5 部署redis参考:http://blog.csdn.net/ludonqin/article/details/47211109一.安装redis ...
- SQL Server 扩展事件(Extented Events)从入门到进阶(4)——扩展事件引擎——基本概念
本文属于 SQL Server 扩展事件(Extented Events)从入门到进阶 系列 在第一二节中,我们创建了一些简单的.类似典型SQL Trace的扩展事件会话.在此过程中,介绍了很多扩展事 ...
- Swift中方法(method)所谓的lazy绑定简介
我们知道在ruby之类的动态语言中对象方法可以先从类中预先抽取,然后再应用到某个具体对象上.这称为无绑定的method对象,也可以叫做lazy绑定. 下面举个例子: irb(main):004:0&g ...
- Racket 模拟SICP的流(延时计算)
默认的Racket是要对函数参数进行求值的, 例如(f 1 (+ 1 2))里面,(+ 1 2)要先求值为3,变为(f 1 3)再进行下一步操作.因此, Racket若按照SICP使用define关键 ...
- 物料分类新增&更新
--新增 INV_ITEM_CATEGORY_PUB.Create_Category ( p_api_version IN NUMBER, p_init_msg_list IN VARCHAR2 DE ...
- 侧滑面板(对viewGroup的自定义)
额,好吧,最近一直在做侧滑的事情,到目前为止一共是学了三种方法了,一个是直接加第三方开源框架SlidingMenu,第二给是用DrawerLayout,今天这个是用谷歌官方提供的在新的support- ...
- 指令汇B新闻客户端开发(二) 主页面布局
这个主页面采用了一个开源框架SlidingMenu,这个可以在git上面下载.把这些下载下来的文件import我们的eclipse中,用我们的项目去加载这个library,在这个过程中很有可能会报错, ...