加入收藏 | 设为首页 | 会员中心 | 我要投稿 温州站长网 (https://www.0577zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 安全 > 正文

京东资深架构师:高性能高并发服务的瓶颈及突破思路

发布时间:2021-01-08 06:17:53 所属栏目:安全 来源:网络整理
导读:副标题#e# 《京东资深架构师:高性能高并发服务的瓶颈及突破思路》要点: 本文介绍了京东资深架构师:高性能高并发服务的瓶颈及突破思路,希望对您有用。如果有疑问,可以联系我们。 本文根据DBAplus社群第74期线上分享整理而成 ? ? ? ? ? ? ? ? ? ? ? ? ?

基于事件驱动的模式(也称I/O多路复用)在充分利用cpu有效计算能力这件事件上是非常出色的.比较典型的有select/poll/epoll/kevent(这些机制本身之间的优劣今天先不展开说明,后续以epoll为例说明),这种模式的特点是将要监听的socket fd注册在epoll上,等这个描述符可读事件或者可写事件就绪了,那么就会触发相应的读操作或者写操作,可以简单地理解为需要cpu干活的时候就会告知cpu需要做什么事情,实际使用时示例如图7所示.

这个事情拿一个经典的例子来说明.就是在餐厅就餐,餐厅里有很多顾客(访问),每连接每线程的方式相当于每个客户一个服务员(线程相当于一个服务员),服务的过程中一个服务员一直为一个客户服务,那就会出现这个服务员除了真正提供服务以外有很大一段时间可能是空闲的,且随着客户数越多服务员数量也会越多,可餐厅的容量是有限的,因为要同时容纳相同数量的服务员和顾客,所以餐厅服务顾客的数量将变成理论容量的50%.那这件事件对于老板(老板相当于开发人员,希望可以充分利用cpu的计算能力,也就是在cpu计算能力<成本>一定的情况下希望尽量的多做一些事情)来说代价就会很大.

线程池的方式是雇佣固定数量的服务员,服务的时候一个服务员服务好几个客户,可以理解为一个服务员在客户A面前站1分钟,看看A客户是否需要服务,如果不需要就到B客户那边站1分钟,看看B客户是否需要服务,以此类推.这种情况会比之前每个客户一个服务员的情况节省一些成本,但是还是会出现一些成本上的浪费.

还有一种模式也就是epoll的方式,相当于服务员就在总台等着,客户有需要的时候就会在桌上的呼叫器上按一下按钮表示自己需要服务,服务员每次看一下总台显示的信息,比如一共有100个客户,一次可能有10个客户呼叫,这个服务员就会过去为这10个客户服务(假设服务每个客户的时候不会出现停顿且可以在较短的时间内处理完),等这个服务员为这10个客户服务员完以后再重新回到总台查看哪些客户需要服务,依此类推.在这种情况下,可能只需要一个服务员,而餐厅剩余的空间可以全部给客户使用.

nginx服务器性能非常好,也能支撑非常多的连接,其网络模型使用的就是epoll的方式,且在实现的时候采用了多个子进程的方式,相当于同时有多个epoll在工作,充分利用了cpu多核的特性,所以并发及性能都会比单个epoll的方式会有更大的提升.

另外Redis缓存服务器大家应该也非常熟悉,用的也是epoll的方式,性能也是非常好,通过这些现成的经典开源项目,大家就可以直观地理解基于事件驱动这一方式在实际生产环境中的性能是非常高的,性能提升以后并发效果一般都会随之提升.

但是这种方式在实现的时候是非常考验编程功底以及逻辑严谨性,换句话编程友好性是非常差的.因为一个完整的上下文逻辑会被切成很多片段,比如“客户端发送一个命令-服务器端接收命令进行操作-然后返回结果”这个过程,至少会包括一个可读事件、一个可写事件,可读事件简单地理解就是指这条命令已经发送到服务器端的tcp缓存区了,服务器去读取命令(假设一次读取完,如果一次读取的命令不完整,可能会触发多次读事件),服务器再根据命令进行操作获取到结果,同时注册一个可写事件到epoll上,等待下一次可写事件触发以后再将结果发送出去,想象一下当有很多客户端同时来访问时,服务器就会出现一种情况——一会儿在处理某个客户端的读事件,一会儿在处理另外的客户端的写事件,总之都是在做一个完整访问的上下文中的一个片段,其中任何一个片段有等待或者卡顿都将引起整个程序的阻塞.

当然这个问题在多线程编程时也是同样是存在的,只不过有时候大家习惯将线程设置成多个,有些线程阻塞了,但可能其他线程并没有在同一时刻阻塞,所以问题不是特别严重,更严谨的做法是在多线程编程时,将线程池的数量调整到最小进行测试,如果确实有卡顿,可以确保程序在最快的时间内出现卡顿,从而快速确认逻辑上是否有不足或者缺陷,确认这种卡顿本身是否是正常现象.

3、语言层提供协程支持

多线程编程的方式明显是支持了高并发,但因为整个程序线程间上下文调度可能造成cpu的利用率不是那么高,而基于事件驱动的编程方式效果非常好的,但对编程功底要求非常高,而且在实现的时候需要花费的时间也是最多的.所以一种比较折中的方式是考虑采用提供协程支持的语言比如golang这种的.

简单说就是语言层面抽象出了一种更轻量级的线程,一般称为协程,在golang里又叫goroutine,这些底层最终也是需要用操作系统的线程去跑,在golang的runtime实现时底层用到的操作系统的线程数量相对会少一点,而上层程序里可以跑很多的goroutine,这些goroutine会在语言层面进行调度,看该由哪个线程来最终执行这个goroutine.

因为goroutine之间的切换代价是远小于操作系统线程之间的切换代价,而底层用到的操作系统数量又较少,线程间的上下文切换代价本来也会大大降低.

这类语言能比其他语言的多线程方式提供更好的并发,因为它将操作系统的线程间切换的代价在语言层面尽可能挤压到最小,同时编程复杂度大大降低,在这类语言中上下文逻辑可以保持连贯.因为降低了线程间上下文切换的代价,而goroutine之间的切换成本相对来说是远远小于线程间切换成本,所以cpu的有效计算能力相对来说也不会太低,相当于可以比较容易的获得了一个高并发且性能还可以的服务.

(编辑:温州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读