PHP Opcache的工做原理

PHP項目中,尤爲是在高併發大流量的場景中,如何提高PHP的響應時間,是一項十分重要的工做。php

而Opcache又是優化PHP性能不可缺失的組件,尤爲是應用了PHP框架的項目中,做用更是明顯。面試

1. 概述

在理解 OPCache 功能以前,咱們有必要先理解PHP-FPM + Nginx 的工做機制,以及PHP腳本解釋執行的機制。json

1.1 PHP-FPM + Nginx 的工做機制

請求從Web瀏覽器到Nginx,再到PHP處理完成,一共要經歷以下五個步驟:瀏覽器

第一步:啓動服務緩存

  • 啓動PHP-FPM。PHP-FPM 支持兩種通訊模式:TCP socket和Unix socket;
  • PHP-FPM 會啓動兩種類型的進程:Master 進程 和 Worker 進程,前者負責監控端口、分配任務、管理Worker進程;後者就是PHP的cgi程序,負責解釋編譯執行PHP腳本。
  • 啓動Nginx。首先會載入 ngx_http_fastcgi_module 模塊,初始化FastCGI執行環境,實現FastCGI協議請求代理
  • 這裏要注意:fastcgi的worker進程(cgi進程),是由PHP-FPM來管理,不是Nginx。Nginx只是代理

第二步:Request => Nginx服務器

  • Nginx 接收請求,並基於location配置,選擇一個合適handler
  • 這裏就是代理PHP的 handler

第三步:Nginx => PHP-FPM架構

  • Nginx 把請求翻譯成fastcgi請求
  • 經過TCP socket/Unix Socket 發送給PHP-FPM 的master進程

第四步:PHP-FPM Master => Worker併發

  • PHP-FPM master 進程接收到請求
  • 分配Worker進程執行PHP腳本,若是沒有空閒的Worker,返回502錯誤
  • Worker(php-cgi)進程執行PHP腳本,若是超時,返回504錯誤
  • 處理結束,返回結果

第五步:PHP-FPM Worker => Master => Nginx框架

  • PHP-FPM Worker 進程返回處理結果,並關閉鏈接,等待下一個請求
  • PHP-FPM Master 進程經過Socket 返回處理結果
  • Nginx Handler順序將每個響應buffer發送給第一個filter → 第二個 → 以此類推 → 最終響應發送給客戶端

1.2 PHP腳本解釋執行的機制

瞭解了PHP + Nginx 總體的處理流程後,咱們接下來看一下PHP腳本具體執行流程,首先咱們看一個實例:socket

<?php
if (!empty($_POST)) {
    echo "Response Body POST: ", json_encode($_POST), "\n";
}

if (!empty($_GET)) {
    echo "Response Body GET: ", json_encode($_GET), "\n";
}

咱們分析一下執行過程:

  1. php初始化執行環節,啓動Zend引擎,加載註冊的擴展模塊
  2. 初始化後讀取腳本文件,Zend引擎對腳本文件進行詞法分析(lex),語法分析(bison),生成語法樹
  3. Zend 引擎編譯語法樹,生成opcode,
  4. Zend 引擎執行opcode,返回執行結果

在PHP cli模式下,每次執行PHP腳本,四個步驟都會依次執行一遍;

在PHP-FPM模式下,步驟1)在PHP-FPM啓動時執行一次,後續的請求中再也不執行;步驟2)~4)每一個請求都要執行一遍;

其實步驟2)、3)生成的語法樹和opcode,同一個PHP腳本每次運行的結果都是同樣的,

在PHP-FPM模式下,每次請求都要處理一遍,是對系統資源極大的浪費,那麼有沒有辦法優化呢?

固然有,如:

  • OPCache:前身是Zend Optimizer+ ,是 Zend Server 的一個開源組件;官方出品,強力推薦
  • APC:Alternative PHP Cache 是一個開放自由的 PHP opcode 緩存組件,用於緩存、優化 PHP 中間代碼;已經不更新了不推薦
  • APCu:是APC的一個分支,共享內存,緩存用戶數據,不能緩存opcode,能夠配合Opcache 使用
  • eAccelerate:一樣是不更新了,不推薦
  • xCache:再也不推薦使用了

2. OPCache 介紹

OPCache 是Zend官方出品的,開放自由的 opcode 緩存擴展,還具備代碼優化功能,省去了每次加載和解析 PHP 腳本的開銷。

PHP 5.5.0 及後續版本中已經綁定了 OPcache 擴展。

緩存兩類內容:

  • OPCode
  • Interned String,如註釋、變量名等

3. OPCache 原理

OPCache緩存的機制主要是:將編譯好的操做碼放入共享內存,提供給其餘進程訪問。

這裏就涉及到內存共享機制,另外全部內存資源操做都有鎖的問題,咱們一一解讀。

3.1 共享內存

UNIX/Linux 系統提供不少種進程間內存共享的方式:

  • System-V shm API: System V共享內存,
    • sysv shm是持久化的,除非被一個進程明確的刪除,不然它始終存在於內存裏,直到系統關機;
  • mmap API:
    • mmap映射的內存在不是持久化的,若是進程關閉,映射隨即失效,除非事先已經映射到了一個文件上
    • 內存映射機制mmap是POSIX標準的系統調用,有匿名映射和文件映射兩種
    • mmap的一大優勢是把文件映射到進程的地址空間
    • 避免了數據從用戶緩衝區到內核page cache緩衝區的複製過程;
    • 固然還有一個優勢就是不須要頻繁的read/write系統調用
  • POSIX API:System V 的共享內存是過期的, POSIX共享內存提供了使用更簡單、設計更合理的API.
  • Unix socket API

OPCache 使用了前三個共享內存機制,根據配置或者默認mmap 內存共享模式。

依據PHP字節碼緩存的場景,OPCache的內存管理設計很是簡單,快速讀寫,不釋放內存,過時數據置爲Wasted。

當Wasted內存大於設定值時,自動重啓OPCache機制,清空並從新生成緩存。

3.2 互斥鎖

任何內存資源的操做,都涉及到鎖的機制。

共享內存:一個單位時間內,只容許一個進程執行寫操做,容許多個進程執行讀操做;

寫操做同時,不阻止讀操做,以致於不多有鎖死的狀況。

這就引起另一個問題:新代碼、大流量場景,進程排隊執行緩存opcode操做;重複寫入,致使資源浪費。

PHP進階架構師>>>視頻、面試文檔免費獲取docs.qq.com圖標

4. OPCache 緩存解讀

OPCache 是官方的Opcode 緩存解決方案,在PHP5.5版本以後,已經打包到PHP源碼中一塊兒發佈。

它將PHP編譯產生的字節碼以及數據緩存到共享內存中, 在每次請求,從緩存中直接讀取編譯後的opcode,進行執行。

經過節省腳本的編譯過程,提升PHP的運行效率。

若是正在使用APC擴展,作一樣的工做,如今強烈推薦OPCache來代替,尤爲是PHP7中。

4.1 OPCode 緩存

Opcache 會緩存OPCode以及以下內容:

  • PHP腳本涉及到的函數
  • PHP腳本中定義的Class
  • PHP腳本文件路徑
  • PHP腳本OPArray
  • PHP腳本自身結構/內容

4.2 Interned String 緩存

首先咱們須要理解,什麼是 Interned String?

在PHP5.4的時候, 引入了Interned String機制, 用於優化PHP對字符串的存儲和處理。

尤爲是處理大塊的字符串,好比PHP doces時,Interned String 能夠優化內存。

Interned String 緩存的內容包括:變量名稱、類名、方法名、字符串、註釋等。

在PHP-FPM模式中,Interned String 緩存字符,僅限於Worker 進程內部。

而緩存到OPCache中,那麼Worker進程之間可使用 Interned String 緩存的字符串,節省內存。

咱們須要注意一個事情,在PHP開發中,通常會有大段的註釋,也會被緩存到OPCache中。

能夠經過php.ini的配置,關閉註釋的緩存。

可是,像Zend Framework等框架中,會引用註釋,因此,是否關閉註釋的緩存,須要區別對待。

5. OPCache 更新策略

是緩存,都存在過時,以及更新策略等。

而OPCache的更新策略很是簡單,到期數據置爲Wasted,達到設定值,清空緩存,重建緩存。

這裏須要注意:在高流量的場景下,重建緩存是一件很是耗費資源的事兒。

OPCache 在建立緩存時並不會阻止其餘進程讀取。

這會致使大量進程反覆新建緩存。因此,不要設置OPCache過時時間

每次發佈新代碼時,都會出現反覆新建緩存的狀況。如何避免呢?

  • 不要在高峯期發佈代碼,這是任何狀況下都要遵照的規則
  • 代碼預熱,好比使用腳本批量調PHP 訪問URL,或者使用OPCache 暴露的API 如opcache_compile_file() 進行編譯緩存

6. OPCache 的配置

6.1 內存配置

  • opcache.preferred_memory_model="mmap" OPcache 首選的內存模塊。若是留空,OPcache 會選擇適用的模塊, 一般狀況下,自動選擇就能夠知足需求。可選值包括: mmapshm, posix 以及 win32
  • opcache.memory_consumption=64 OPcache 的共享內存大小,以兆字節爲單位,默認64M
  • opcache.interned_strings_buffer=4 用來存儲臨時字符串的內存大小,以兆字節爲單位,默認4M
  • opcache.max_wasted_percentage=5 浪費內存的上限,以百分比計。若是達到此上限,那麼 OPcache 將產生從新啓動續發事件。默認5

6.2 容許緩存的文件數量以及大小

  • opcache.max_accelerated_files=2000 OPcache 哈希表中可存儲的腳本文件數量上限。真實的取值是在質數集合 { 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987 } 中找到的第一個大於等於設置值的質數。設置值取值範圍最小值是 200,最大值在 PHP 5.5.6 以前是 100000,PHP 5.5.6 及以後是 1000000。默認值2000
  • opcache.max_file_size=0 以字節爲單位的緩存的文件大小上限。設置爲 0 表示緩存所有文件。默認值0

6.3 註釋相關的緩存

  • opcache.load_commentsboolean 若是禁用,則即便文件中包含註釋,也不會加載這些註釋內容。本選項能夠和 opcache.save_comments 一塊兒使用,以實現按需加載註釋內容。
  • opcache.fast_shutdown boolean 若是啓用,則會使用快速中止續發事件。所謂快速中止續發事件是指依賴 Zend 引擎的內存管理模塊 一次釋放所有請求變量的內存,而不是依次釋放每個已分配的內存塊。

6.4 二級緩存的配置

  • opcache.file_cache 配置二級緩存目錄並啓用二級緩存。啓用二級緩存能夠在 SHM 內存滿了、服務器重啓或者重置 SHM 的時候提升性能。默認值爲空字符串 "",表示禁用基於文件的緩存。
  • opcache.file_cache_onlyboolean 啓用或禁用在共享內存中的 opcode 緩存。
  • opcache.file_cache_consistency_checksboolean 當從文件緩存中加載腳本的時候,是否對文件的校驗和進行驗證。
  • opcache.file_cache_fallbackboolean 在 Windows 平臺上,當一個進程沒法附加到共享內存的時候, 使用基於文件的緩存,也即:opcache.file_cache_only=1。須要顯示的啓用文件緩存。

以上內容但願幫助到你們

相關文章
相關標籤/搜索