아시다시피 NGINX는 연결을 처리하기 위해 비동기식 이벤트 기반 접근 방식을 사용합니다. 이 접근 방식을 사용하면 기존 서버와 마찬가지로 각 요청에 대해 추가 전용 프로세스나 스레드를 만들 필요가 없으며 대신 단일 작업자 프로세스에서 여러 연결과 요청이 처리됩니다. 이를 위해 NGINX는 비차단 소켓 모드에서 작동하며 epoll 및 kqueue와 같은 효과적인 방법을 사용합니다. 완전히 로드된 프로세스 수가 적고(일반적으로 CPU 코어당 하나만) 일정하기 때문에 작업 전환은 메모리를 거의 소비하지 않으며 CPU 주기를 낭비하지 않습니다. 이 접근 방식의 장점은 NGINX 자체를 사용하여 이미 잘 알려져 있습니다. NGINX는 수백만 개의 동시 요청을 매우 잘 처리할 수 있습니다. 각 프로세스는 추가 메모리를 소비하고 프로세스 간 전환은 CPU주기를 소비합니다. CPU 캐시의 데이터를 삭제합니다. 그러나 비동기식 이벤트 중심 접근 방식에는 여전히 문제가 있습니다. 혹은 제가 즐겨 부르는 문제는 적군, 적군의 이름이 블로킹입니다. 불행하게도 많은 타사 모듈이 차단 호출을 사용하지만 사용자(때로는 모듈 개발자조차)가 차단의 단점을 인식하지 못합니다. 차단 작업은 NGINX 성능을 저하시킬 수 있으므로 어떤 대가를 치르더라도 피해야 합니다. 현재 공식 NGINX 코드에서도 모든 시나리오에서 차단 사용을 방지하는 것은 여전히 불가능합니다. NGINX1.7.11에 구현된 스레드 풀 메커니즘은 이 문제를 해결합니다. 이 스레드 풀이 무엇인지, 어떻게 사용하는지 나중에 설명하겠습니다. 이제 우리의 "적군"과 정면충돌을 해보자. 2. 질문 먼저 이 문제를 더 잘 이해하기 위해 NGINX가 어떻게 작동하는지 몇 문장으로 설명하겠습니다. 일반적으로 NGINX는 이벤트 핸들러, 즉 커널에서 모든 연결 이벤트에 대한 정보를 수신한 다음 운영 체제에 수행할 작업에 대한 지침을 보내는 컨트롤러입니다. 실제로 NGINX는 바이트를 읽고 보내는 일상적인 작업을 수행하는 운영 체제를 조정하는 모든 더러운 작업을 수행합니다. 따라서 NGINX의 경우 빠르고 시기적절한 대응이 매우 중요합니다. 작업자 프로세스는 커널의 이벤트를 수신하고 처리합니다. 이벤트는 시간 초과될 수 있습니다. , 소켓이 읽고 쓸 준비가 되었거나 오류가 발생했다는 알림입니다. NGINX는 수많은 이벤트를 수신한 후 이를 차례로 처리하고 필요한 작업을 수행합니다. 따라서 모든 처리는 스레드의 대기열을 통해 간단한 루프로 수행됩니다. NGINX는 대기열에서 이벤트를 가져와 소켓 읽기 및 쓰기와 같이 이에 응답합니다. 대부분의 경우 이 접근 방식은 매우 빠르며(일부 데이터를 메모리에 복사하는 데 약간의 CPU 주기만 필요할 수도 있음) NGINX는 대기열의 모든 이벤트를 플래시로 처리할 수 있습니다. 모든 처리는 스레드 에 의해 간단한 루프로 이루어집니다. 완료 그런데 NGINX에서 처리할 작업이 길고 무거운 작업이라면 어떻게 될까요? 전체 이벤트 처리 루프는 이 작업이 완료되기를 기다리는 동안 중단됩니다. 따라서 "차단 작업"은 이벤트 처리 루프를 일정 기간 동안 크게 중지시키는 작업을 의미합니다. 다양한 이유로 작업이 차단 작업이 될 수 있습니다. 예를 들어, NGINX는 오랜 시간 동안 CPU를 많이 사용하는 처리로 인해 바쁘거나 리소스(예: 하드 디스크 또는 뮤텍스)에 액세스하거나 동기 모드로 데이터베이스에서 해당 라이브러리 함수 호출을 얻기 위해 대기 중일 수 있습니다. 등.). 요점은 이러한 작업을 처리하는 동안 대기열의 일부 이벤트에서 활용할 수 있는 사용 가능한 시스템 리소스가 더 있더라도 작업자 프로세스가 다른 작업을 수행하거나 다른 이벤트를 처리할 수 없다는 것입니다. 비유를 들어보겠습니다. 점원은 앞에 길게 늘어선 고객을 상대해야 합니다. 줄의 첫 번째 고객은 매장이 아닌 창고에 있는 품목을 원했습니다. 판매원은 물건을 가지러 창고로 달려갔습니다. 이제 팀 전체는 이런 종류의 배포를 위해 몇 시간을 기다려야 하고 팀 구성원 모두가 불만스러워합니다. 사람들의 반응을 상상할 수 있겠죠? 이 시간은 구매하려는 품목이 매장에 있지 않는 한 줄을 선 모든 사람의 대기 시간에 추가됩니다. 모두 줄을 서서 먼저 구매하는 사람을 기다려야 했습니다 NGINX에서도 거의 같은 상황이 발생합니다. 예를 들어 파일을 읽을 때 파일이 메모리에 캐시되어 있지 않으면 디스크에서 읽어야 합니다. 디스크(특히 회전하는 디스크)에서 읽는 속도가 느리고 대기열에 대기 중인 다른 요청은 디스크에 액세스할 필요가 없을 때 강제로 대기하게 됩니다. 결과적으로 대기 시간이 증가하고 시스템 리소스가 제대로 활용되지 않습니다. 한 번의 차단 작업으로 인해 모든 후속 작업이 상당히 지연될 수 있습니다. 일부 운영 체제에서는 파일 읽기 및 쓰기를 위한 비동기 인터페이스인 NGINX를 제공합니다. 이러한 인터페이스를 사용할 수 있습니다(AIO 지침 참조). FreeBSD가 좋은 예입니다. 불행히도 Linux에서는 동일한 이점을 얻을 수 없습니다. Linux는 파일 읽기를 위한 비동기 인터페이스를 제공하지만 분명한 단점도 있습니다. 그 중 하나는 파일 액세스와 버퍼링을 정렬해야 하는데 NGINX가 이를 매우 잘 처리한다는 것입니다. 그러나 다른 단점은 더욱 심각합니다. 비동기 인터페이스에서는 파일 설명자에 O_DIRECT 플래그를 설정해야 합니다. 즉, 파일에 대한 모든 액세스가 메모리 내 캐시를 우회하여 디스크의 로드가 증가한다는 의미입니다. 이것이 최선의 선택이 아닌 경우가 많이 있습니다. 이 문제를 타겟 방식으로 해결하기 위해 NGINX 1.7.11에서 스레드 풀이 도입되었습니다. 기본적으로 NGINX+에는 아직 스레드 풀이 포함되어 있지 않지만 사용해 보고 싶다면 영업팀에 문의하세요. NGINX+ R6은 스레드 풀이 활성화된 빌드입니다. 이제 스레드 풀에 들어가서 이것이 무엇인지, 어떻게 작동하는지 살펴보겠습니다. 3. Thread Pool 물건을 배급하기 위해 창고부터 먼 곳까지 가야 하는 불쌍한 판매원의 이야기로 돌아가자. 이번에 그는 더 똑똑해졌고(혹은 화가 난 고객들의 강의를 듣고 더 똑똑해졌을 수도?) 주문 처리 서비스 팀을 고용했습니다. 이제 멀리 떨어진 창고에 있는 물건을 사고 싶은 사람은 더 이상 창고에 직접 갈 필요 없이 주문을 주문 처리 서비스에 맡기면 주문이 처리됩니다. 동시에 우리 영업사원은 계속해서 다른 고객에게 서비스를 제공할 수 있습니다. 따라서 창고에서 상품을 구매하려는 고객만 배송을 기다리면 되고, 다른 고객은 즉시 서비스를 받을 수 있습니다. 주문을 처리 서비스로 전달해도 대기열이 차단되지 않습니다. Yes NGINX의 경우 스레드 풀은 배포 서비스 기능을 수행합니다. 이는 작업 대기열과 이 대기열을 처리하는 스레드 집합으로 구성됩니다. 작업자 프로세스가 잠재적으로 긴 작업을 수행해야 하는 경우 작업자 프로세스는 더 이상 작업 자체를 수행하지 않고 작업을 스레드 풀 대기열에 넣습니다. 그러면 유휴 스레드는 대기열에서 제거될 수 있습니다. queue 이 작업을 가져와 실행합니다.
작업자 프로세스는 차단 작업을 스레드 풀로 오프로드합니다
그래서 대기열이 하나 더 있는 것과 같습니다. 예. 하지만 이 시나리오에서는 대기열이 특수 리소스로 제한됩니다. 디스크가 데이터를 생성할 수 있는 것보다 더 빠르게 디스크를 읽을 수는 없습니다. 어쨌든, 적어도 이제 디스크는 더 이상 다른 이벤트를 지연시키지 않으며 파일 액세스 요청만 기다려야 합니다. 일반적으로 "디스크에서 읽기" 작업은 차단 작업의 가장 일반적인 예이지만 실제로 NGINX에 구현된 스레드 풀을 사용하면 메인 루프에 맞지 않는 모든 작업을 처리할 수 있습니다. 현재 스레드 풀에 오프로드된 두 가지 기본 작업은 대부분의 운영 체제에서 read() 시스템 호출이고 Linux에서는 sendfile()입니다. 다음으로 스레드 풀을 테스트하고 벤치마킹할 것입니다. 향후 버전에서는 확실한 이점이 있는 경우 다른 작업을 스레드 풀로 오프로드할 수 있습니다. 4. 벤치마킹 이제 이론에서 실습으로 넘어가겠습니다. 차단 작업과 비차단 작업이 혼합된 최악의 상황에서 스레드 풀을 사용할 때의 효과를 시뮬레이션하는 합성 벤치마크를 수행합니다. 또한 메모리에 전혀 맞지 않는 데이터 세트가 필요합니다. 48GB RAM이 있는 시스템에서 각각 4MB의 파일 크기로 총 256GB의 임의 데이터를 생성한 다음 버전 1.9.0으로 NGINX를 구성했습니다. 구성은 간단합니다. 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;
}
}
}위에 표시된 것처럼 더 나은 성능을 달성하기 위해 여러 매개변수를 조정했습니다. 로깅과 accept_mutex를 동시에 비활성화하고 sendfile을 활성화하고 설정했습니다. sendfile_max_chunk의 크기입니다. 이 마지막 지시문은 NGINX가 전체 파일을 한 번에 전송하려고 시도하지 않고 대신 매번 512KB의 청크로 데이터를 전송하기 때문에 sendfile() 호출을 차단하는 데 소요되는 최대 시간을 줄입니다. 이 테스트 서버에는 Intel Xeon E5645 프로세서 2개(총 코어 12개, 하이퍼 스레드 24개)와 10Gbps 네트워크 인터페이스가 있습니다. 디스크 하위 시스템은 Western Digital WD1003FBYX 4개로 구성됩니다.
RAID10 디스크 배열. 이 모든 하드웨어는 Ubuntu Server 14.04.1 LTS로 구동됩니다.
벤치마킹을 위한 로드 생성기 및 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配置参数编译。在最简单的场景中,配置看起来很朴实。我们只需要在http、 server,或者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,我们可以有其他的选择:# 我们假设每块硬盘挂载在相应的目录中:/mnt/disk1、/mnt/disk2、/mnt/disk3
proxy_cache_path /mnt/disk1 levels=1:2 keys_z
use_temp_path=off;
proxy_cache_path /mnt/disk2 levels=1:2 keys_z
use_temp_path=off;
proxy_cache_path /mnt/disk3 levels=1:2 keys_z
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://backend;
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와 호환되지 않았습니다. 자체적인 비차단 프로토타입 라이브러리를 개발하는 데 많은 시간과 자원을 투자할 수 있지만 항상 그만한 가치가 있습니까? 이제 스레드 풀을 사용하면 이러한 모듈의 성능에 영향을 주지 않고 이러한 라이브러리를 비교적 쉽게 사용할 수 있습니다.
이상으로 Nginx의 스레드 풀과 성능 분석을 소개했으며, PHP 튜토리얼에 관심이 있는 친구들에게 도움이 되기를 바랍니다.
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.