通常,我们写服务器处理模型的程序时候,有以下几种模型:
每收到一个请求,创建一个新的进程,来处理该请求;
每收到一个请求,创建一个新的线程,来处理该请求;
每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求(也就是启动协程) 也就是
事件驱动模型
上面的几种方式,各有千秋,
第一种方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。第二种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。第三种方式,在写应用程序代码时,逻辑比前面两种都复杂。
综合考虑各方面因素,一般普遍认为第三种方式是大多数网络服务器采用的方式。
- 实例一:什么是事件驱动模型?
在ui编程中,经常要对鼠标进行点击,首先如何获取说表点击呢?
方式一:创建一个线程,该线程一直循环检测是否有鼠标点击,那么这个方式有以下缺点:
造成cpu资源的浪费,如果鼠标点击的频率非常小,但是扫面线程还是会一直循环检测,这会造成很多cpu资源的浪费;如果扫描鼠标点击的接口是阻塞的呢?(就是点击鼠标,此时要运行一个任务需要时间),如果我们不但要扫描鼠标还要扫描键盘,由于扫描鼠标阻塞,那么可能永远不会去扫描键盘,而如果要扫描的设备非常多,这就会带来响应时间的问题。
所以,该方式是非常不好的!!!
方式二:事件驱动模型
目前大部分ui都是事件驱动模型,如很多UI平台都会提供onclick()事件。事件驱动模型大体思路如下:
1、有一个事件(消息)队列
2、鼠标按下时,往这个队列中增加一个点击事件(消息)
3、有一个循环,不断的从队列中取出事件,调用不同的函数,如onclick()等
4、事件(消息)一般都各自保存在各自的处理函数指针,这样每个消息都有自己独立的处理函数
事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。另外两种常见的编程范式是(单线程)同步以及多线程编程。(回调机制:可以参考上篇最后一个实例)
注意,io操作是操作系统进行的,不需要程序,那么事件驱动模型,每次遇到io事件就把该事件放在操作系统中的一个队列中,然后等待操作系统返回,而这个返回就是回调机制的使用,返回io执行结果。定义好事件,并且注册一个回调函数,然后放入操作系统的队列
下图展示了随着时间的推移,这三种模式下程序所做的工作。这个程序有3个任务需要完成,每个任务都在等待I/O操作时阻塞自身。阻塞在I/O操作上所花费的时间已经用灰色框标示出来了。
- 实例二:IO多路复用
协程实现了io自动阻塞切换,那么协程在原理上是怎么实现的?也就是我们如何实现在io阻塞时的自动切换?
1、同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?(讨论的背景是Linux环境下的network IO)
http://www.cnblogs.com/alex3714/articles/5876749.html
缓存(标准I/O)I/O是什么?
又称标准I/O,比如socket-server,我们从网卡接收数据,数据会先到达操作系统内核的缓冲区中,再被拷贝到应用程序的地址空间。
缓存I/O的缺点:
数据在传输的过程中需要在应用程序的地址空间和内核进行多次数据拷贝操作,这些数据拷贝的操作带来的cpu,内存的(尤其是内存)的开销是特别大的。
I/O模式
1、阻塞I/O(blocking I/O)
linux中所有的socket默认是阻塞I/O。
这是典型的socket,阻塞I/O。
特点:io执行的等待数据阶段和拷贝数据阶段都被阻塞了。
2、非阻塞I/O non - blocking
linux下,可以通过设置socket使其成为non-blocking I/O.
特点:进程执行recv操作没数据立刻返回error,进程不需要等待。马上进程再次执行recv动作,就不断的询问内核,知道有数据。nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。
注意:只是非阻塞
3、I/O多路复用 (I/O multiplexing model) (事件驱动 I/O)
IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。
阻塞I/O 不能实现socket的并发,但是非阻塞模型在用户看来已经实现了socket的并发,因为用户进程不需要等待,但是拷贝数据还是处于阻塞状态。
select poll epoll就是实现了这个socket并发。不断循环socket链接。
I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。
这种方式和第一种方式的区别就是:阻塞I/O只是一个socket链接,而多路复用 I/O检测很多链接,有一个受到数据就返回,但是进程都会阻塞。
4、异步I/O
用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
特点是:完全不需要用户进程等待
- 实例三:多路复用中的select、poll、epoll
http://www.cnblogs.com/alex3714/p/4372426.html
epoll ,linux2.6以后才支持。windows不支持epoll,windows只支持select。
现在最流行的,nginx在用,包括djang等,python下最牛逼的异步网络框架。
网友评论