编程基础知识
高并发
现在很多公司的招聘要求上都写着要求会高并发编程,其实不难,首先要理解什么是并发,然后如何实现并发,能实现并发后,再配合高性能的分布式运算(其实多线程/进程即可)就可以称为高并发了。
并发以及并发的实现
并发的概念很容易理解,就是能同时或很短的时间内开始很多项工作,如果了解多路复用IO(或称为事件驱动IO)的技术,那么并发就很好理解了。
在多路复用IO中,针对磁盘IO、网络IO这种耗时的操作,早期用select,中期poll,现在的epoll/kqueue都是用来处理耗时操作的工具,而我们开发中只需要把耗时操作交给这些工具处理即可,这样就可以达到并发的要求,当然IO处理完后消息返回会有相应的回调函数来处理,但这不影响我们同时开始很多项任务。
而Python中的协程其实也是做类似的工作,用多层协程嵌套,将耗时操作放在内层生成器中,最外层生成器则可以直接返回去做别的任务,当内层生成器通过send返回结果时,最外层生成器就能接到结果并继续处理这个任务。
所以并发用到的技术就是IO多路复用,相当于把工作外包给其它公司去做,我只要发任务就行了,任务做完了我会得到通知。
分布式实现
分布式就是利用集群的资源来处理任务,这个过程涉及到跨进程、跨线程、跨主机、跨网络的资源共享,有很多专业的消息队列为我们做到了,如RabbitMQ、Kafka等。
其次是涉及到集群管理、资源分配的功能,做分布式也有很多工具,底层原理其实都是差不多的,都是通过socket来通信,至于通信协议,可以使用HTTP/RPC等等,可以说学会了什么协议,就可以用什么协议,无非是效率问题,随着学习的深入,有朝一日你也能自创一个协议;然后集群管理可以利用一些高效算法来分配资源,提供自定义的一些选项,如日志、自动挂起、网络分区的处理等等。
最终实现
做高并发最重要的就是IO多路复用技术,很多情况下用不到分布式,多线程/多进程就能满足要求,像Tornado的异步Handler就是基于epoll提供的并发处理功能,虽然Tornado是单进程的,但是我们可以通过tornado.httpserver来生成服务器实例,以此提供多进程参数来处理,然后外层再用多个Nginx服务器做upstream负载均衡,这就很完美了。
并行
并行的概念其实也不难理解,就是多个进程或线程可以“同时”执行相同或不同的任务,对于相同的任务要注意变量的隔离,当然并行强调的是“同时”进行的过程,而上述的并发只是强调能够“同时”开始。
要注意这里的“同时”,我们的程序是按行来执行的,那么必然会存在哪个程序先执行,哪个后执行,所以这里的“同时”其实是指不会因为某个函数阻塞住后续的函数执行。
从原则上看,所有函数都是同步顺序执行的,但是由于CPU的运算速度实在太快,只要它同步执行时不会阻塞,那么我们就认为这些函数是“同时”执行的。
虽然原理上并行很好理解,但对于Python来说,由于CPython解释器的GIL全局线程锁的存在,Python的多线程其实是假的,同时在运行的永远只有一个线程,所以也就有了Python处理磁盘、网络IO的多线程比较好(不要忘了多路复用IO的并发优点),但对于计算IO几乎没用的说法。
同步
上面提到了同步,这个很好理解,同步就是按顺序执行,如果前一个函数阻塞住了(例如读取数据但数据还没到,或正在计算结果但还未计算完成),后面的函数就不会执行,而是等到前面的函数执行完才会按顺序执行下来。
异步
异步是相对同步的,这是个很吊的技术,但是没有很好的实现,因为实在是难以处理过程变量。
异步就是某个函数阻塞了,没事,进程或线程就换个函数去执行,永远没有阻塞。但是带来一个问题,因为函数执行的结果对于进程/线程来说,不知道什么时候能返回,当运算量升上来时,可能会返回很多结果,我们很难理清到底哪个结果是哪个函数持有的,对于程序员的编程能力是极大的损耗,头发掉的更快了,同时性能不一定很高。
异步IO
异步IO是相对同步IO来讲的,多路复用IO其实也是同步IO,但很多人称之为异步处理。
异步IO与同步IO的区别就在于从用户到内核和从内核到用户这两个过程是否是阻塞的,异步IO的两个过程都是非阻塞的,而多路复用IO只有第一个过程是非阻塞的。
记住一点,多路复用IO只是将第一步的阻塞交给别的函数去处理了,所以它不阻塞,但第二步返回的结果仍然要它去处理,所以第二步仍然是阻塞的,阻塞在等待数据从内核拷贝到用户空间这个过程(当然因为是内存级拷贝,其实速度很快,所以大家就称之为异步处理了)。