NGINX引入线程池 性能提高9倍

1. 引言

\\

正如咱们所知,NGINX采用了异步、事件驱动的方法来处理链接。这种处理方式无需(像使用传统架构的服务器同样)为每一个请求建立额外的专用进程或者线程,而是在一个工做进程中处理多个链接和请求。为此,NGINX工做在非阻塞的socket模式下,并使用了epollkqueue这样有效的方法。nginx

\\

由于满负载进程的数量不多(一般每核CPU只有一个)并且恒定,因此任务切换只消耗不多的内存,并且不会浪费CPU周期。经过NGINX自己的实例,这种方法的优势已经为众人所知。NGINX能够很是好地处理百万级规模的并发请求。数据库

\\

5acc17f466340dc9ef38c6fea5d456fd.jpg

\\
\

每一个进程都消耗额外的内存,并且每次进程间的切换都会消耗CPU周期并丢弃CPU高速缓存中的数据。后端

\
\\

可是,异步、事件驱动方法仍然存在问题。或者,我喜欢将这一问题称为“敌兵”,这个敌兵的名字叫阻塞(blocking)。不幸的是,不少第三方模块使用了阻塞调用,然而用户(有时甚至是模块的开发者)并不知道阻塞的缺点。阻塞操做能够毁掉NGINX的性能,咱们必须不惜一切代价避免使用阻塞。缓存

\\

即便在当前官方的NGINX代码中,依然没法在所有场景中避免使用阻塞,NGINX1.7.11中实现的线程池机制解决了这个问题。咱们将在后面讲述这个线程池是什么以及该如何使用。如今,让咱们先和咱们的“敌兵”进行一次面对面的碰撞。服务器

\\

2. 问题

\\

首先,为了更好地理解这一问题,咱们用几句话说明下NGINX是如何工做的。网络

\\

一般状况下,NGINX是一个事件处理器,即一个接收来自内核的全部链接事件的信息,而后向操做系统发出作什么指令的控制器。实际上,NGINX干了编排操做系统的所有脏活累活,而操做系统作的是读取和发送字节这样的平常工做。因此,对于NGINX来讲,快速和及时的响应是很是重要的。架构

\\

619f542b2ccd0007f02b360f197e6810.png

\\
\

工做进程监听并处理来自内核的事件并发

\
\\

事件能够是超时、socket读写就绪的通知,或者发生错误的通知。NGINX接收大量的事件,而后一个接一个地处理它们,并执行必要的操做。所以,全部的处理过程是经过一个线程中的队列,在一个简单循环中完成的。NGINX从队列中取出一个事件并对其作出响应,好比读写socket。在多数状况下,这种方式是很是快的(也许只须要几个CPU周期,将一些数据复制到内存中),NGINX能够在一瞬间处理掉队列中的全部事件。app

\\

7a57db06101b7b10c7c5cbc3f3a5e885.jpg

\\
\

全部处理过程是在一个简单的循环中,由一个线程完成负载均衡

\
\\

可是,若是NGINX要处理的操做是一些又长又重的操做,又会发生什么呢?整个事件处理循环将会卡住,等待这个操做执行完毕。

\\

所以,所谓“阻塞操做”是指任何致使事件处理循环显著中止一段时间的操做。操做能够因为各类缘由成为阻塞操做。例如,NGINX可能因长时间、CPU密集型处理,或者可能等待访问某个资源(好比硬盘,或者一个互斥体,亦或要从处于同步方式的数据库得到相应的库函数调用等)而繁忙。关键是在处理这样的操做期间,工做进程没法作其余事情或者处理其余事件,即便有更多的可用系统资源能够被队列中的一些事件所利用。

\\

咱们来打个比方,一个商店的营业员要接待他面前排起的一长队顾客。队伍中的第一位顾客想要的某件商品不在店里而在仓库中。这位营业员跑去仓库把东西拿来。如今整个队伍必须为这样的配货方式等待数个小时,队伍中的每一个人都很不爽。你能够想见人们的反应吧?队伍中每一个人的等待时间都要增长这些时间,除非他们要买的东西就在店里。

\\

2f627e54b94aa2bf67e42006d3417a2e.jpg

\\
\

队伍中的每一个人不得不等待第一我的的购买

\
\\

在NGINX中会发生几乎一样的状况,好比当读取一个文件的时候,若是该文件没有缓存在内存中,就要从磁盘上读取。从磁盘(特别是旋转式的磁盘)读取是很慢的,而当队列中等待的其余请求可能不须要访问磁盘时,它们也得被迫等待。致使的结果是,延迟增长而且系统资源没有获得充分利用。

\\

11d9f8758e8d021a7d35b755e9927dda.png

\\
\

一个阻塞操做足以显著地延缓全部接下来的操做

\
\\

一些操做系统为读写文件提供了异步接口,NGINX可使用这样的接口(见AIO指令)。FreeBSD就是个很好的例子。不幸的是,咱们不能在Linux上获得相同的福利。虽然Linux为读取文件提供了一种异步接口,可是存在明显的缺点。其中之一是要求文件访问和缓冲要对齐,但NGINX很好地处理了这个问题。可是,另外一个缺点更糟糕。异步接口要求文件描述符中要设置O_DIRECT标记,就是说任何对文件的访问都将绕过内存中的缓存,这增长了磁盘的负载。在不少场景中,这都绝对不是最佳选择。

\\

为了有针对性地解决这一问题,在NGINX 1.7.11中引入了线程池。默认状况下,NGINX+尚未包含线程池,可是若是你想试试的话,能够联系销售人员,NGINX+ R6是一个已经启用了线程池的构建版本。

\\

如今,让咱们走进线程池,看看它是什么以及如何工做的。

\\

3. 线程池

\\

让咱们回到那个可怜的,要从大老远的仓库去配货的售货员那儿。这回,他已经变聪明了(或者也许是在一群愤怒的顾客教训了一番以后,他才变得聪明的?),雇用了一个配货服务团队。如今,当任何人要买的东西在大老远的仓库时,他再也不亲自去仓库了,只须要将订单丢给配货服务,他们将处理订单,同时,咱们的售货员依然能够继续为其余顾客服务。所以,只有那些要买仓库里东西的顾客须要等待配货,其余顾客能够获得即时服务。

\\

fb12e21f45764f70af1142c679b13d06.jpg

\\
\

传递订单给配货服务不会阻塞队伍

\
\\

对NGINX而言,线程池执行的就是配货服务的功能。它由一个任务队列和一组处理这个队列的线程组成。
\当工做进程须要执行一个潜在的长操做时,工做进程再也不本身执行这个操做,而是将任务放到线程池队列中,任何空闲的线程均可以从队列中获取并执行这个任务。

\\

149b2c390a257bf2554804b211fb21ee.jpg

\\
\

工做进程将阻塞操做卸给线程池

\
\\

那么,这就像咱们有了另一个队列。是这样的,可是在这个场景中,队列受限于特殊的资源。磁盘的读取速度不能比磁盘产生数据的速度快。无论怎么说,至少如今磁盘再也不延误其余事件,只有访问文件的请求须要等待。

\\

“从磁盘读取”这个操做一般是阻塞操做最多见的示例,可是实际上,NGINX中实现的线程池可用于处理任何不适合在主循环中执行的任务。

\\

目前,卸载到线程池中执行的两个基本操做是大多数操做系统中的read()系统调用和Linux中的sendfile()。接下来,咱们将对线程池进行测试(test)和基准测试(benchmark),在将来的版本中,若是有明显的优点,咱们可能会卸载其余操做到线程池中。

\\

4. 基准测试

\\

如今让咱们从理论过分到实践。咱们将进行一次模拟基准测试(synthetic benchmark),模拟在阻塞操做和非阻塞操做的最差混合条件下,使用线程池的效果。

\\

另外,咱们须要一个内存确定放不下的数据集。在一台48GB内存的机器上,咱们已经产生了每文件大小为4MB的随机数据,总共256GB,而后配置NGINX,版本为1.9.0。

\\

配置很简单:

\\
\worker_processes 16;\\events {\    accept_mutex off;\}\\http {\    include mime.types;\    default_type application/octet-stream;\\    access_log off;\    sendfile on;\    sendfile_max_chunk 512k;\\    server {\        listen 8000;\\        location / {\            root /storage;\        }\    }\}
\\

如上所示,为了达到更好的性能,咱们调整了几个参数:禁用了loggingaccept_mutex,同时,启用了sendfile并设置了sendfile_max_chunk的大小。最后一个指令能够减小阻塞调用sendfile()所花费的最长时间,由于NGINX不会尝试一次将整个文件发送出去,而是每次发送大小为512KB的块数据。

\\

这台测试服务器有2个Intel Xeon E5645处理器(共计:12核、24超线程)和10-Gbps的网络接口。磁盘子系统是由4块西部数据WD1003FBYX 磁盘组成的RAID10阵列。全部这些硬件由Ubuntu服务器14.04.1 LTS供电。

\\

e24302f467da944b0543cb90872b1b5f.jpg

\\
\

为基准测试配置负载生成器和NGINX

\
\\

客户端有2台服务器,它们的规格相同。在其中一台上,在wrk中使用Lua脚本建立了负载程序。脚本使用200个并行链接向服务器请求文件,每一个请求均可能未命中缓存而从磁盘阻塞读取。咱们将这种负载称做随机负载

\\

在另外一台客户端机器上,咱们将运行wrk的另外一个副本,使用50个并行链接屡次请求同一个文件。由于这个文件将被频繁地访问,因此它会一直驻留在内存中。在正常状况下,NGINX可以很是快速地服务这些请求,可是若是工做进程被其余请求阻塞的话,性能将会降低。咱们将这种负载称做恒定负载

\\

性能将由服务器上ifstat监测的吞吐率(throughput)和从第二台客户端获取的wrk结果来度量。

\\

如今,没有使用线程池的第一次运行将不会带给咱们很是振奋的结果:

\\
\% ifstat -bi eth2\eth2\Kbps in  Kbps out\5531.24  1.03e+06\4855.23  812922.7\5994.66  1.07e+06\5476.27  981529.3\6353.62  1.12e+06\5166.17  892770.3\5522.81  978540.8\6208.10  985466.7\6370.79  1.12e+06\6123.33  1.07e+06
\\

如上所示,使用这种配置,服务器产生的总流量约为1Gbps。从下面所示的top输出,咱们能够看到,工做进程的大部分时间花在阻塞I/O上(它们处于top的D状态):

\\
\top - 10:40:47 up 11 days,  1:32,  1 user,  load average: 49.61, 45.77 62.89\Tasks: 375 total,  2 running, 373 sleeping,  0 stopped,  0 zombie\%Cpu(s):  0.0 us,  0.3 sy,  0.0 ni, 67.7 id, 31.9 wa,  0.0 hi,  0.0 si,  0.0 st\KiB Mem:  49453440 total, 49149308 used,   304132 free,    98780 buffers\KiB Swap: 10474236 total,    20124 used, 10454112 free, 46903412 cached Mem\\  PID USER     PR  NI    VIRT    RES     SHR S  %CPU %MEM    TIME+ COMMAND\ 4639 vbart    20   0   47180  28152     496 D   0.7  0.1  0:00.17 nginx\ 4632 vbart    20   0   47180  28196     536 D   0.3  0.1  0:00.11 nginx\ 4633 vbart    20   0   47180  28324     540 D   0.3  0.1  0:00.11 nginx\ 4635 vbart    20   0   47180  28136     480 D   0.3  0.1  0:00.12 nginx\ 4636 vbart    20   0   47180  28208     536 D   0.3  0.1  0:00.14 nginx\ 4637 vbart    20   0   47180  28208     536 D   0.3  0.1  0:00.10 nginx\ 4638 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.12 nginx\ 4640 vbart    20   0   47180  28324     540 D   0.3  0.1  0:00.13 nginx\ 4641 vbart    20   0   47180  28324     540 D   0.3  0.1  0:00.13 nginx\ 4642 vbart    20   0   47180  28208     536 D   0.3  0.1  0:00.11 nginx\ 4643 vbart    20   0   47180  28276     536 D   0.3  0.1  0:00.29 nginx\ 4644 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.11 nginx\ 4645 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.17 nginx\ 4646 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.12 nginx\ 4647 vbart    20   0   47180  28208     532 D   0.3  0.1  0:00.17 nginx\ 4631 vbart    20   0   47180    756     252 S   0.0  0.1  0:00.00 nginx\ 4634 vbart    20   0   47180  28208     536 D   0.0  0.1  0:00.11 nginx\ 4648 vbart    20   0   25232   1956    1160 R   0.0  0.0  0:00.08 top\25921 vbart    20   0  121956   2232    1056 S   0.0  0.0  0:01.97 sshd\25923 vbart    20   0   40304   4160    2208 S   0.0  0.0  0:00.53 zsh
\\

在这种状况下,吞吐率受限于磁盘子系统,而CPU在大部分时间里是空闲的。从wrk得到的结果也很是低:

\\
\Running 1m test @ http://192.0.2.1:8000/1/1/1\  12 threads and 50 connections\  Thread Stats   Avg    Stdev     Max  +/- Stdev\    Latency     7.42s  5.31s   24.41s   74.73%\    Req/Sec     0.15    0.36     1.00    84.62%\  488 requests in 1.01m, 2.01GB read\Requests/sec:      8.08\Transfer/sec:     34.07MB
\\

请记住,文件是从内存送达的!第一个客户端的200个链接建立的随机负载,使服务器端的所有的工做进程忙于从磁盘读取文件,所以产生了过大的延迟,而且没法在合理的时间内处理咱们的请求。

\\

如今,咱们的线程池要登场了。为此,咱们只需在location块中添加aio threads指令:

\\
\location / {\ root /storage;\ aio threads;\}
\\

接着,执行NGINX reload从新加载配置。

\\

而后,咱们重复上述的测试:

\\
\% ifstat -bi eth2\eth2\Kbps in  Kbps out\60915.19  9.51e+06\59978.89  9.51e+06\60122.38  9.51e+06\61179.06  9.51e+06\61798.40  9.51e+06\57072.97  9.50e+06\56072.61  9.51e+06\61279.63  9.51e+06\61243.54  9.51e+06\59632.50  9.50e+06
\\

如今,咱们的服务器产生的流量是9.5Gbps,相比之下,没有使用线程池时只有约1Gbps!

\\

理论上还能够产生更多的流量,可是这已经达到了机器的最大网络吞吐能力,因此在此次NGINX的测试中,NGINX受限于网络接口。工做进程的大部分时间只是休眠和等待新的事件(它们处于top的S状态):

\\
\top - 10:43:17 up 11 days,  1:35,  1 user,  load average: 172.71, 93.84, 77.90\Tasks: 376 total,  1 running, 375 sleeping,  0 stopped,  0 zombie\%Cpu(s):  0.2 us,  1.2 sy,  0.0 ni, 34.8 id, 61.5 wa,  0.0 hi,  2.3 si,  0.0 st\KiB Mem:  49453440 total, 49096836 used,   356604 free,    97236 buffers\KiB Swap: 10474236 total,    22860 used, 10451376 free, 46836580 cached Mem\\  PID USER     PR  NI    VIRT    RES     SHR S  %CPU %MEM    TIME+ COMMAND\ 4654 vbart    20   0  309708  28844     596 S   9.0  0.1  0:08.65 nginx\ 4660 vbart    20   0  309748  28920     596 S   6.6  0.1  0:14.82 nginx\ 4658 vbart    20   0  309452  28424     520 S   4.3  0.1  0:01.40 nginx\ 4663 vbart    20   0  309452  28476     572 S   4.3  0.1  0:01.32 nginx\ 4667 vbart    20   0  309584  28712     588 S   3.7  0.1  0:05.19 nginx\ 4656 vbart    20   0  309452  28476     572 S   3.3  0.1  0:01.84 nginx\ 4664 vbart    20   0  309452  28428     524 S   3.3  0.1  0:01.29 nginx\ 4652 vbart    20   0  309452  28476     572 S   3.0  0.1  0:01.46 nginx\ 4662 vbart    20   0  309552  28700     596 S   2.7  0.1  0:05.92 nginx\ 4661 vbart    20   0  309464  28636     596 S   2.3  0.1  0:01.59 nginx\ 4653 vbart    20   0  309452  28476     572 S   1.7  0.1  0:01.70 nginx\ 4666 vbart    20   0  309452  28428     524 S   1.3  0.1  0:01.63 nginx\ 4657 vbart    20   0  309584  28696     592 S   1.0  0.1  0:00.64 nginx\ 4655 vbart    20   0  30958   28476     572 S   0.7  0.1  0:02.81 nginx\ 4659 vbart    20   0  309452  28468     564 S   0.3  0.1  0:01.20 nginx\ 4665 vbart    20   0  309452  28476     572 S   0.3  0.1  0:00.71 nginx\ 5180 vbart    20   0   25232   1952    1156 R   0.0  0.0  0:00.45 top\ 4651 vbart    20   0   20032    752     252 S   0.0  0.0  0:00.00 nginx\25921 vbart    20   0  121956   2176    1000 S   0.0  0.0  0:01.98 sshd\25923 vbart    20   0   40304   3840    2208 S   0.0  0.0  0:00.54 zsh
\\

如上所示,基准测试中还有大量的CPU资源剩余。

\\

wrk的结果以下:

\\
\Running 1m test @ http://192.0.2.1:8000/1/1/1\  12 threads and 50 connections\  Thread Stats   Avg      Stdev     Max  +/- Stdev\    Latency   226.32ms  392.76ms   1.72s   93.48%\    Req/Sec    20.02     10.84    59.00    65.91%\  15045 requests in 1.00m, 58.86GB read\Requests/sec:    250.57\Transfer/sec:      0.98GB
\\

服务器处理4MB文件的平均时间从7.42秒降到226.32毫秒(减小了33倍),每秒请求处理数提高了31倍(250 vs 8)!

\\

对此,咱们的解释是请求再也不由于工做进程被阻塞在读文件,而滞留在事件队列中,等待处理,它们能够被空闲的进程处理掉。只要磁盘子系统能作到最好,就能服务好第一个客户端上的随机负载,NGINX可使用剩余的CPU资源和网络容量,从内存中读取,以服务于上述的第二个客户端的请求。

\\

5. 依然没有银弹

\\

在抛出咱们对阻塞操做的担心并给出一些使人振奋的结果后,可能大部分人已经打算在你的服务器上配置线程池了。先别着急。

\\

实际上,最幸运的状况是,读取和发送文件操做不去处理缓慢的硬盘驱动器。若是咱们有足够多的内存来存储数据集,那么操做系统将会足够聪明地在被称做“页面缓存”的地方,缓存频繁使用的文件。

\\

“页面缓存”的效果很好,可让NGINX在几乎全部常见的用例中展现优异的性能。从页面缓存中读取比较快,没有人会说这种操做是“阻塞”。而另外一方面,卸载任务到一个线程池是有必定开销的。

\\

所以,若是内存有合理的大小而且待处理的数据集不是很大的话,那么无需使用线程池,NGINX已经工做在最优化的方式下。

\\

卸载读操做到线程池是一种适用于很是特殊任务的技术。只有当常常请求的内容的大小,不适合操做系统的虚拟机缓存时,这种技术才是最有用的。至于可能适用的场景,好比,基于NGINX的高负载流媒体服务器。这正是咱们已经模拟的基准测试的场景。

\\

咱们若是能够改进卸载读操做到线程池,将会很是有意义。咱们只须要知道所需的文件数据是否在内存中,只有不在内存中时,读操做才应该卸载到一个单独的线程中。

\\

再回到售货员那个比喻的场景中,这回,售货员不知道要买的商品是否在店里,他必需要么老是将全部的订单提交给配货服务,要么老是亲自处理它们。

\\

人艰不拆,操做系统缺乏这样的功能。第一次尝试是在2010年,人们试图将这一功能添加到Linux做为fincore()系统调用,可是没有成功。后来还有一些尝试,是使用RWF_NONBLOCK标记做为preadv2()系统调用来实现这一功能(详情见LWN.net上的非阻塞缓冲文件读取操做异步缓冲读操做)。但全部这些补丁的命运目前还不明朗。悲催的是,这些补丁尚没有被内核接受的主要缘由,貌似是由于旷日持久的撕逼大战(bikeshedding)。

\\

另外一方面,FreeBSD的用户彻底没必要担忧。FreeBSD已经具有足够好的异步读取文件接口,咱们应该用这个接口而不是线程池。

\\

6. 配置线程池

\\

因此,若是你确信在你的场景中使用线程池能够带来好处,那么如今是时候深刻了解线程池的配置了。

\\

线程池的配置很是简单、灵活。首先,获取NGINX 1.7.11或更高版本的源代码,使用--with-threads配置参数编译。在最简单的场景中,配置看起来很朴实。咱们只须要在httpserver,或者location上下文中包含aio threads指令便可:

\\
\aio threads;
\\

这是线程池的最简配置。实际上的精简版本示例以下:

\\
\thread_pool default threads=32 max_queue=65536;\aio threads=default;
\\

这里定义了一个名为“default”,包含32个线程,任务队列最多支持65536个请求的线程池。若是任务队列过载,NGINX将输出以下错误日志并拒绝请求:

\\
\thread pool \"NAME\" queue overflow: N tasks waiting
\\

错误输出意味着线程处理做业的速度有可能低于任务入队的速度了。你能够尝试增长队列的最大值,可是若是这无济于事,那么这说明你的系统没有能力处理如此多的请求了。

\\

正如你已经注意到的,你可使用thread_pool指令,配置线程的数量、队列的最大值,以及线程池的名称。最后要说明的是,能够配置多个独立的线程池,将它们置于不一样的配置文件中,用作不一样的目的:

\\
\http {\ thread_pool one threads=128 max_queue=0;\ thread_pool two threads=32;\\ server {\ location /one {\ aio threads=one;\ }\\        location /two {\ aio threads=two;\ }\    }\…\}
\\

若是没有指定max_queue参数的值,默认使用的值是65536。如上所示,能够设置max_queue为0。在这种状况下,线程池将使用配置中所有数量的线程,尽量地同时处理多个任务;队列中不会有等待的任务。

\\

如今,假设咱们有一台服务器,挂了3块硬盘,咱们但愿把该服务器用做“缓存代理”,缓存后端服务器的所有响应信息。预期的缓存数据量远大于可用的内存。它其实是咱们我的CDN的一个缓存节点。毫无疑问,在这种状况下,最重要的事情是发挥硬盘的最大性能。

\\

咱们的选择之一是配置一个RAID阵列。这种方法毁誉参半,如今,有了NGINX,咱们能够有其余的选择:

\\
\\\proxy_cache_path /mnt/disk1 levels=1:2 keys_zone=cache_1:256m max_size=1024G\                 use_temp_path=off;\proxy_cache_path /mnt/disk2 levels=1:2 keys_zone=cache_2:256m max_size=1024G\                 use_temp_path=off;\proxy_cache_path /mnt/disk3 levels=1:2 keys_zone=cache_3:256m max_size=1024G\                 use_temp_path=off;\\thread_pool pool_1 threads=16;\thread_pool pool_2 threads=16;\thread_pool pool_3 threads=16;\\split_clients $request_uri $disk {\    33.3%     1;\    33.3%     2;\    *         3;\}\\location / {\    proxy_pass http:\    proxy_cache_key $request_uri;\    proxy_cache cache_$disk;\    aio threads=pool_$disk;\    sendfile on;\}
\\

在这份配置中,使用了3个独立的缓存,每一个缓存专用一块硬盘,另外,3个独立的线程池也各自专用一块硬盘。

\\

缓存之间(其结果就是磁盘之间)的负载均衡使用split_clients模块,split_clients很是适用于这个任务。

\\

proxy_cache_path指令中设置use_temp_path=off,表示NGINX会将临时文件保存在缓存数据的同一目录中。这是为了不在更新缓存时,磁盘之间互相复制响应数据。

\\

这些调优将带给咱们磁盘子系统的最大性能,由于NGINX经过单独的线程池并行且独立地与每块磁盘交互。每块磁盘由16个独立线程和读取和发送文件专用任务队列提供服务。

\\

我敢打赌,你的客户喜欢这种量身定制的方法。请确保你的磁盘也持有一样的观点。

\\

这个示例很好地证实了NGINX能够为硬件专门调优的灵活性。这就像你给NGINX下了一道命令,让机器和数据用最佳姿式来搞基。并且,经过NGINX在用户空间中细粒度的调优,咱们能够确保软件、操做系统和硬件工做在最优模式下,尽量有效地利用系统资源。

\\

7. 总结

\\

综上所述,线程池是一个伟大的功能,将NGINX推向了新的性能水平,除掉了一个众所周知的长期危害——阻塞——尤为是当咱们真正面对大量内容的时候。

\\

甚至,还有更多的惊喜。正如前面提到的,这个全新的接口,有可能没有任何性能损失地卸载任何长期阻塞操做。NGINX在拥有大量的新模块和新功能方面,开辟了一方新天地。许多流行的库仍然没有提供异步非阻塞接口,此前,这使得它们没法与NGINX兼容。咱们能够花大量的时间和资源,去开发咱们本身的无阻塞原型库,但这么作始终都是值得的吗?如今,有了线程池,咱们能够相对容易地使用这些库,而不会影响这些模块的性能。

\\

查看英文原文:Thread Pools in NGINX Boost Performance 9x!