返璞归真、回归实质,这些技能特色背后的底层事理到底是什么?如何能普通易懂、绝不费力真正透彻理解这些技能背后的事理,正是《从根上理解高性能、高并发》系列文章所要分享的。
1.2 文章源起我整理了相称多有关IM、推送等即时通讯技能干系的资源和文章,从最开始的开源IM框架MobileIMSDK,到网络编程经典巨著《TCP/IP详解》的在线版本,再到IM开拓纲领性文章《新手入门一篇就够:从零开拓移动端IM》,以及网络编程由浅到深的《网络编程
越往知识的深处走,越以为对即时通讯技能理解的太少。于是后来,为了让开发者门更好地从根本电信技能的角度理解网络(尤其移动网络)特性,我跨专业网络整理了《IM开拓者的零根本通信技能入门》系列高阶文章。这系列文章已然是普通即时通讯开拓者的网络通信技能知识边界,加上之前这些网络编程资料,办理网络通信方面的知识盲点基本够用了。
对付即时通讯IM这种系统的开拓来说,网络通信知识确实非常主要,但回归到技能实质,实现网络通信本身的这些技能特色:包括上面提到的线程池、零拷贝、多路复用、事宜驱动等等,它们的实质是什么?底层事理又是若何?这便是整理本系列文章的目的,希望对你有用。

《从根上理解高性能、高并发(一):深入打算机底层,理解线程与线程池》
《从根上理解高性能、高并发(二):深入操作系统,理解I/O与零拷贝技能》
《从根上理解高性能、高并发(三):深入操作系统,彻底理解I/O多路复用》
《从根上理解高性能、高并发(四):深入操作系统,彻底理解同步与异步》
《从根上理解高性能、高并发(五):深入操作系统,理解高并发中的协程》
《从根上理解高性能、高并发(六):普通易懂,高性能做事器到底是如何实现的》( 本文)
1.4 本篇概述接上篇《从根上理解高性能、高并发(五):深入操作系统,理解高并发中的协程》,本篇是高性能、高并发系列的第6篇文章(也是完结篇)。
本篇是本系列文章的完结篇,你将能理解到,一个范例的做事器端是如何利用前5篇中讲解的各单项技能从而实现高性能高并发的。
2、本文作者应作者哀求,不供应真名,也不供应个人照片。
本文作者紧张技能方向为互联网后端、高并发高性能做事器、检索引擎技能,网名是“码农的荒岛求生”。感谢作者的无私分享。
3、正文弁言当你在阅读本篇文章的时候,有没有想过,做事器是怎么把这篇文章发送给你的呢?
提及来很大略:不便是一个用户要求吗?做事器根据要求从数据库中捞出这篇文章,然后通过网络发回去吗。
实在有点繁芜:做事器端到底是如何并行处理成千上万个用户要求的呢?这里面又涉及到哪些技能呢?
这篇文章便是来为你解答这个问题的。
4、多进程
历史上最早涌现也是最大略的一种并行处理多个要求的方法便是利用多进程。
比如在Linux天下中,我们可以利用fork、exec等系统调用创建多个进程,我们可以在父进程中吸收用户的连接要求,然后创建子进程去处理用户要求。
就像这样:
这种方法的优点就在于:
1)编程大略,非常随意马虎理解;2)由于各个进程的地址空间是相互隔离的,因此一个进程崩溃后并不会影响其它进程;3)充分利用多核资源。多进程并行处理的优点很明显,但是缺陷同样明显:
1)各个进程地址空间相互隔离,这一优点也会变成缺陷,那便是进程间要想通信就会变得比较困难,你须要借助进程间通信(IPC,interprocess communications)机制,想一想你现在知道哪些进程间通信机制,然后让你用代码实现呢?显然,进程间通信编程相对繁芜,而且性能也是一大问题;2)我们知道创建进程开销是比线程要大的,频繁的创建销毁进程无疑会加重系统包袱。幸好,除了进程,我们还有线程。
5、多线程
不是创建进程开销大吗?不是进程间通信困难吗?这些对付线程来说统统不是问题。
什么?你还不理解线程,赶紧看看这篇《深入打算机底层,理解线程与线程池》,这里详细讲解了线程这个观点是怎么来的。
由于线程共享进程地址空间,因此线程间通信天然不须要借助任何通信机制,直接读取内存就好了。
线程创建销毁的开销也变小了,要知道线程就像寄居蟹一样,屋子(地址空间)都是进程的,自己只是一个租客,因此非常的轻量级,创建销毁的开销也非常小。
我们可以为每个要求创建一个线程,纵然一个线程因实行I/O操作——比如读取数据库等——被壅塞停息运行也不会影响到其它线程。
就像这样:
但线程便是完美的、包治百病的吗,显然,打算机天下从来没有那么大略。
由于线程共享进程地址空间,这在为线程间通信带来便利的同时也带来了无尽的麻烦。
正是由于线程间共享地址空间,因此一个线程崩溃会导致全体进程崩溃退出,同时线程间通信切实其实太大略了,大略到线程间通信只须要直接读取内存就可以了,也大略到涌现问题也极其随意马虎,去世锁、线程间的同步互斥、等等,这些极随意马虎产生bug,无数程序员宝贵的韶光就有相称一部分用来办理多线程带来的无尽问题。
虽然线程也有缺点,但是比较多进程来说,线程更有上风,但想纯挚的利用多线程就能办理高并发问题也是不切实际的。
由于虽然线程创建开销比较进程小,但依然也是有开销的,对付动辄数万数十万的链接的高并发做事器来说,创建数万个线程会有性能问题,这包括内存占用、线程间切换,也便是调度的开销。
因此,我们须要进一步思考。
6、事宜驱动:Event Loop
到目前为止,我们提到“并行”二字就会想到进程、线程。
但是:并行编程只能依赖这两项技能吗?并不是这样的!
还有另一项并行技能广泛运用在GUI编程以及做事器编程中,这便是近几年非常盛行的事宜驱动编程:event-based concurrency。
PS:搞IM做事端开拓的程序员肯定不陌生,著名的Java NIO高性能网络编程框架Netty中EvenLoop 这个接口意味着什么(有关Netty框架的高性能事理可以读这篇《新手入门:目前为止最透彻的的Netty高性能事理和框架架构解析》)。
大家不要以为这是一项很难懂的技能,实际上事宜驱动编程事理上非常大略。
这一技能须要两种质料:
1)event;2)处理event的函数,这一函数常日被称为event handler;剩下的就大略了:你只须要安静的等待event到来就好,当event到来之后,检讨一下event的类型,并根据该类型找到对应的event处理函数,也便是event handler,然后直接调用该event handler就好了。
That's it !
以上便是事宜驱动编程的全部内容,是不是很大略!
从上面的谈论可以看到:我们须要不断的吸收event然后处理event,因此我们须要一个循环(用while或者for循环都可以),这个循环被称为Event loop。
利用伪代码表示便是这样:
while(true) {
event = getEvent();
handler(event);
}
Event loop中要做的事情实在是非常大略的,只须要等待event的带来,然后调用相应的event处理函数即可。
把稳:这段代码只须要运行在一个线程或者进程中,只须要这一个event loop就可以同时处理多个用户要求。
有的同学可以依然不明白:为什么这样一个event loop可以同时处理多个要求呢?
缘故原由很大略:对付网络通信做事器来说,处理一个用户要求时大部分韶光实在都用在了I/O操作上,像数据库读写、文件读写、网络读写等。当一个要求到来,大略处理之后可能就须要查询数据库等I/O操作,我们知道I/O是非常慢的,当发起I/O后我们大可以不用等待该I/O操作完造诣可以连续处理接下来的用户要求。
现在你该当明白了吧:虽然上一个用户要求还没有处理完我们实在就可以处理下一个用户要求了,这也是并行,这种并行就可以用事宜驱动编程来处理。
这就好比餐厅做事员一样:一个做事员不可能一贯等上一个顾客下单、上菜、用饭、买单之后才接待下一个顾客,做事员是怎么做的呢?当一个顾客下完单后直接处理下一个顾客,当顾客吃完饭后会自己回来买单结账的。
看到了吧:同样是一个做事员也可以同时处理多个顾客,这个做事员就相称于这里的Event loop,纵然这个event loop只运行在一个线程(进程)中也可以同时处理多个用户要求。
相信你已经对事宜驱动编程有一个清晰的认知了,那么接下来的问题便是,这个事宜也便是event该怎么获取呢?
7、事宜来源:IO多路复用在《深入操作系统,彻底理解I/O多路复用》这篇文章中我们知道,在Linux/Unix天下中统统皆文件,而我们的程序都是通过文件描述符来进行I/O操作的,当然对付网络编程中的socket也不例外。
那我们该如何同时处理多个文件描述符呢?
IO多路复用技能正是用来办理这一问题的:通过IO多路复用技能,我们一次可以监控多个文件描述,当某个“文件”(实际可能是im网络通信中socket)可读或者可写的时候我们就能得到关照啦。
这样IO多路复用技能就成了event loop的原材料供应商,源源不断的给我们供应各种event,这样关于event来源的问题就办理了。
当然:关于IO多路复用技能的详细讲解请拜会《深入操作系统,彻底理解I/O多路复用》,本文作为纲领性文章,就不再赘述了。
至此:关于利用事宜驱动来实现并发编程的所有问题都办理了吗?event的来源问题办理了,当得到event后调用相应的handler,看上客岁夜功告成了。
想一想还有没有其它问题?
8、问题:壅塞式IO
现在:我们可以利用一个线程(进程)就能基于事宜驱动进行并行编程,再也没有了多线程中让人恼火的各种锁、同步互斥、去世锁等问题了。
但是:打算机科学中从来没有涌现过一种能办理所有问题的技能,现在没有,在可预期的将来也不会有。
那上述方法有什么问题吗?
不要忘了,我们event loop是运行在一个线程(进程),这虽然办理了多线程问题,但是如果在处理某个event时须要进行IO操作会怎么样呢?
在《深入操作系统,理解I/O与零拷贝技能》一文中,我们讲解了最常用的文件读取在底层是如何实现的,程序员最常用的这种IO办法被称为壅塞式IO。
也便是说:当我们进行IO操作,比如读取文件时,如果文件没有读取完成,那么我们的程序(线程)会被壅塞而停息实行,这在多线程中不是问题,由于操作系统还可以调度其它线程。
但是:在单线程的event loop中是有问题的,缘故原由就在于当我们在event loop中实行壅塞式IO操作时全体线程(event loop)会被停息运行,这时操作系统将没有其它线程可以调度,由于系统中只有一个event loop在处理用户要求,这样当event loop线程被壅塞停息运行时所有用户要求都没有办法被处理。你能想象当做事器在处理其它用户要求读取数据库导致你的要求被停息吗?
因此:在基于事宜驱动编程时有一条把稳事变,那便是不许可发起壅塞式IO。
有的同学可能会问,如果不能发起壅塞式IO的话,那么该若何进行IO操作呢?
PS:有壅塞式IO,就有非壅塞式IO。我们连续往下谈论。
9、办理方法:非壅塞式IO为战胜壅塞式IO所带来的问题,当代操作系统开始供应一种新的发起IO要求的方法,这种方法便是异步IO。对应的,壅塞式IO便是同步IO,关于同步和异步这两个观点可以参考《从根上理解高性能、高并发(四):深入操作系统,彻底理解同步与异步》。
异步IO时,假设调用aio_read函数(详细的异步IO API请参考详细的操作系统平台),也便是异步读取,当我们调用该函数后可以立即返回,并连续其它事情,虽然此时该文件可能还没有被读取,这样就不会壅塞调用线程了。此外,操作系统还会供应其它方法供调用线程来检测IO操作是否完成。
就这样,在操作系统的帮助下IO的壅塞调用问题也办理了。
10、基于事宜驱动并行编程的难点
虽然有异步IO来办理event loop可能被壅塞的问题,但是基于事宜编程依然是困难的。
首先:我们提到,event loop是运行在一个线程中的,显然一个线程是没有办法充分利用多核资源的,有的同学可能会说那就创建多个event loop实例不就可以了,这样就有多个event loop线程了,但是这样一来多线程问题又会涌现。
另一点在于编程方面,在《从根上理解高性能、高并发(四):深入操作系统,彻底理解同步与异步》这篇文章中我们讲到过,异步编程须要结合回调函数(这种编程办法须要把处理逻辑分为两部分:一部分调用方自己处理,另一部分在回调函数中处理),这一编程办法的改变加重了程序员在理解上的包袱,基于事宜编程的项目后期会很难扩展以及掩护。
那么有没有更好的方法呢?
要找到更好的方法,我们须要办理问题的实质,那么这个实质问题是什么呢?
11、更好的方法
为什么我们要利用异步这种难以理解的办法编程呢?
是由于:壅塞式编程虽然随意马虎理解但会导致线程被壅塞而停息运行。
那么聪明的你一定会问了:有没有一种方法既能结合同步IO的大略理解又不会因同步调用导致线程被壅塞呢?
答案是肯定的:这便是用户态线程(user level thread),也便是大名鼎鼎的协程(关于协程请详读本系列的上篇《从根上理解高性能、高并发(五):深入操作系统,理解高并发中的协程》,本文就不再赘述了)。
虽然基于事宜编程有这样那样的缺陷,但是在当今的高性能高并发做事器上基于事宜编程办法依然非常盛行,但已经不是纯粹的基于单一线程的事宜驱动了,而是 event loop + multi thread + user level thread。
关于这一组合,同样值得拿出一篇文章来讲解,我们将在后续文章中详细谈论。
12、本文小结
高并发技能从最开始的多进程一起演进到当前的事宜驱动,打算机技能就像生物一样也在不断演化进化,但不管若何,理解历史才能更深刻的理解当下。希望这篇文章能对大家理解高并发做事器有所帮助。
附录:更多高性能、高并发文章精选
《高性能网络编程(一):单台做事器并发TCP连接数到底可以有多少》
《高性能网络编程(二):上一个10年,著名的C10K并发连接问题》
《高性能网络编程(三):下一个10年,是时候考虑C10M并发问题了》
《高性能网络编程(四):从C10K到C10M高性能网络运用的理论探索》
《高性能网络编程(五):一文读懂高性能网络编程中的I/O模型》
《高性能网络编程(六):一文读懂高性能网络编程中的线程模型》
《高性能网络编程(七):到底什么是高并发?一文即懂!
》
《以网游做事真个网络接入层设计为例,理解实时通信的技能寻衅》
《知乎技能分享:知乎千万级并发的高性能长连接网关技能实践》
《淘宝技能分享:手淘亿级移动端接入层网关的技能演进之路》
《一套海量在线用户的移动端IM架构设计实践分享(含详细图文)》
《一套原创分布式即时通讯(IM)系统理论架构方案》
《微信后台基于韶光序的海量数据冷热分级架构设计实践》
《微信技能总监谈架构:微信之道——大道至简(演讲全文)》
《如何解读《微信技能总监谈架构:微信之道——大道至简》》
《快速裂变:见证微信强大后台架构从0到1的演进进程(一)》
《17年的实践:腾讯海量产品的技能方法论》
《腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面》
《以微博类运用处景为例,总结海量社交系统的架构设计步骤》
《新手入门:零根本理解大型分布式架构的演进历史、技能事理、最佳实践》
《重新手到架构师,一篇就够:从100到1000万高并发的架构演进之路》
本文已同步发布于“即时通讯技能圈”公众年夜众号。
同步发布链接是:http://www.52im.net/thread-3315-1-1.html