標籤:opcache
PHP7 opcache緩衝清理問題
背景
OPcache通過opcode的緩衝和最佳化,提供更快的PHP執行過程。
業務在php7環境運營時,為了提升請求的效能,在PHP7環境中配置OPcache擴充。
業務在更新代碼後,訪問業務系統時提示無法找到對應的檔案或請求的內容還是更新前的舊內容,
webserver重啟以後,請求訪問到的檔案就都是最新的了,問題就貌似解決了。
問題分析
根據現象分析,代碼更新後請求找不到新增的檔案,尤其是還在請求已有檔案更新前的內容,那麼可能跟緩衝有關係,考慮到跟業務代碼邏輯無關,關閉opcache的配置問題就不再出現,基本上可以定位到問題出在opcache的配置上。
650) this.width=650;" src="https://s4.51cto.com/wyfs02/M02/9C/B1/wKiom1l0u8LxZ6VcAAA6HvSBfs0135.png-wh_500x0-wm_3-wmp_4-s_608633139.png" class="amplify" style="border:medium none;vertical-align:top;height:auto;" title="1500543707_60_w1002_h134.png" alt="wKiom1l0u8LxZ6VcAAA6HvSBfs0135.png-wh_50" />
650) this.width=650;" src="https://s1.51cto.com/wyfs02/M01/9C/B1/wKioL1l0u8Kz-X7GAABTlh-FtMc965.png-wh_500x0-wm_3-wmp_4-s_2036350728.png" title="1500543717_97_w838_h306.png" alt="wKioL1l0u8Kz-X7GAABTlh-FtMc965.png-wh_50" />
cat /usr/local/php/etc/subconfig/opcache.inizend_extension=opcache.soopcache.enable=1opcache.revalidate_freq=0opcache.validate_timestamps=0opcache.max_accelerated_files=7963opcache.memory_consumption=192opcache.interned_strings_buffer=16opcache.fast_shutdown=1opcache.enable_cli=1
opcache.enable 啟用作業碼緩衝,預設為“1”
如果禁用此選項,則不會最佳化和緩衝代碼。 在運行期使用 ini_set() 函數只能禁用 opcache.enable 設定,不可以啟用此設定。 如果在指令碼中嘗試啟用此設定項會產生警告。
opcache.enable_cli 僅針對 CLI 版本的 PHP 啟用作業碼緩衝。
通常被用來測試和調試。
opcache.revalidate_freq=0 檢查指令碼時間戳記是否有更新的周期,以秒為單位。
設定為 0 會導致針對每個請求, OPcache 都會檢查指令碼更新。
opcache.validate_timestamps=0 如果啟用,那麼 OPcache 會每隔 opcache.revalidate_freq 設定的秒數 檢查指令碼是否更新。
如果禁用此選項,你必須使用 opcache_reset() 或者 opcache_invalidate() 函數來手動重設 OPcache,也可以 通過重啟 Web 服務器來使檔案系統更改生效。
最初的配置是:
opcache.revalidate_freq=60,opcache.validate_timestamps=1
即每60秒檢測一次更新位元組程式碼快取,業務代碼更新後可能需要60秒以後才能訪問到最新的內容,也就出現了最初訪問不到新增的內容。
代碼更新方式
php代碼的更新方式有兩種,一種是覆蓋webserver配置的目錄下的檔案來更新,一種是每次都部署一個全量包目錄,然後軟連結到webserver指定的目錄。
第一種覆蓋更新的方式,如果使用在到期時間後自動清理opcache緩衝內容的話,更新操作如果有延遲,就會出現新舊代碼檔案混合在一起的情況。
第二種全量包目錄發布後,軟連結到webserver指定路徑的方式,雖然不會存在新舊檔案混合的問題,但是在未自動清理時,即便webserver已經連結到webserver對應目錄,業務訪問的還是舊檔案。
代碼緩衝的問題
目前使用rsync同步目錄檔案的方式是我們更新代碼的主要方式,最初使用每60s定時清理opcache的快取檔案,在60s內更新的檔案不會生效,就導致了業務反饋代碼更新後訪問不到的問題。
使用定時更新代碼緩衝的問題,還有更新檔案較多時,代碼檔案發布的過程中緩衝發生更新,將會有60s新舊檔案的緩衝混合存在的問題。
根據相關研究人員推薦,如果採用覆蓋更新代碼檔案時,更新操作完畢後,手動清理緩衝比較合適。
opcache.validate_timestamps=0
即,將oopcache.validate_timestamps設定為0。
配置了opcache.validate_timestamps值為0,必須手動清空Zend OPcache緩衝的位元組碼,才能訪問到最新的檔案內容。適合在生產環境中設定為0,但在開發環境會帶來不便,可以在開發環境中這樣配置啟用自動驗證緩衝功能:
opcache.validate_timestamps=1
opcache.revalidate_freq=0
手動清理緩衝
除了重啟php-fpm的進程可以清理opcache緩衝外,
手動清理緩衝涉及到的opcache函數主要為:opcache_reset()和opcache_invalidate() 。
boolean opcache_reset ( void )該函數將重設整個位元組程式碼快取。
在調用 opcache_reset() 之後,所有的指令碼將會重新載入並且在下次被點擊的時候重新解析。
需要注意的是,當PHP以PHP-FPM的方式啟動並執行時候,opcache的緩衝是無法通過php命令進行清除的,只能通過http或cgi到php-fpm進程的方式來清除緩衝。
In some (most?) systems,
PHP‘s CLI has a separate opcode cache to the one used by the web server ,
or PHP-FPM process,which means running opcache_reset() in the CLI,
won‘t reset the webserver/fpm opcode cache, and vice-versa.
曲線救國,使用命令列清理php-fpm的opcache緩衝:
#!/bin/bashcgi-fcgi -v > /dev/null 2>&1|| yum --enablerepo=epel install fcgi -y > /dev/null 2>&1echo ‘<?php opcache_reset(); echo "ok\n";‘ > /tmp/php-fpm-opcache-reset.php;SCRIPT_FILENAME=/tmp/php-fpm-opcache-reset.php REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000;rm -f /tmp/php-fpm-opcache-reset.php;
opcache_invalidate 廢除指定指令碼緩衝
boolean opcache_invalidate ( string $script [, boolean $force = FALSE ] )該函數的作用是使得指定指令碼的位元組程式碼快取失效。 如果 force 沒有設定或者傳入的是 FALSE,那麼只有當指令碼的修改時間 比對應位元組碼的時間更新,指令碼的緩衝才會失效。參數 script緩衝需要被作廢對應的指令碼路徑force如果該參數設定為TRUE,那麼不管是否必要,該指令碼的緩衝都將被廢除。
opcache_invalidate可以針對單個或幾個指令碼進行來清理緩衝。
總結
如果代碼發布是全量發布,切換軟連結的方式,可以設定opcache.validate_timestamps=1和opcache.validate_timestamps=1來定時自動更新緩衝。
如果代碼發布是覆蓋更新舊目錄,則可以重啟php-fpm及在指令碼中或代碼檔案中使用opcache_reset函數來清理所有緩衝。
如果可以擷取到更新的代碼檔案清單,則可以使用opcache_invalidate函數來清理代碼,同時也可以避免影響到其他業務的緩衝。
PHP7 opcache緩衝清理問題