最近处处在争论这些话题,发现不少人对一些基础的常识并不了解,在此发表一文作一下解释。此文未必能解答全部问题,各位能有一个大体的了解就好。 node
你们都知道互联网的基础就是网络通讯,早期的互联网能够说是一个小群体的集合。互联网还不够普及,用户也很少。一台服务器同时在线100个用户估计 在当时已经算是大型应用了。因此并不存在什么C10K的难题。互联网的爆发期应该是在www网站,浏览器,雅虎出现后。最先的互联网称之为Web1.0, 互联网大部分的使用场景是下载一个Html页面,用户在浏览器中查看网页上的信息。这个时期也不存在C10K问题。 程序员
Web2.0时代到来后就不一样了,1方面是普及率大大提升了,用户群体几何倍增加。2是互联网再也不是单纯的浏览万维网网页,逐渐开始进行交互,并且 应用程序的逻辑也变的更复杂,从简单的表单提交,到即时通讯和在线实时互动。C10K的问题才体现出来了。每个用户都必须与服务器保持TCP链接才能进 行实时的数据交互。Facebook这样的网站同一时间的并发TCP链接可能会过亿。 编程
腾讯QQ也是有C10K问题的,只不过他们是用了UDP这种原始的包交换协议来实现的,绕开了这个难题。固然过程确定是痛苦的。若是当时有epoll技术,他们确定会用TCP。后来的手机QQ,微信都采用TCP协议。
这时候问题就来了,最初的服务器都是基于进程/线程模型的,新到来一个TCP链接,就须要分配1个进程(或者线程)。而进程又是操做系统最昂贵的资 源,一台机器没法建立不少进程。若是是C10K就要建立1万个进程,那么操做系统是没法承受的。若是是采用分布式系统,维持1亿用户在线须要10万台服务 器,成本巨大,也只有Facebook,Google,雅虎才有财力购买如此多的服务器。这就是C10K问题的本质。 浏览器
实际上当时也有异步模式,如:select/poll模型,这些技术都有必定的缺点,如selelct最大不能超过1024,poll没有限制,但每次收到数据须要遍历每个链接查看哪一个链接有数据请求。
既然有了C10K问题,程序员们就开始行动去解决它。因而FreeBSD推出了kqueue,Linux推出了epoll,Windows推出了 IOCP。这些操做系统提供的功能就是为了解决C10K问题。由于Linux是互联网企业中使用率最高的操做系统,Epoll就成为C10K killer、高并发、高性能、异步非阻塞这些技术的代名词了。 服务器
epoll技术的编程模型就是异步非阻塞回调,也能够叫作Reactor,事件驱动,事件轮循(EventLoop)。Epoll就是为了解决 C10K问题而生。使用Epoll技术,使得小公司也能够玩高并发。不须要购买不少服务器,有几台服务器就能够服务大量用户。 Nginx,libevent,node.js这些就是Epoll时代的产物。 微信
C10K问题解决后,程序员又提出了更高的挑战,也就是最近在火热争论的C100K,C1M等。Epoll既然能解决C10K,解决什么 C100K,C1M也是能够的。只不过这个已经没有意义了。一个公司有1亿用户难道他买不起1万台服务器嘛。WhatsApp有2亿用户,卖了150亿美 元。1万台服务器最多花费5000万美圆。 网络
看到阿里技术保障部的人也在谈C10K话题,我要补充一下,搞路由器、交换机、网关、防火墙之类基础网络设备的人,就不要参与C10K话题了。咱们说的是应用层程序。
当程序员还沉浸在解决C10K问题带来的成就感时,一个新的问题被抛出了。异步嵌套回调太TM难写了。尤为是Node.js层层回调,缩进了几十 层,要把程序员逼疯了。因而一个新的技术被提出来了,那就是协程(coroutine)。这个技术本质上也是异步非阻塞技术,它是将事件回调进行了包装, 让程序员看不到里面的事件循环。程序员就像写阻塞代码同样简单。好比调用 client->recv() 等待接收数据时,就像阻塞代码同样写。其实是底层库在执行recv时悄悄保存了一个状态,好比代码行数,局部变量的值。而后就跳回到EventLoop 中了。何时真的数据到来时,它再把刚才保存的代码行数,局部变量值取出来,又开始继续执行。 并发
这个就像时间禁止的游戏同样,国王对巫师说“我必须立刻获得宝物,否则就砍了你的脑壳”,巫师念了一句时间中止的咒语,直到过了1年后勇士们才把宝 物送来。这时候巫师解开咒语,把宝物交给国王。这里国王就能够理解成协程,他根本没感受到时间中止,在他中止到醒来期间发生了什么他不知道,也不关心。 异步
这就是协程的本质。协程是异步非阻塞的另一种展示形式。Golang,Erlang,Lua协程都是这个模型。 分布式
再回到同步阻塞这个话题,不知道你们看完协程是否感受获得,实际上协程和同步阻塞是同样的。答案是的。因此协程也叫作用户态进/用户态线程。区别就在于进程/线程是操做系统充当了EventLoop调度,而协程是本身用Epoll进行调度。
协程的优势是它比系统线程开销小,缺点是若是其中一个协程中有密集计算,其余的协程就不运行了。操做系统进程的缺点是开销大,优势是不管代码怎么写,全部进程均可以并发运行。
Erlang解决了协程密集计算的问题,它基于自行开发VM,并不执行机器码。即便存在密集计算的场景,VM发现某个协程执行时间过长,也能够进行停止切换。Golang因为是直接执行机器码的,因此没法解决此问题。因此Golang要求用户必须在密集计算的代码中,自行Yield。
实际上同步阻塞程序的性能并不差,它的效率很高,不会浪费资源。当进程发生阻塞后,操做系统会将它挂起,不会分配CPU。直到数据到达才会分配 CPU。多进程只是开多了以后反作用太大,由于进程多了互相切换有开销。因此若是一个服务器程序只有1000左右的并发链接,同步阻塞模式是最好的。
协程虽然是用户态调度,实际上仍是须要调度的,既然调度就会存在上下文切换。因此协程虽然比操做系统进程性能要好,但总仍是有额外消耗的。而异步回调是没有切换开销的,它等同于顺序执行代码。因此异步回调程序的性能是要优于协程模型的。
这里是指Nginx这种多进程异步非阻塞程序。Node.js/Redis此类程序若是不开多个进程,因为没法利用多核计算优点,因此性能并很差。在