分类目录: IO编程

[转]TCP协议疑难杂症全景解析

说明:

1) 本文以TCP的发展历程解析容易引起混淆,误会的方方面面
2) 本文不会贴大量的源码,大多数是以文字形式描述,我相信文字看起来是要比代码更轻松的
3) 针对对象:对TCP已经有了全面了解的人。因为本文不会解析TCP头里面的每一个字段或者3次握手的细节,也不会解释慢启动和快速重传的定义
4) 除了《TCP/IP详解》(卷一,卷二)以及《Unix网络编程》以及Linux源代码之外,学习网络更好的资源是RFC
5) 本文给出一个提纲,如果想了解细节,请直接查阅RFC
6) 翻来覆去,终于找到了这篇备忘,本文基于这篇备忘文档修改。

阅读全文 »

| 1 分2 分3 分4 分5 分 (4.57- 7票) Loading ... Loading ... | 同时归档在:WEB网络 | 标签: |

OpenSSL HeartBleed漏洞原理漫画图解

前几天OpenSSL 的「滴血之心」漏洞造成的恐慌几乎席卷全球,国内外白帽子黑帽子疯狂刷数据刷积分,各大网站的安全部门也是遇到了从未有过的危机。虽然大家都讨论的很热烈,但对于用户们来说,只关心一件事情:我们支付宝里的钱还安全么。

除此以外,好奇的人们或许更想知道OpenSSL的程序员到底犯了什么错误,好在有xkcd这样的geek网站,用最最通俗易懂的方式,向大家展示了这个漏洞的原理和可爱之处。

阅读全文 »

| 1 分2 分3 分4 分5 分 (4.00- 9票) Loading ... Loading ... | 同时归档在:WEB网络, 移动互联 | 标签: , , , |

多核编程入门

image

链接:下载

| 1 分2 分3 分4 分5 分 (4.43- 7票) Loading ... Loading ... | 同时归档在:多线程编程 | 标签: |

[转] 高并发系统设计

一、服务器内部设计

服务器设计涉及Socket的阻塞/非阻塞,操作系统IO的同步和异步(之前被人问到过两次。第一次让我说说知道的网络模型,我说ISO模型和TCP/IP模型,结果被鄙视了。最后人说了解linux epoll吗?不了解呀!汉,回去查资料才知道是这回事。第二次让我说说知道线程模型,汉!这个名词感觉没有听说过,线程?模型?半同步/半异步,领导者/跟随者知道吗。再汉,我知道同步/异步,还有半同步/半异步?啥呀?领导者/跟随者,我现在没有领导。回去一顿恶补,原来是ACE框架里边经常有这样的提法,Reactor属于同步/半同步,PREACTOR属于领导者/跟随者模式。瀑布汗。小插曲一段,这些不懂没关系,下边我慢慢分解),事件分离器,线程池等。内部设计希望通过各个模块的给出一个简单设计,经过您的进一步的组合和打磨,就可以实现一个基本的高并发服务器。

1. Java高并发服务器

Java设计高并发服务器相对比较简单。直接是用ServerSocket或者Channel+selector实现。前者属于同步IO设计,后者采用了模拟的异步IO。为什么说模拟的异步IO呢?记得网上看到一篇文章分析了java的selector。在windows上通过建立一个127.0.0.1到127.0.0.1的连接实现IO的异步通知。在linux上通过建立一个管道实现IO的异步通知。考虑到高并并发系统的要求和java上边的异步IO的限制(通常操作系统同时打开的文件数是有限制的)和效率问题,java的高并发服务器设计不做展开深入的分析,可以参考C高并发服务器的分析做同样的设计。

2. C高并发服务器设计
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的时候,发送请求后,结果是通过事件通知的。这是产生了事件分离器的需求。事件分离器主要任务是管理和分离不同文件描述符上的所发生的事件,让后通知相应的事件,派发相应的动作。

Ø 线程池

线程池基本上比较简单,实现线程的借入和借出,创建和销毁。最完好可以做到通过一个事件触发一个线程开始工作(注:在epoll中,事件触发又分为边沿触发和水平触发)。

2) 常见的设计模式

根据Socket的阻塞非阻塞,IO的同步和异步。可以分为如下4中情形

阻塞同步 阻塞异步
非阻塞同步 非阻塞异步

阻塞同步方式是原始的方式,也是许多教科书上介绍的方式,因为Socket和IO默认的为阻塞和同步方式。基本流程如下:

listen_fd = socket( AF_INET,SOCK_STREAM,0 )

bind( listen_fd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr_in))

listen( listen_fd,1 )

accept( listen_fd, (struct sockaddr*)&remote_addr,&addr_len )

recv( accept_fd ,&in_buf ,1024 ,0 )

close(accept_fd)

阻塞异步方式有所改进,但是Socket的阻塞方式,前一个连接没有处理完成,下一个连接不能接入,是高并发服务器所不可接收的方式。只不过在上边阻塞同步方式的基础上使用select(严格来说select是一种IO多路服用技术。因为linux尚没有完整的实现异步IO,而winsock实在理解socket没有linux上面那么直观。,这里为了方便,没有做严格的区分)或者其它异步IO方式。

非阻塞同步方式,通过设置socket选项为NONBLOCK,可以很快的接收连接,但是处理采用同步IO方式,服务器处理性能也比较差。

上边三种方式不做深入介绍。下边主要从非阻塞异步IO方式介绍。

非阻塞异步IO方式中,由于异步IO方式在同一系统可能有多种实现,不同系统也有不同实现,下边介绍几种常见的IO方式和服务器框架。

Ø Select

Select采用轮训注册的fd方式。是一种比较老的IO多路服用实现方式,效率相对要差一些。Select方式在windows和linux上都支持。

基本框架如下:


socket( AF_INET,SOCK_STREAM,0 )
fcntl(listen_fd, F_SETFL,flags|O_NONBLOCK);
bind( listen_fd, (structsockaddr *)&my_addr,sizeof(struct sockaddr_in))
listen( listen_fd,1 )
FD_ZERO( &fd_sets );
FD_SET(listen_fd,&fd_sets);
for(k=0; k<=i; k++){

FD_SET(accept_fds[k],&fd_sets);
}
events = select( max_fd + 1,&fd_sets, NULL, NULL, NULL );
if(FD_ISSET(listen_fd,&fd_sets) ){
accept_fd = accept( listen_fd, (structsockaddr
*)&remote_addr,&addr_len );
}
for( j=0; j<=i; j++ ){
if(
FD_ISSET(accept_fds[j],&fd_sets) ){

recv( accept_fds[j] ,&in_buf ,1024 ,0 );
}
}

Ø Epoll

Epoll是linux2.6内核以后支持的一种高性能的IO多路服用技术。服务器框架如下:

socket( AF_INET,SOCK_STREAM,0 )
fcntl(listen_fd, F_SETFL,flags|O_NONBLOCK);
bind( listen_fd, (structsockaddr *)&my_addr,sizeof(struct sockaddr_in))
listen( listen_fd,1 )
epoll_ctl(epfd,EPOLL_CTL_ADD,listen_fd,&ev);
ev_s = epoll_wait(epfd,events,20,500 );
for(i=0; i<ev_s;i++){

if(events[i].data.fd==listen_fd){

accept_fd = accept( listen_fd,(structsockaddr *)&remote_addr,&addr_len
);

fcntl(accept_fd, F_SETFL,flags|O_NONBLOCK);

epoll_ctl(epfd,EPOLL_CTL_ADD,accept_fd,&ev);

}

else if(events[i].events&EPOLLIN){

recv( events[i].data.fd ,&in_buf,1024 ,0 );

}
}

Ø AIO

在windows上微软实现了异步IO,通过AIO可以方便的实现高并发的服务器。框架如下:

WSAStartup( 0x0202 , & wsaData)
CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL, 0 , 0 )
WSASocket(AF_INET,SOCK_STREAM, 0 , NULL, 0 , WSA_FLAG_OVERLAPPED)
bind(Listen, (PSOCKADDR) & InternetAddr, sizeof
(InternetAddr))
listen(Listen, 5 )
WSAAccept(Listen, NULL, NULL,NULL, 0 )
PerHandleData =(LPPER_HANDLE_DATA) GlobalAlloc(GPTR, sizeof
(PER_HANDLE_DATA)
CreateIoCompletionPort((HANDLE)Accept, CompletionPort, (DWORD) PerHandleData,
0 )
PerIoData= (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof
(PER_IO_OPERATION_DATA))
WSARecv(Accept,&(PerIoData->DataBuf),1,&RecvBytes,&Flags,&(PerIoData->Overlapped),
NULL)
(GetQueuedCompletionStatus(CompletionPort, & BytesTransferred,
(LPDWORD) &
PerHandleData,(LPOVERLAPPED * ) & PerIoData, INFINITE)
if (PerIoData -> BytesRECV >PerIoData ->
BytesSEND){
WSASend(PerHandleData-> Socket, & (PerIoData
->DataBuf), 1 , & SendBytes, 0 ,

& (PerIoData ->Overlapped), NULL)
}

3) 引入线程池和事件分离器后

由于上边只是单纯的使用非阻塞Socket和异步IO的方式。提高了接收连接和处理的速度。但是还是不能解决两个客户端同时连接的问题。这时就需要引入多线程机制。引入多线程后,又有许多策略。Linux上通常采用主进程负责接收连接,之后fork子进程处理连接。Windows通常采用线程池方式,避免线程创建和销毁的开销,当然linux上也可以采用线程池方式。采用多进程和多线程方式后。事件处理也可以再优化,定义一个简单的事件处理器,把所有事件放入一个队列,各个线程去事件队列取相应的事件,然后自己开始工作。这就是我上边提到的半同步/半异步方式了。如果线程工作的时候是接收到连接后,自己处理后续的发送和接收,然后选出另外一个线程作为领导继续接收连接,其它线程作为追随者。这就是领导者/追随者模式了。具体可以参考ACE的Reactor和Preactor的具体实现。半同步和/半异步网上也有很多的讨论,可以自己深入研究。代码就比较复杂了,这里就不给出代码了。

二、分布式系统设计

前面讲述了分布式系统中的核心的服务器的实现。可以是http服务器,缓存服务器,分布式文件系统等的内部实现。下边主要从一个高并发的大型网站出发,看一个高并发系统的设计。下边是一个高并发系统的逻辑结构:

分布式系统设计

主要是参考这篇文章http://www.chinaz.com/web/2010/0310/108211.shtml。下边主要想从这个架构的各个部分的实现展开。

1. 缓存系统

缓存是每一个高并发,高可用系统不可或缺的模块。下边就几个常见缓存系统系统进行介绍。

Squid

Squid作为一个前端缓存,通常部署在网络的离用户最近的地方,通过缓存网站的页面,使用户不必每次都跑到服务器去取数据,提高系统响应和性能。实现应该比较简单:一个带有存储功能的代理。用户访问页面的时候,由它代理,然后存储请求结果,下次再访问的时候,查看是否需要更新,有更新就去服务器取新数据,否则直接返回用户页面。

Ehcache

Ehcache是一个对象缓存系统。通常在J2EE中配合Hibernate使用,这里请原谅作者本人之前是做J2EE开发的,其它使用方式暂不是很了解。应用查询数据库,对经常需要查询,却更新不频繁的数据,可以放入ehcache缓存,提高访问速度。Ehcahe支持内存缓存和硬盘两种方式,支持分布式缓存。数据缓存的基本原理就是:为需要缓存的对象建立一个map,临时对象放入map,查询的时候先查询map,没有找到再查找数据库。关机时可以序列化到硬盘。分布式缓存没有研究过。

页面缓存和动态页面静态化

在大型网站经常使用的一种缓存技术就是动态页面的缓存。由于动态页面经常更新,上边的缓存就不起作用了。通常会采用SSI(Server side include)等技术将动态页面的或者页面片段进行缓存。

还有一种就是动态页面静态化。

2. 负载均衡系统

Ø 负载均衡策略

负载均衡策略有随机分配,平均分配,分布式一致性hash等。随机分配就是通过随机数选择一个服务器来服务。平均分配就是一次循环分配一次。分布式一致性hash算法,比较负载,把资源和节点映射到一个换上,然后通过一定的算法资源对应到节点上,使得添加和去掉服务器变得非常容易,减少对其它服务器的影响。很有名的一个算法,据说是P2P的基础。了解不是很深,就不详细说了,要露马脚了。

Ø 软件负载均衡

软件负载均衡可以采用很多方案,常见的几个方案有:

基于DNS的负载均衡,通过DNS正向区域的配置,将一个域名根据一定的策略解析到多个ip地址,实现负载均衡,这里需要DNS服务器的配合。
基于LVS的负载均衡。LVS可以将多个linux服务器做成一个虚拟的服务器,对外提供服务器,实现负载均衡。
基于Iptables的负载均衡。Iptables可以通过做nat,对外提供一个虚拟IP,对内映射到多个服务器实现负载均衡。基本上可以和硬件均衡方案一致了,这里的linux服务器相当于一台路由器。

Ø 硬件负载均衡

基于路由器的负载均衡,在路由器上配置nat实现负载均衡。对外网一个虚拟IP,内网映射几个内网IP。一些网络设备厂商也提供了一些负载均衡的设备,如F5,不过价格不菲哦。数据库的负载均衡数据库的负载均衡可以是数据库厂商提供的集群方案。

云计算

转载信息:

作者:周顺利
原文链接:http://blog.csdn.net/shatty/article/details/6629896
注:本文大多数观点和代码都是从网上或者开源代码中抄来的,为了疏理和组织这片文章,作者也费了不少心血,为了表示对我劳动的尊重,请转载时注明作者和出处。

| 1 分2 分3 分4 分5 分 (4.50- 6票) Loading ... Loading ... | 同时归档在:C/C++, 多线程编程 | 标签: , , , , , |

Netty和Jetty的Java NIO 网络框架模型分析

Netty的NIO框架模型。在以前的文章中,为解决Jetty的问题,分析过Java NIO基于多路事件分离器的异步IO框架模型。一直都没有系统分析Netty和Jetty的网络模型,这两天将二者的网络框架部分的代码仔细读了一下,整理了二者的网络模型,画出了Netty的模型图:

netty_network_frame_model

在图中,每个侦听都会创建一个Acceptor Reactor,由Boss线程来监控多路分离器,这里只关注连接事件,当有新的建立连接请求达到时,该线程会第一时间响应,将接收到的请求注册到事件多路分离器中,事件多路分离器有多个,默认情况下其个数为CPU核心数的两倍,应该是CPU超线程的数目。这里会给每一个达到的连接编一个序号,将序号对分离器个数取模(hash到0~3的一维空间),根据模值分配给相应的分离器。事件分离器开始监听新的连接上面的读写事件。检查线程为NioWorker。读写数据会通过回调用户注册的handler的相应接口来实现。因此,处理耗时数据的情况下,需要用户将其提交给后台线程,而不应该阻塞事件分离器,否则会导致新的连接无法建立,其他并发请求无法处理。

Jetty在代码风格上面跟Netty差别很大,看jetty代码感觉更清晰一点,可能是因为以前处理问题已经看得非常多了。前面的文章也说过,Jetty是在一个线程中调用一个同步的accept()方法来等待新的连接请求,等到新的连接到来时,就生成新的change事件放到多路分离器中,同样也是有多个多路分离器,选取原则与Netty完全一样。简单的轮询实现负载均衡。这是典型的半同步半异步(Half-Sync/Half-Async)的模式。在只使用1个事件分离器时,会发现分发线程通常会引入很多问题。前面两篇文章中提到的问题分析都跟这个有关。

不知道Jetty与Netty为什么在接受新请求这里有差别,难道Netty的方式更利于处理短连接,而Jetty则更利于处理长连接,比如Http连接?优待进一步的并发测试才能说明问题。如果netty的方式很好,那么Jetty应该也早就改成了该方式。

| 1 分2 分3 分4 分5 分 (5.00- 9票) Loading ... Loading ... | 同时归档在:Jetty, Netty | 标签: , , |

温习一下AIO编程,AIO编程简单实例

完成端口(AIO)是Windows下开发多并发网络连接、异步服务器程序的最常用方案。JDK 1.7版本引入了AIO的相关封装。这里把之前写过的一个AIO例子发出来温习一下。为下一步分析JDK的AIO做一些准备工作。

下面的例子是AIO与线程池结合的例子,每个连接在建立之后即调用一次receive方法,这里的receive是不阻塞主线程的继续运行接受其他连接的。可以把这个调用想象成一个事件注册的过程,告诉内核对象,我关心这样的读取数据事件,当数据过来时,先把它收下来,放到之前用参数指定的内存缓冲区中,然后通知一下我就可以了。子线程就在等待数据接收完成的完成端口上面等待,直到数据接收完成,处理数据。让繁杂的数据接收拷贝过程交给内核对象去完成。使得线程管理和编程更加简单。

阅读全文 »

| 1 分2 分3 分4 分5 分 (4.80- 5票) Loading ... Loading ... | 同时归档在:C/C++ |
返回顶部