性能追击:万字长文30+图揭秘8大主流服务器程序线程模型

本文深入探讨了8种主流服务器程序的线程模型,包括Node.js的Event Loop、Apache的MPM Prefork和Worker、Nginx的单线程异步处理、Netty的主从Reactor模式、Redis的单线程模型、Tomcat的BIO/NIO/NIO2/APR线程模型。文章通过详细的图文分析,揭示了各服务器如何处理并发请求和优化性能,并探讨了各自的优缺点。
摘要由CSDN通过智能技术生成

本文我们来探索一下主流的各种应用服务器的网络处理模型,看看大家都是怎么设计网络程序的。在本文中,我会从Node.js、Apache Server、Nginx、Netty、Redis、Tomcat、MySQL、Zuul等常用的服务器程序,给大家逐一分析,分析各种服务器程序的性能,心中有数,才能手中有术,从此性能是熟客。

虽然涉及到很多底层知识,各种框架的原理,但是我都会尽量配上直白易懂的图文,方便大家理解。

更多优质文章,让我们相约在IT宅(itzhai.com)的Java架构杂谈公众号。

首先,我们从什么都能写的Javascript说起,来看看Node.js服务器的并发内幕。

1、Node.js

上篇文章中,我们介绍了JavaScript在浏览器端的运行模式,接下来,我们继续讲讲Node.js的运行模式,揭开它高性能背后的实现机制。

1.1、Node.js运行模式

以下是一个流传度很广的Node.js系统视图,来源于 Totally Radical Richard[1]

Node.js是单线程的Event Loop[2]:

  • V8引擎解析JS脚本,调用Node API;
  • libuv库执行Node API,会先将API请求封装成事件,放入事件队列,Event Loop线程处于空闲状态时,就开始遍历处理事件队列中的事件,对事件进行处理:如果不是阻塞任务,直接处理得到结果,通过回调函数返回给V8;如果是阻塞任务,则从Worker线程池中取出一个线程,交给线程处理,最终线程把处理结果设置到事件的结果属性中,把事件放回事件队列,等待Event Loop线程执行回调返回给V8引擎。

其中请求的任务会被封装成如下的结构:[3]

varevent= createEvent({ params:request.params, // 传递请求参数 result:null, // 存放请求结果 callback:function(){} // 指定回调函数 });

更多精彩讨论,可以阅读这个帖子:How the single threaded non blocking IO model works in Node.js[4],以下是来源于这个帖子的图片:

当然了,在客户端请求到Node.js服务器的时候,肯定会有一个创建已连接套接字的过程,然后把这个已连接套接字描述符与具体的执行代码关联起来,这样再异步处理完成之后,才知道要响应给哪个客户端。

1.2、Node.js异步案例

以上的运行模式说明还是需要结合例子来说明比较好理解。

如果没有通过回调函数进行异步处理,我们可能会写出如下代码:

var result = db.query("select * from t_user"); // do something with result here... console.log("do something else...");

这个代码在执行查询result的时候,查询速度可能很慢,等待查询出结果后,才可以执行后面的console.log操作,因为这是在一个线程上执行的。

但是Node.js不是这么玩的,Node.js的运行模式下,只有一个Event Loop线程,如果这个线程被阻塞,这将导致无法接收新的请求。为了避免这种情况,我们按照Node.js的回调方式重写代码:

db.query("select * from t_user", function(rows) { var result = rows; // do something with result here... }); console.log("do something else...");

现在,Node.js可以异步处理查询请求了,并且把查询请求委托给Worker Thread,等待Worker Thread得到查询结果之后,再把结果连同回调匿名函数封装成事件发布到事件队列,等待Event Loop线程执行该回调函数。这样console.log代码就可以立刻得到执行,而不会因为查询请求导致被阻塞住了。

1.3、Node.js并发模型优缺点

从以上分析可知,Node.js通过事件驱动,把阻塞的IO任务丢到线程池中进行异步处理,也就是说,Node.js适合I/O密集型任务

但是,如果碰到CPU密集型任务的时候,Node.js中的EventLoop线程就会自己处理任务,这样会导致在事件队列中的CPU密集型任务没有处理完,那么后面的任务就不会被执行到了,从而导致后续的请求响应变慢。

如下图,本来socket2和socket3很快就可以处理完的,但是由于socket1的任务一直占用着CPU时间,导致socket2和socket3都不能及时得到处理,从表现上看,就是响应变慢了。

如果CPU是单核的还好,充分的利用了CPU内核,但是如果CPU是多核的,这种情况就会导致其他内存处于闲置状态,造成资源浪费。

所以,Node.js不适合CPU密集型任务

Node.js适合请求和响应内容小,无需大量计算逻辑的场景,这能够充分发挥Node.js运行模式的优势。类似的场景有聊天程序。

不过,从Node.js可以通过提供cluster、child_process API创建子进程的方式来充分利用多核的能力,但是多进程也就意味着牺牲了共享内存,并且通信必须使用json进行传输。[5]

Node.js V10.5.0开始,提供了worker_threads,让Node.js拥有了多工作线程:Event Loop线程 + 自己启动的线程,多工作线程对于CPU密集型的JavaScript操作非常有用。

需要注意的是,worker_threads可以作为Node.js中CPU密集型问题的解决策略之一,对于IO,Node.js原生线程(Event Loop线程)已经做了很好的支持,无需自己启动一个线程去做此类工作(参考本节第一张图片的介绍)。

好了,前端知识不能聊深了,聊深就出破绽了,毕竟也是有前端大佬在关注IT宅的Java架构杂谈公众号,在偷偷学学Java技术的。

接下来,我们从一根羽毛的故事说起。

2、Apache

Apache于1995年首次发布,并迅速占领了市场,成为世界上最受欢迎的Web服务器。配合世界上最好的语言——PHP搭建网站,在那个年代可谓是打遍天下无敌手。

这里我们来探讨下Apache Web服务器使用的两个工作模型:

  • Apache MPM Prefork:用于实现多进程模型;
  • Apache MPM Worker:用于实现多线程模型;

Apache使用到了Multi Processing Module模块(MPM)来实现多进程或者多线程处理器。

2.1、Apache MPM Prefork

一句话总结:Prefork是一个非线程型的、预派生的MPM

Prefork预派生出多个进程,每个进程在某个确定的时间只单独处理一个连接,效率高,但内存使用比较大。

这种模型是每个请求一个进程的模型,由一个父进程创建了许多子进程,这些子进程等待请求的到达并且进行处理,每个请求均由单独的进程进行处理。

需要注意的是,每个进程都会使用RAM和CPU等系统资源,使用的RAM数量都是相等的。

如果同时有很多请求,那么apache会产生很多子进程,这将导致大量的资源利用率。

2.2、Apache MPM Worker

一句话总结:Worker是支持混合的多线程多进程的MPM。如下图:

子进程借助内部固定数量的线程来处理请求,该数量由配置文件中的参数“ThreadsPerChild指定。

该模型一般使用多个子进程,每个子进程有多个线程,每个线程在某个确定的时间只处理一个连接,消耗内存较少。这种Apache模型可以用较少的系统资源来满足大量请求,因为这种模型下,有限数量的进程将为许多请求提供服务。

PHP攻城狮提问题:为啥mod_php中不能使用MPM Worker?

由于一些mod_php模块问题,导致该模块不能和MPM Worker一起使用,PMP Worker一般都是跟apache的mod_fcgid 搭配,而PHP则是安装php-cgi来运行。

作为一个Java攻城狮,这里我就不展开继续讲了,毕竟世界上最好的语言相信大家会主动去了解的。

即使是一个请求用一个线程,Apache在高并发场景下,运行效率也是很差的。因为,如果一个请求需要数据库中的一些数据以及磁盘中的文件等涉及到IO操作的处理,则该线程将进入等待。因此,Apache中的某些线程(Worker模式)或者进程(Prefork模式)只是停下来下来等待某些任务完成,这些线程或者进程吃掉了系统资源。

而接下来我们介绍对并发场景处理更高效的主角:Nginx,从根本上说,Apache和Nginx差别很大。Nginx的诞生是为了解决Apache中的c10k问题。

想象以下,从猪圈里冲出一群猪,Apache Server能够抵挡得住吗,也许不行,但是,Nginx,一定可以。这就是Nginx的强大之处。

3、Nginx

Nginx是一种开源Web服务器,自从最初作为Web服务器获得成功以来,现在还用作反向代理,HTTP缓存和负载均衡器。

Nginx旨在提供低内存使用率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值