Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5772093
  • 博文数量: 675
  • 博客积分: 20301
  • 博客等级: 上将
  • 技术积分: 7671
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-31 16:15
文章分类

全部博文(675)

文章存档

2012年(1)

2011年(20)

2010年(14)

2009年(63)

2008年(118)

2007年(141)

2006年(318)

分类: C/C++

2011-05-03 15:38:04

曾经我是多么崇尚异步编程,认为异步编程是服务器端编程的通解,事件驱动+消息驱动,设计是那么的优美。刚工作那会,看到这边代码,清一色的使用线程池同步模式,感觉好像进入了石器时代。随着自己慢慢开始负责一些模块的开发,发现其实自己之前的认识有些偏差,异步IO模型不是万金油。

在很多时候,我们需要关心的并不是高并发,而是高吞吐、低延迟。异步IO模型,由于使用事件驱动,事件发生时,需要唤醒某个工作线程,将消息传送给工作者线程,这样唤醒、消息传递无形当中就增加了整个处理流程的延迟。高并发可以实现高吞吐,但是高吞吐并不一定需要高并发

试想一下一个高磁盘IO的模块,使用异步网络IO模型,磁盘IO的read,write都是阻塞的,磁盘的读写都在10ms以上,一个请求的process如果落到磁盘上基本上都要上百ms。磁盘IO操作阻塞当前线程进而阻塞了其它连接的网络IO。这个时候,即使前端接入的网络IO是异步的又如何呢,系统只有有限的worker线程(通常异步IO模型靠事件驱动在单线程中集成IO和逻辑处理功能),每个worker线程进行的磁盘IO数每秒也是有限的。无法提高真正的效率。另外,内存拷贝也是一大障碍。

事件驱动的异步IO模型,在全内存处理情况下,效率还是很高的;但是一旦逻辑操作有阻塞操作,就会使得吞吐量降下来了。 基于leader/follower 模式的线程池, 响应时间会更好, 尤其是在并发访问比较高的时候。

解决方法:
1. 全异步操作,这是理想情况,需要保证代码中没有阻塞,依赖的库也没有阻塞。
2. 改用leader/follower 模式的线程池,通过提高并发改善响应时间,提高吞吐量。
3. 对于数据呈长尾分布可以使用cache,避免磁盘IO;对于数据呈随机分布的,就不要加cache了,基本上是浪费内存。

方法1是一种理想情况下的解决方案,可以实现以较低的系统负载实现较高的吞吐,但是需要aio支持,尤其是磁盘的aio;方法2简单,较易实现,立竿见影;方法3,在适当的时候可以锦上添花。

Elliptics已经将网络IO和磁盘IO移到线程池中了,采用leader/follower模型,每个线程竞争accept监听socket,进行处理。同样国内的beansdb也采用了同样的方式,可能是出于代码改动小的考虑,依然使用了drive_machine,不过不影响理解。

while(1)
{
if(will_exit)
break;
lock();
accept_fd = accept(listen_fd);
unlock();

process(accept_fd);
}



参考:


阅读(7325) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~