日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
流量一來(lái),時(shí)間變慢,怪PHP-FPM進(jìn)程數(shù)不夠?

感覺(jué)php-FPM進(jìn)程數(shù)不夠?

柯橋ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書(shū)銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書(shū)合作)期待與您的合作!

作為一個(gè) phper,用的最多的架構(gòu)就是 LNMP。每次一到流量來(lái)了,我們的服務(wù)就從原來(lái)的 幾百毫秒到幾秒的時(shí)間。這個(gè)時(shí)候我們各種猜測(cè),mysql 有慢 sql,redis 有大 key,php-fpm 進(jìn)程數(shù)不夠等等情況。其中可以通過(guò)業(yè)務(wù)的一些日志來(lái)排查如上情況。我們這次主要證明的卻是 php-fpm 進(jìn)程數(shù)不夠情況的實(shí)踐。

重現(xiàn)現(xiàn)場(chǎng)

1.將我本地的的 PHP-FPM 進(jìn)程數(shù)調(diào)整為 2

#vim /etc/php-fpm.d/www.conf
pm = static
pm.max_children = 2

2.使用 ab 來(lái)壓測(cè)接口

$ ab -c 40  -n 3000 http://127.0.0.1/group/check_groups
Server Software:        nginx/1.16.0
Server Hostname:        miner_platform.cn
Server Port:            80
Document Path:          /group/check_groups
Document Length:        44 bytes
Concurrency Level:      40
Time taken for tests:   29.384 seconds
Complete requests:      3000
Failed requests:        0
Write errors:           0
Total transferred:      699000 bytes
HTML transferred:       132000 bytes
Requests per second:    102.10 [#/sec] (mean)
Time per request:       391.788 [ms] (mean)
Time per request:       9.795 [ms] (mean, across all concurrent requests)
Transfer rate:          23.23 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       3
Processing:   306  344  80.6    318    3558
Waiting:      306  343  80.5    318    3555
Total:        307  344  80.6    318    3558
Percentage of the requests served within a certain time (ms)
  50%    318
  66%    322
  75%    333
  80%    369
  90%    428
  95%    461
  98%    508
  99%    553
 100%   3558 (longest request)

嘗試解決問(wèn)題

1. PHP-FPM STATUS

我們發(fā)現(xiàn)接口 318ms 到 3.558s 的都有,那我們?nèi)绾沃?php-fpm 進(jìn)程少不夠?qū)е逻@個(gè)問(wèn)題呢?換一種說(shuō)話有什么辦法能讓我們知道 php-fpm 內(nèi)部是處理不過(guò)來(lái)嗎? 這個(gè)時(shí)候我們就需要打開(kāi) php-fpm 內(nèi)置 status 了。

詳細(xì)步驟參考:https://www.php.cn/php-weizijiaocheng-485633.html

$ curl http://127.0.0.1/status.php
pool:                 www
process manager:      static
start time:           29/Nov/2021:18:27:38 +0800
start since:          6493
accepted conn:        3136
listen queue:         38
max listen queue:     39
listen queue len:     128
idle processes:       0
active processes:     2
total processes:      2
max active processes: 2
max children reached: 0
slow requests:        0

具體詳細(xì)的字段可以參見(jiàn)上面的鏈接,有詳細(xì)說(shuō)明,我們主要說(shuō)下幾個(gè)參數(shù)

  • listen queue:這個(gè)就是此時(shí)此刻我們的 php-fpm 作為服務(wù)端,處于 accept 隊(duì)列 的數(shù)量。

  • max listen queue: 從 php-fpm 進(jìn)程啟動(dòng)到現(xiàn)在處于等待連接的最大數(shù)量(說(shuō)白了,就是我們上面說(shuō)的 listen queue 的最大值持久化)

  • listen queue len : 有過(guò) socket 網(wǎng)絡(luò)編程經(jīng)驗(yàn)的同學(xué)都知道。int listen(int sockfd, int backlog); 是可以設(shè)置該參數(shù),但是他和系統(tǒng)設(shè)置有關(guān)系。

2. netstat 查看鏈接狀態(tài)

我們得到的結(jié)論是:當(dāng) php-fpm 進(jìn)程處理不過(guò)來(lái)的時(shí)候,請(qǐng)求就會(huì)放在 accept 隊(duì)列,知道了這個(gè)情況以后,我們甚至不需要通過(guò) status。

第一行表示的監(jiān)聽(tīng) socket, Recv-Q 表示 accept queue 長(zhǎng)度。

$netstat -antp | grep php-fpm
tcp       38      0 127.0.0.1:9000          0.0.0.0:*               LISTEN      97/php-fpm: master  
tcp        8      0 127.0.0.1:9000          127.0.0.1:55540         ESTABLISHED 964/php-fpm: pool w 
tcp        8      0 127.0.0.1:9000          127.0.0.1:55536         ESTABLISHED 965/php-fpm: pool w

綜上我們知道了,當(dāng) PHP-FPM 進(jìn)程數(shù)不夠的時(shí)候,nginx 客戶端請(qǐng)求的連接的 accept 隊(duì)列 長(zhǎng)度就會(huì)變大。這樣就完了嗎?不,我們還需要去分析為什么能得到這個(gè)現(xiàn)象。

原理分析

簡(jiǎn)述 PHP-FPM 工作過(guò)程

首先我們需要簡(jiǎn)單里說(shuō)一說(shuō) php-fpm 的工作過(guò)程。我們就簡(jiǎn)單模型一下它的偽代碼(這里只為了表述整個(gè) socket 的過(guò)程)

// 1. 創(chuàng)建 socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
// 2. 綁定socket
socket_bind($socket, "0.0.0.0", 9000);
// 3. 監(jiān)聽(tīng) socket
socket_listen($socket, 5);
for($i=0;$i<2;$i++) {
    $pid = pcntl_fork()
    // 4. 創(chuàng)建2個(gè)進(jìn)程
    if ($pid == 0) {
        // 5. 子進(jìn)程接受socket
        while($fd = socket_accept($socket)) {
            echo "客戶端${fd}連接" . PHP_EOL;
            $tmp = socket_read($fd, 1024);
            echo "client data:" . $tmp . PHP_EOL;
            $data = "HTTP/1.1 200 ok\r\nContent-Length:2\r\n\r\nhi";
            socket_write($fd, $data, strlen($data));
        }    
        exit;
    }
}
// 5. 監(jiān)聽(tīng)子進(jìn)程退出
// 其他 TODO

1.master 進(jìn)程創(chuàng)建了監(jiān)聽(tīng) socket,但是不處理業(yè)務(wù)正在

2.work 進(jìn)程接受同步堵塞接受請(qǐng)求(堵塞在 accept),然后處理業(yè)務(wù)。

抓取 nginx->php-fpm socket

我們知道了 php-fpm 大概工作的過(guò)程,這個(gè)時(shí)候我們就需要通過(guò)一次請(qǐng)求大概知道 nginx 與 php-fpm 交互的過(guò)程。

$curl http://miner_platform.cn/group/check_groups
{"code":10006,"message":"sign\u65e0\u6548."}

1.nginx 系統(tǒng)調(diào)用

需要關(guān)注的點(diǎn)都在這個(gè)里面注釋了。抓取的是 nginx work 進(jìn)程

$ strace -f -s 64400 -p 958
 strace: Process 958 attached
 epoll_wait(8, [{EPOLLIN, {u32=1226150064, u64=94773974503600}}], 512, -1) = 1
 accept4(6, {sa_family=AF_INET, sin_port=htons(46616), sin_addr=inet_addr("127.0.0.1")}, [112->16], SOCK_NONBLOCK) = 3
 epoll_ctl(8, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLRDHUP|EPOLLET, {u32=1226159737, u64=94773974513273}}) = 0
 epoll_wait(8, [{EPOLLIN, {u32=1226159737, u64=94773974513273}}], 512, 60000) = 1
 recvfrom(3, "GET /group/check_groups HTTP/1.1\r\nUser-Agent: curl/7.29.0\r\nHost: miner_platform.cn\r\nAccept: */*\r\n\r\n", 1024, 0, NULL, NULL) = 99
 stat("/data/miner_platform/src/public/group/check_groups", 0x7ffcb593d1b0) = -1 ENOENT (No such file or directory)
 stat("/data/miner_platform/src/public/group/check_groups", 0x7ffcb593d1b0) = -1 ENOENT (No such file or directory)
 epoll_ctl(8, EPOLL_CTL_MOD, 3, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=1226159737, u64=94773974513273}}) = 0
 lstat("/data", {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0
 lstat("/data/miner_platform", {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0
 lstat("/data/miner_platform/src", {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0
 lstat("/data/miner_platform/src/public", {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0
 getsockname(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("127.0.0.1")}, [112->16]) = 0
 // 1. 創(chuàng)建 socket    
 socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 11
 ioctl(11, FIONBIO, [1])                 = 0
 epoll_ctl(8, EPOLL_CTL_ADD, 11, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=1226163953, u64=94773974517489}}) = 0
 // 2. 連接 127.0.0.1:9000    
 connect(11, {sa_family=AF_INET, sin_port=htons(9000), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)    
 epoll_wait(8, [{EPOLLOUT, {u32=1226159737, u64=94773974513273}}, {EPOLLOUT, {u32=1226163953, u64=94773974517489}}], 512, 60000) = 2
 getsockopt(11, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
 // 3. 按照FASTCGI協(xié)議寫(xiě)入這次請(qǐng)求     
 writev(11, [{iov_base="\1\1\0\1\0\10\0\0\0\1\0\0\0\0\0\0\1\4\0\1\2!\7\0\17)SCRIPT_FILENAME/data/miner_platform/src/public/index.php\f\0QUERY_STRING\16\3REQUEST_METHODGET\f\0CONTENT_TYPE\16\0CONTENT_LENGTH\v\nSCRIPT_NAME/index.php\v\23REQUEST_URI/group/check_groups\f\nDOCUMENT_URI/index.php\r\37DOCUMENT_ROOT/data/miner_platform/src/public\17\10SERVER_PROTOCOLHTTP/1.1\16\4REQUEST_SCHEMEhttp\21\7GATEWAY_INTERFACECGI/1.1\17\fSERVER_SOFTWAREnginx/1.16.0\v\tREMOTE_ADDR127.0.0.1\v\5REMOTE_PORT46616\v\tSERVER_ADDR127.0.0.1\v\2SERVER_PORT80\v\21SERVER_NAMEminer_platform.cn\17\3REDIRECT_STATUS200\17\vHTTP_USER_AGENTcurl/7.29.0\t\21HTTP_HOSTminer_platform.cn\v\3HTTP_ACCEPT*/*\0\0\0\0\0\0\0\1\4\0\1\0\0\0\0\1\5\0\1\0\0\0\0", iov_len=592}], 1) = 592
 epoll_wait(8, [{EPOLLIN|EPOLLOUT, {u32=1226163953, u64=94773974517489}}], 512, 60000) = 1
 // 4. 接受 PHP-FPM響應(yīng)結(jié)果   
 recvfrom(11, "\1\6\0\1\0\257\1\0X-Powered-By: PHP/7.2.16\r\nCache-Control: no-cache, private\r\nDate: Wed, 01 Dec 2021 12:24:52 GMT\r\nContent-Type: application/json\r\n\r\n{\"code\":10006,\"message\":\"sign\\u65e0\\u6548.\"}\0\1\3\0\1\0\10\0\0\0\0\0\0\0\"}\0", 4096, 0, NULL, NULL) = 200
 epoll_wait(8, [{EPOLLIN|EPOLLOUT|EPOLLRDHUP, {u32=1226163953, u64=94773974517489}}], 512, 60000) = 1
 readv(11, [{iov_base="", iov_len=3896}], 1) = 0
 // 5. 關(guān)閉這次socket連接    
 close(11)                               = 0
 // 6. 響應(yīng)給瀏覽器    
 writev(3, [{iov_base="HTTP/1.1 200 OK\r\nServer: nginx/1.16.0\r\nContent-Type: application/json\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nX-Powered-By: PHP/7.2.16\r\nCache-Control: no-cache, private\r\nDate: Wed, 01 Dec 2021 12:24:52 GMT\r\n\r\n", iov_len=222}, {iov_base="2c\r\n", iov_len=4}, {iov_base="{\"code\":10006,\"message\":\"sign\\u65e0\\u6548.\"}", iov_len=44}, {iov_base="\r\n", iov_len=2}, {iov_base="0\r\n\r\n", iov_len=5}], 5) = 277
 write(5, "127.0.0.1 - - [01/Dec/2021:20:24:52 +0800] \"GET /group/check_groups HTTP/1.1\" 200 55 \"-\" \"curl/7.29.0\" \"-\" 1.029 127.0.0.1:9000 200 1.030\n", 138) = 138
 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
 epoll_wait(8, [{EPOLLIN|EPOLLOUT|EPOLLRDHUP, {u32=1226159737, u64=94773974513273}}], 512, 65000) = 1
 recvfrom(3, "", 1024, 0, NULL, NULL)    = 0
 close(3)                                = 0
 epoll_wait(8,

2.php-fpm 系統(tǒng)調(diào)用

抓取了 php-fpm work 進(jìn)程

// 1. accept 接收到了 nginx(127.0.0.1:45512 ) 客戶端發(fā)送的數(shù)據(jù)
965   accept(9, {sa_family=AF_INET, sin_port=htons(45512), sin_addr=inet_addr("127.0.0.1")}, [112->16]) = 4
中間省略了許多
// 2. 響應(yīng)給客戶端
965   write(4, "\1\6\0\1\0\257\1\0X-Powered-By: PHP/7.2.16\r\nCache-Control: no-cache, private\r\nDate: Wed, 01 Dec 2021 12:37:18 GMT\r\nContent-Type: application/json\r\n\r\n{\"code\":10006,\"message\":\"sign\\u65e0\\u6548.\"}\0\1\3\0\1\0\10\0\0\0\0\0\0\0p\0\0", 200) = 200
// 3. 不給給這個(gè)socket 寫(xiě)數(shù)據(jù)了
965   shutdown(4, SHUT_WR)              = 0
// 4. 接受nginx(127.0.0.1:45512 )客戶端數(shù)據(jù) 
965   recvfrom(4, "\1\5\0\1\0\0\0\0", 8, 0, NULL, NULL) = 8
// 5. 接受nginx(127.0.0.1:45512 )客戶端數(shù)據(jù) 
965   recvfrom(4, "", 8, 0, NULL, NULL) = 0
// 6. 關(guān)閉這個(gè)連接
965   close(4)                          = 0
965   lstat("/data/miner_platform/src/vendor/composer/../../app/Http/Middleware/BusinessHeaderCheck.php", {st_mode=S_IFREG|0777, st_size=989, ...}) = 0
965   stat("/data/miner_platform/src/app/Http/Middleware/BusinessHeaderCheck.php", {st_mode=S_IFREG|0777, st_size=989, ...}) = 0
965   chdir("/")                        = 0
965   times({tms_utime=3583, tms_stime=1977, tms_cutime=0, tms_cstime=0}) = 4315309933
965   setitimer(ITIMER_PROF, {it_interval={tv_sec=0, tv_usec=0}, it_value={tv_sec=0, tv_usec=0}}, NULL) = 0
965   fcntl(3, F_SETLK, {l_type=F_UNLCK, l_whence=SEEK_SET, l_start=0, l_len=0}) = 0
965   setitimer(ITIMER_PROF, {it_interval={tv_sec=0, tv_usec=0}, it_value={tv_sec=0, tv_usec=0}}, NULL) = 0
965   accept(9,

TCP 三次握手

上面我們已經(jīng)清楚了一次請(qǐng)求,請(qǐng)求并發(fā)高的時(shí)候流程也是如此,這個(gè)時(shí)候我們就引出了下面這個(gè)圖與我們上面描述的過(guò)程是一樣的,只是細(xì)化了三次握手的過(guò)程。這個(gè)時(shí)候我們引出了 sync queue 和 accept queue。

  • 我們調(diào)用 listen (上面是 php-fpm master 進(jìn)程執(zhí)行的),于此同時(shí)內(nèi)核創(chuàng)建了兩個(gè)隊(duì)列 sync queue 和 accept queue

  • 三次握手第二步當(dāng) Server (指的是 php-fpm master 進(jìn)程) 發(fā)送了 SYN+ACK 報(bào)文后,此時(shí)會(huì)將這個(gè)信息放入到 sync queue

  • 當(dāng)三次握手完成時(shí),未被應(yīng)用 (指的是 php-fpm work 進(jìn)程) 調(diào)用 accept 取走的連接隊(duì)列。此時(shí)的 socket 處于 ESTABLISHED 狀態(tài)。每次應(yīng)用調(diào)用 accept() 函數(shù)會(huì)移除隊(duì)列頭的連接。如果隊(duì)列為空,accept () 通常會(huì)阻塞。全連接隊(duì)列也被稱為 accept queue。

結(jié)論


網(wǎng)頁(yè)標(biāo)題:流量一來(lái),時(shí)間變慢,怪PHP-FPM進(jìn)程數(shù)不夠?
URL地址:http://www.dlmjj.cn/article/dpiddhe.html