服务器如何实现高性能
|
为什么我们要使用异步这种难以理解的方式编程呢? 是因为阻塞式编程虽然容易理解但会导致线程被阻塞而暂停运行。 那么聪明的你一定会问了,有没有一种方法既能结合同步IO的简单理解又不会因同步调用导致线程被阻塞呢? 答案是肯定的,这就是用户态线程,user level thread,也就是大名鼎鼎的协程,关于协程值得单独拿出一篇文章来讲解,就在下一篇。 虽然基于事件编程有这样那样的缺点,但是在当今的高性能高并发服务器上基于事件编程方式依然非常流行,但已经不是纯粹的基于单一线程的事件驱动了,而是event loop + multi thread + user level thread。 在基于事件驱动编程时有一条注意事项,那就是不允许发起阻塞式IO。 有的同学可能会问,如果不能发起阻塞式IO的话,那么该怎样进行IO操作呢? 有阻塞式IO,就有非阻塞式IO。 非阻塞IO 为克服阻塞式IO所带来的问题,现代操作系统开始提供一种新的发起IO请求的方法,这种方法就是异步IO,对应的,阻塞式IO就是同步IO,关于同步和异步这两个概念可以参考《从小白到高手,你需要理解同步与异步》。 异步IO时,假设调用aio_read函数(具体的异步IO API请参考具体的操作系统平台),也就是异步读取,当我们调用该函数后可以立即返回,并继续其它事情,虽然此时该文件可能还没有被读取,这样就不会阻塞调用线程了。此外,操作系统还会提供其它方法供调用线程来检测IO操作是否完成。 就这样,在操作系统的帮助下IO的阻塞调用问题也解决了。 在《终于明白了,一文彻底理解I/O多路复用》这篇文章中我们知道,在Linux/Unix世界中一切皆文件,而我们的程序都是通过文件描述符来进行I/O操作的,当然对于socket也不例外,那我们该如何同时处理多个文件描述符呢? IO多路复用技术正是用来解决这一问题的,通过IO多路复用技术,我们一次可以监控多个文件描述,当某个文件(socket)可读或者可写的时候我们就能得到通知啦。 这样IO多路复用技术就成了event loop的原材料供应商,源源不断的给我们提供各种event,这样关于event来源的问题就解决了。 有的同学可以依然不明白为什么这样一个event loop可以同时处理多个请求呢? 原因很简单,对于web服务器来说,处理一个用户请求时大部分时间其实都用在了I/O操作上,像数据库读写、文件读写、网络读写等。当一个请求到来,简单处理之后可能就需要查询数据库等I/O操作,我们知道I/O是非常慢的,当发起I/O后我们大可以不用等待该I/O操作完成就可以继续处理接下来的用户请求。 由于线程共享进程地址空间,这在为线程间通信带来便利的同时也带来了无尽的麻烦。 正是由于线程间共享地址空间,因此一个线程崩溃会导致整个进程崩溃退出,同时线程间通信简直太简单了,简单到线程间通信只需要直接读取内存就可以了,也简单到出现问题也极其容易,死锁、线程间的同步互斥、等等,这些极容易产生bug,无数程序员宝贵的时间就有相当一部分用来解决多线程带来的无尽问题。 虽然线程也有缺点,但是相比多进程来说,线程更有优势,但想单纯的利用多线程就能解决高并发问题也是不切实际的。
因为虽然线程创建开销相比进程小,但依然也是有开销的,对于动辄数万数十万的链接的高并发服务器来说,创建数万个线程会有性能问题,这包括内存占用、线程间切换,也就是调度的开销。 (编辑:南昌站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

