在即时通讯网社区里,多是做IM、音讯推送、客服零碎、音视频聊天这类实时通信方面的开发者,在波及到即时通讯技术时聊的最多的话题就是高并发、高吞吐、海量用户。
代码还没开始写,就思考万一哪天这IM用户量破百万、千万该怎么办的问题,是少数程序员的根本涵养。
在面视即时通讯相干工作的时候,高并发也是必谈问题,那到底什么是高并发?嗯,真要说出个所以然来,还真有点懵。
什么是高并发?
高并发是互联网零碎架构的性能指标之一,它通常是指单位工夫内零碎可能同时解决的申请数。
简略点说,就是QPS(Queries per second)。
那么咱们在议论高并发的时候,到底在谈什么货色呢?归根结底,到底什么是高并发?
高并发到底是什么?
这里先给出论断:
1)高并发的根本体现为单位工夫内零碎可能同时解决的申请数;
2)高并发的外围是对CPU资源的无效压迫。
举个例子:如果咱们开发了一个叫做MD5穷举的利用,每个申请都会携带一个md5加密字符串,最终零碎穷举出所有的后果,并返回原始字符串。这个时候咱们的利用场景或者说利用业务是属于CPU密集型而不是IO密集型。
这个时候CPU始终在做无效计算,甚至能够把CPU利用率跑满,这时咱们议论高并发并没有任何意义。(当然,咱们能够通过加机器也就是加CPU来进步并发能力,这个是一个失常猿都晓得废话计划,议论加机器没有什么意义,没有任何高并发是加机器解决不了,如果有,那阐明你加的机器还不够多!)
对于大多数互联网利用来说,CPU不是也不应该是零碎的瓶颈,零碎的大部分工夫的情况都是CPU在等I/O(硬盘/内存/网络) 的读/写操作实现。
这个时候就可能有人会说,我看系统监控的时候,内存和网络都很失常,然而CPU利用率却跑满了这是为什么?
这是一个好问题,后文我会给出理论的例子,再次强调上文说的“无效压迫”这4个字,这4个字会围绕本文的全部内容!
控制变量法
万事万物都是互相联系的,当咱们在议论高并发的时候,零碎的每个环节应该都是须要与之相匹配的。
1)咱们会通过DNS服务器的解析,申请达到负载平衡集群;
2)负载平衡服务器会依据配置的规定,想申请摊派到服务层。服务层也是咱们的业务核心层,这里可能也会有一些RPC、MQ的一些调用等等;
3)再通过缓存层;
4)最初长久化数据;
5)返回数据给客户端。
要达到高并发,咱们须要负载平衡、服务层、缓存层、长久层都是高可用、高性能的。
甚至在第5步,咱们也能够通过压缩动态文件、HTTP2推送动态文件、CDN来做优化,这里的每一层咱们都能够写几本书来谈优化。
本文次要探讨服务层这一块,即图红线圈进去的那局部。不再思考讲述数据库、缓存相干的影响。
高中的常识通知咱们,这个叫控制变量法。
那咱们再谈谈上下文切换:
在议论上下文切换之前,咱们再明确两个名词的概念:
1)并行:两个事件同一时刻实现;
2)并发:两个事件在同一时间段内交替产生,从宏观上看,两个事件都产生了。
线程是操作系统调度的最小单位,过程是资源分配的最小单位。因为CPU是串行的,因而对于单核CPU来说,同一时刻肯定是只有一个线程在占用CPU资源的。因而,Linux作为一个多任务(过程)零碎,会频繁的产生过程/线程切换。
在每个工作运行前,CPU都须要晓得从哪里加载,从哪里运行,这些信息保留在CPU寄存器和操作系统的程序计数器外面,这两样货色就叫做CPU上下文。
过程是由内核来治理和调度的,过程的切换只能产生在内核态,因而虚拟内存、栈、全局变量等用户空间的资源,以及内核堆栈、寄存器等内核空间的状态,就叫做过程上下文。
后面说过,线程是操作系统调度的最小单位。同时线程会共享父过程的虚拟内存和全局变量等资源,因而父过程的资源加上线上本人的公有数据就叫做线程的上下文。
对于线程的上下文切换来说,如果是同一过程的线程,因为有资源共享,所以会比多过程间的切换耗费更少的资源。
当初就更容易解释了,过程和线程的切换,会产生CPU上下文切换和过程/线程上下文的切换。而这些上下文切换,都是会耗费额定的CPU的资源的。
进一步谈谈协程的上下文切换:
那么协程就不须要上下文切换了吗?须要,然而不会产生 CPU上下文切换和过程/线程上下文的切换,因为这些切换都是在同一个线程中,即用户态中的切换,你甚至能够简略的了解为,协程上下文之间的切换,就是挪动了一下你程序外面的指针,CPU资源仍旧属于以后线程。
须要深刻理解的,能够再深刻看看Go的GMP模型。
最终的成果就是协程进一步压迫了CPU的无效利用率。
回到开始的那个问题
这个时候就可能有人会说,我看系统监控的时候,内存和网络都很失常,然而CPU利用率却跑满了这是为什么?即时通讯软件开发征询蔚可云开发。
留神本篇文章在谈到CPU利用率的时候,肯定会加上无效两字作为定语,CPU利用率跑满,很多时候其实是做了很多低效的计算。
以"世界上最好的语言"为例。
典型PHP-FPM的CGI模式,每一个HTTP申请:
1)都会读取框架的数百个php文件;2)都会从新建设/开释一遍MYSQL/REIDS/MQ连贯;3)都会从新动静解释编译执行PHP文件;4)都会在不同的php-fpm过程间接不停的切换切换再切换。
php的这种CGI运行模式,基本上就决定了它在高并发上的灾难性体现。
找到问题,往往比解决问题更难。当咱们了解了高并发之后,咱们会发现高并发和高性能并不是编程语言限度了你,限度你的只是你的思维。
找到问题,解决问题!当咱们能无效压迫CPU性能之后,能达到什么样的成果?
上面咱们看看 php+Swoole的HTTP服务与Java高性能的异步框架Netty的HTTP服务之间的性能差别比照。