注:本文大多数观点和代码都是从网上或者开源码中抄来的,为了疏理和组织这片文章,作者也费了不少心血,为了表示对我劳动的尊重,请转载时注明作者和出处。
一、引子
近期失业在家,闲来无事。
通过网上查找资料和查看开源码。研究了一下互联网高并发系统的一些设计。这里主要从server内部设计和整个系统设计两个方面讨论,很多其它的是从互联网大型站点设计方面考虑,高性能计算之类系统没有研究过。
二、server内部设计
server设计涉及Socket的堵塞/非堵塞。操作系统IO的同步和异步(之前被人问到过两次。
第一次让我说说知道的网络模型。我说ISO模型和TCP/IP模型。结果被歧视了。最后人说了解linux epoll吗?不了解呀!
汉,回去查资料才知道是这回事。第二次让我说说知道线程模型。汉!这个名词感觉没有听说过,线程?模型?半同步/半异步,领导者/尾随者知道吗。
再汉。我知道同步/异步。还有半同步/半异步?啥呀?领导者/尾随者。我如今没有领导。回去一顿恶补,原来是ACE框架里边常常有这种提法,Reactor属于同步/半同步,PREACTOR属于领导者/尾随者模式。瀑布汗。
小插曲一段,这些不懂没关系,下边我慢慢分解),事件分离器,线程池等。内部设计希望通过各个模块的给出一个简单设计,经过您的进一步的组合和打磨,就能够实现一个主要的高并发server。
1.Java高并发server
Java设计高并发server相对照较简单。
直接是用ServerSocket或者Channel+selector实现。前者属于同步IO设计,后者採用了模拟的异步IO。
为什么说模拟的异步IO呢?记得网上看到一篇文章分析了java的selector。
在windows上通过建立一个127.0.0.1到127.0.0.1的连接实现IO的异步通知。在linux上通过建立一个管道实现IO的异步通知。考虑到高并并发系统的要求和java上边的异步IO的限制(通常操作系统同一时候打开的文件数是有限制的)和效率问题。java的高并发server设计不做展开深入的分析,能够參考C高并发server的分析做相同的设计。
2.C高并发server设计
1)基本概念
Ø堵塞和非堵塞socket
所谓堵塞Socket。是指其完毕指定的任务之前不同意程序调用还有一个函数,在Windows下还会堵塞本线程消息的发送。
所谓非堵塞Socket,是指操作启动之后,假设能够马上得到结果就返回结果。否则返回表示结果须要等待的错误信息。不等待任务完毕函数就返回。
一个比較有意思的问题是accept的Socket是堵塞的还是非堵塞的呢?下边是MSDN上边的一段话:The accept function extracts thefirst connection on the queue of pending connections on socket s. It thencreates and returns a handle to the new socket. The newly created socket is thesocket that will handle the actual connection; it has the same properties assocket s, including the asynchronous events registered with the WSAAsyncSelector WSAEventSelect functions.
Ø同步/异步IO
有两种类型的文件IO同步:同步文件IO和异步文件IO。
异步文件IO也就是重叠IO。
在同步文件IO中。线程启动一个IO操作然后就马上进入等待状态。直到IO操作完毕后才醒来继续运行。而异步文件IO方式中,线程发送一个IO请求到内核,然后继续处理其它的事情。内核完毕IO请求后。将会通知线程IO操作完毕了。
假设IO请求须要大量时间运行的话,异步文件IO方式能够显著提高效率。由于在线程等待的这段时间内。CPU将会调度其它线程进行运行。假设没有其它线程须要运行的话,这段时间将会浪费掉(可能会调度操作系统的零页线程)。假设IO请求操作非常快。用异步IO方式反而还低效,还不如用同步IO方式。
同步IO在同一时刻仅仅同意一个IO操作,也就是说对于同一个文件句柄的IO操作是序列化的,即使使用两个线程也不能同一时候对同一个文件句柄同一时候发出读写操作。重叠IO同意一个或多个线程同一时候发出IO请求。异步IO在请求完毕时,通过将文件句柄设为有信号状态来通知应用程序,或者应用程序通过GetOverlappedResult察看IO请求是否完毕,也能够通过一个事件对象来通知应用程序。高并发系统通常採用异步IO方式提高系统性能。
Ø事件分离器
事件分离器的概念是针对异步IO来说的。在同步IO的情况下。运行操作等待返回结果,不要事件分离器。异步IO的时候。发送请求后。结果是通过事件通知的。这是产生了事件分离器的需求。事件分离器主要任务是管理和分离不同文件描写叙述符上的所发生的事件。让后通知对应的事件,派发对应的动作。
下边是lighthttpd事件分离器定义:
- /**
- *fd-eventhandlerforselect(),poll()andrt-signalsonLinux2.4
- *
- */
- typedefstructfdevents{
- fdevent_handler_ttype;
- fdnode**fdarray;
- size_tmaxfds;
- #ifdefUSE_LINUX_SIGIO
- intin_sigio;
- intsignum;
- sigset_tsigset;
- siginfo_tsiginfo;
- bitset*sigbset;
- #endif
- #ifdefUSE_LINUX_EPOLL
- intepoll_fd;
- structepoll_event*epoll_events;
- #endif
- #ifdefUSE_POLL
- structpollfd*pollfds;
- size_tsize;
- size_tused;
- buffer_intunused;
- #endif
- #ifdefUSE_SELECT
- fd_setselect_read;
- fd_setselect_write;
- fd_setselect_error;
- fd_setselect_set_read;
- fd_setselect_set_write;
- fd_setselect_set_error;
- intselect_max_fd;
- #endif
- #ifdefUSE_SOLARIS_DEVPOLL
- intdevpoll_fd;
- structpollfd*devpollfds;
- #endif
- #ifdefUSE_FREEBSD_KQUEUE
- intkq_fd;
- structkevent*kq_results;
- bitset*kq_bevents;
- #endif
- #ifdefUSE_SOLARIS_PORT
- intport_fd;
- #endif
- int(*reset)(structfdevents*ev);
- void(*free)(structfdevents*ev);
- int(*event_add)(structfdevents*ev,intfde_ndx,intfd,intevents);
- int(*event_del)(structfdevents*ev,intfde_ndx,intfd);
- int(*event_get_revent)(structfdevents*ev,size_tndx);
- int(*event_get_fd)(structfdevents*ev,size_tndx);
- int(*event_next_fdndx)(structfdevents*ev,intndx);
- int(*poll)(structfdevents*ev,inttimeout_ms);
- int(*fcntl_set)(structfdevents*ev,intfd);
- }fdevents;
- fdevents*fdevent_init(size_tmaxfds,fdevent_handler_ttype);
- intfdevent_reset(