http://blog.chinaunix.net/uid-20761674-id-75056.html

一.C10K的问题

C10K的问题在上个世纪90年代就被提出来了。大概的意思是当用户数超过1万时,很多设计不良好的网络服务程序性能都将急剧下降、甚至瘫痪。并且,这个问题并不能通过升级硬件设备解决,是操作系统固有的问题,也就是说,如果你的服务器最高能支撑1000个并发,尽管你升级了计算能力高一倍的 cpu,内存再翻一番,硬盘转速在快一倍,也无法支撑2000个并发。

经典的网络编程模型有4个:

1. Serve one client with each thread/process, and use blocking I/O。即对每个客户都使用不同的线程或进程进行服务,在每个线程或进程中使用阻塞I/O。这是小程序和java常用的策略,对于交互式的应用也是常见的选择,这种策略很能难满足高性能程序的需求,好处是实现极其简单,容易实现复杂的交互逻辑。我们常用的Apache、ftpd等都是这种工作。

2. Serve many clients with single thread, and use nonblocking I/O and readiness notification。即对所有的客户使用单一一个线程或进程进行服务,在这个线程或进程里,采用异步IO的策略。这是经典模型,优点在于实现较简单,方便移植,也能提供足够的性能;缺点在于无法充分利用多CPU的资源。

3. Serve many clients with each thread, and use nonblocking I/O and readiness notification 对经典模型2的简单改进,仍然采用异步IO的策略,但对所有的客户使用多个线程或进程进行服务。缺点是容易在多线程并发上出bug,甚至某些OS不支持多线程进行readiness notification

4. Serve many clients with each thread, and use asynchronous I/O 在有AI/O支持的OS上,能提供相当高的性能。不过AI/O编程模型和经典模型差别相当大,基本上很难写出一个框架同时支持AI/O和经典模型。这个模型主要是用于window平台上。

在linux上开发高性能的网络应用,只能选着第2、3种方式。考虑到复杂性,我们往往只采用第2种。下面就讨论一下第二种模型。

我们知道,实现异步IO一般是采用select 或poll来实现。Select 定义如下:

int select(int n, fd_set *rd_fds, fd_set *wr_fds, fd_set *ex_fds, struct timeval *timeout);
Poll 的接口如下:
int poll(struct pollfd *ufds, unsigned int nfds, int timeout);

然而 Select 和Poll 在连接数增加时,性能急剧下降。这有两方面的原因:首先操作系统面对每次的select/poll 操作,都需要重新建立一个当前线程的关心事件列表,并把线程挂在这个复杂的等待队列上,这是相当耗时的。其次,应用软件在select/poll 返回后也需要对传入的句柄列表做一次扫描来判断哪些句柄是可用的,这也是很耗时的。这两件事都是和并发数相关,而I/O 事件的密度也和并发数相关,导致CPU 占用率和并发数近似成O(n2)的关系。

因为以上的原因,Unix 上开发了性能更高的epoll, kqueue, /dev/poll 这3个程序接口来解决上述问题。其中epoll 是linux 的方案,kqueue 是freebsd 的方案,/dev/poll 是最古老的Solaris 的方案,使用难度依次递增。

简单的说,这些api 做了两件事:

1. 避免了每次调用select/poll 时kernel 分析参数建立事件等待结构的开销,kernel 维护一个长期的事件关注列表,应用程序通过句柄修改这个列表和捕获I/O 事件。

2. 避免了select/poll 返回后,应用程序扫描整个句柄表的开销,Kernel 直接返回具体的事件列表给应用程序。

. libevent

由于epoll, kqueue, /dev/poll每个接口都有自己的特点,程序移植非常困难,于是需要对这些接口进行封装,以让它们易于使用和移植,其中libevent库就是其中之一。

按照libevent的官方网站,libevent库提供了以下功能:当一个文件描述符的特定事件(如可读,可写或出错)发生了,或一个定时事件发生了,libevent就会自动执行用户指定的回调函数,来处理事件。目前,libevent已支持以下接口/dev/poll, kqueue(2), event ports, select(2), poll(2) 和 epoll(4)。Libevent的内部事件机制完全是基于所使用的接口的。因此libevent非常容易移植,也使它的扩展性非常容易。目前,libevent已在以下操作系统中编译通过:Linux,BSD,Mac OS X,Solaris和Windows。

使用libevent库进行开发非常简单,也很容易在各种unix平台上移植。一个简单的使用libevent库的程序如下:

三.libevent库的应用

Go2代理是一个大流量的代理应用,月流量近TB。其中图片、flash、zip文件占总流量的绝大部分。为了减少流量成本,需要将部分进行分流。开始时,使用了传统的php代理来分流,但Go2并发访问极大,多进程架构的php无法承受,在虚拟主机vps上启动数秒后就立即瘫痪。后改用 python的twisted网络架构,采用了twisted的异步tcp通讯功能。运行一段时间后,发现twisted的异步dns稳定性不太好,经常发生系统级的崩溃。最后,经过分析比较,决定采用libevent库来做Go2 的分流代理应用。

Libevent库支持异步socket,支持异步dns,并本身还带了个简单的http 服务器。Go2 的分流代理应用就是使用了libevent库的以上三个功能。
1、简单的http 服务器:实现的分类代理的用户端的输入,输出管理。
2、异步socket,实现了高并发性的用户接入,和高并发性的目的服务器访问。
3、异步dns,解决了dns查询时的并发性和高效性。

下面简单介绍一下Go2 的分流代理程序的主要流程。源代码请参考http://bbs.mycbc.cn/upload/bbs/viewthread.php?tid=601&extra=page%3D1

A.      主程序流程

1、使用event_init()初始化libevent环境;
2、使用evhttp_new()生成http服务实例,并使用evhttp_bind_socket聆听指定的端口;
3、使用evhttp_set_cb设置http服务实例的url回调函数。
4、进入libevent的事件循环。每当有用户请求url,libevent将自动调用url所对应的回调函数。

B.      用户请求处理流程:

1、Libevent监控到用户url访问请求,libevent调用自定义的回调函数。

2、对用户最终访问的url进行base64解码操作,获取到用户访问的目的URL。

3、检查目的URL是否合法,非法,则结束。

4、URL合法,检查是否有本地缓存,有,直接从本地缓存获取数据,返回,并结束。

5、没有本地缓存,则通过libevent的异步dns查询api 对目的域名进行解释,并设置解释完成后的回调函数。Api原型:evdns_resolve_ipv4(host, 0, http_dns_cb, req),其中host是域名, http_dns_cb是解释完成后的回调函数。

6、在回调函数http_dns_cb中,完成以下操作:

a) 使用libevent异步socket连接到指定的服务器,并通过监控socket是否可写来判断连接是否成功。通过设置监听socket写事件来完成这个功能:event_set (&req->proxyev, socket, EV_WRITE, my_connect_cb, req ),其中参数EV_WRITE告诉libevent,只监控socket的可写事件;my_connect_cb是事件发生后的回调函数。

b) 在my_connect_cb函数中,通过监控socket的可读事件,来获取服务器返回的数据:event_set(&req->proxyev, socket, EV_READ, my_read_cb, req ),其中参数EV_READ是告诉libevent只监控socket的可读事件;my_read_cb是当可读事件发生后的回调函数。

c) my_read_cb回调函数则是核心的数据处理函数。负责从服务器中获取数据,并将数据返回给用户。

7、将图片缓存到本地,结束。

Go2 的分流代理在实际应用中,每分钟能处理3000个请求,月流量近TB,而应用则是跑在一台低端的vps上。

参考文档:
1、C10K问题---epoll简介,搜狗实验室, http://www.sogou.com/labs/report/1-1.pdf
2、The C10K problem(英文),http://www.kegel.com/c10k.html
3、libevent 官方网站,http://www.monkey.org/~provos/libevent/

http://www.cnblogs.com/fll/archive/2008/05/17/1201540.html

C10K问题和Libevent库介绍的更多相关文章

  1. libevent库介绍--事件和数据缓冲

    首先在学习libevent库的使用前,我们还要从基本的了解开始,已经熟悉了epoll以及reactor,然后从event_base学习,依次学习事件event.数据缓冲Bufferevent和数据封装 ...

  2. C/C++ 网络库介绍

    C/C++ 网络库介绍 Aggregated List of Libraries(Source Link) Boost.Asio is really good. Asio is also availa ...

  3. libevent简单介绍

    http://blog.csdn.net/mafuli007/article/details/7476014 1      简介 主页:http://www.monkey.org/~provos/li ...

  4. R1:创建Libevent库

    原文链接:http://www.wangafu.net/~nickm/libevent-book/Ref1_libsetup.html Setting up the Libevent library ...

  5. Libevent库学习笔记

    Libevent是一个事件触发的网络库,适用于windows.linux.bsd等多种平台,Libevent在底层select.pool.kqueue和epoll等机制基础上,封装出一致的事件接口.可 ...

  6. MAC OS下编译apple跨平台的libevent库 (可延申到其它第三库)

    apple下的跨平台是指不同设备上的苹果系统以及同一系统在不同cpu体系的不同版本. 前面一篇介绍如何用ndk编译android跨平台的第三库,那样的方法却不能应用在apple上. 网上可以找到这么一 ...

  7. DBoW2库介绍

    DBoW2库是University of Zaragoza里的Lopez等人开发的开源软件库. 由于在SLAM回环检测上的优异表现(特别是ORB-SLAM2),DBoW2库受到了广大SLAM爱好者的关 ...

  8. Alljoyn瘦客户端库介绍(官方文档翻译)

    Alljoyn瘦客户端库介绍(上) 1.简介 本文档对AllJoynTM瘦客户端的核心库文件(AJTCL)进行了详尽的介绍.本文档介绍了系统整体架构,AllJoyn框架结构,并着重于介绍如何将嵌入式设 ...

  9. Lua5.1基本函数库介绍

    Lua5.1基本函数库介绍assert (v [, message])功能:相当于C的断言,参数:v:当表达式v为nil或false将触发错误,message:发生错误时返回的信息,默认为" ...

随机推荐

  1. Dispatcher及线程操作

    WPF 应用程序启动后,会有两个线程: 1. 一个是用来处理UI呈现(处理UI的请求,比如输入和展现等操作). 2. 一个用来管理 UI的 (对UI元素及整个UI进行管理). WPF在线程里面是不可以 ...

  2. LR 取到怎么样才能得到参数列表中的每一个值

    char *m="e"; lr_save_string("helloworld","p4"); lr_eval_string("{ ...

  3. JS 变量提升

    var a = 1; function foo() { console.log(a); var a = 2; } foo(); //undefined 根据变量提升机制,最后得出undefined; ...

  4. HDOJ-ACM1005(JAVA)

    转载声明:原文转自http://www.cnblogs.com/xiezie/p/5502918.html JAVA语言实现: 拿到题目第一反应是简单地实现递归: import java.util.* ...

  5. POJ1502: MPI Maelstrom

    红果果的dijstra算法应用,这里采用邻接表存储图 小插曲:while(scanf("%d",&n))提交时内存超限,改成while(scanf("%d&quo ...

  6. UVa11404 - Palindromic Subsequence(区间DP+打印路径)

    题目大意 给定一个字符串,要求你删除尽量少的字符,使得原字符串变为最长回文串,并把回文串输出,如果答案有多种,则输出字典序最小的 题解 有两种解法,第一种是把字符串逆序,然后求两个字符串的LCS,并记 ...

  7. codeforces 432D Prefixes and Suffixes

    由于包含了前缀与后缀,很容易想到用KMP去算前缀与后缀的公共缀.另外要计算某个后缀在整个串中出现的次数,由于后缀自动机是比较容易求的,然后就直接上后缀自动机了.先分别用KMP算法与后缀自动机跑一遍,然 ...

  8. php升级到5.4

    这里使用 Webtatic EL6的YUM源来安装php5.4,我们首页安装Webtatic EL6 YUM源 rpm -Uvh http://repo.webtatic.com/yum/el6/la ...

  9. [置顶] 斗地主算法的设计与实现--项目介绍&如何定义和构造一张牌

    大学期间,我在别人的基础上,写了一个简易的斗地主程序. 主要实现了面向对象设计,洗牌.发牌.判断牌型.比较牌的大小.游戏规则等算法. 通过这个斗地主小项目的练习,提高了我的面向对象设计能力,加深了对算 ...

  10. 性能优化-列表类型转换(ConvertList<TSource, TResult>)

    之前,在项目中看到过一段通用列表类型转换的代码,直接的实现便是用反射.大概看了下,它用在领域模型转DTO和SOA接口中契约实体的转换等等.首先,它使用了反射,其次,还是在循环中使用,便有了优化的想法. ...