問題描述
我們的團隊正在開發 WordPress 外掛,並在幾個獨立的伺服器上提供託管的例項。我們的 WordPress 安裝由 Git 管理,所有伺服器具有相同的來源和 WordPress 安裝部署,只有域& 資料庫中的實際資料有所不同。對於每次安裝,MySql 都在同一主機上執行。 WordPress 正在每個伺服器上執行。
然而,在 Windows Server 2008 RC2 上部署了此設定後,我們注意到與其他伺服器相比效能差異很大:頁面生成時間從平均值上升。使用 PHP 生成的頁面為 400 到 4000-5000ms 。對於由 Apache 提供的靜態資源,速度與 linux 上的速度大致相同。
所以我們採取了一些步驟來縮小問題:
-
確保沒有 antivir-software 執行或其他 Windows 域的東西幹擾
-
收集分析資料以在指令碼執行期間識別時間盤點
-
測試不同的伺服器硬體設定
-
Double-check 的 Apache 和 PHP 配置都有明顯的配置錯誤
經過一些分析,我們很快就注意到,正規表示式的評估在我們的 Windows 機器上是非常緩慢的。評估 10.000 正規表示式 (preg_match) 在 Linux 上大約 90ms,Windows 上為 3000ms 。
配置檔案,系統測試和配置詳細資訊如下。我們不想最佳化這個指令碼 (我們知道如何做) 。我們希望讓指令碼在 Windows 上執行大致相同的速度 (在與 opcache /… 相同的設定中) 。也不需要最佳化指令碼的記憶體佔用。
更新:一段時間後,系統似乎耗盡記憶體,觸發記憶體異常和隨機分配。有關詳細資訊,請參閱下文。重新啟動 Apache /PHP 解決了現在的問題。
追溯到_get_browser 是:
File (called from)
require wp-blog-header.php (index.php:17)
wp (wp-blog-header.php:14)
WP->main (functions.php:808)
php::do_action_ref_array (class-wp.php:616)
php::call_user_func_array (wp-includes/plugin:507)
wp_slimstat::slimtrack (php::internal (507))
wp_slimstat::_get_browser (wp-slimstat.php:385)
更新 2:有些原因我不記得我們回到在我們的伺服器上啟用 PHP 作為 Apache 模組 (同樣的,表現不佳) 。但是今天他們執行得很快 (〜 1sec /request) 。新增 Opcache 可將其降低至〜 400ms /req 。 Apache /PHP /Windows 保持不變。
1) 分析結果
在所有機器上使用 XDebug 進行分析。通常我們只收集了幾個遊戲 – 那些足以顯示大部分時間 (50%+) 花費的位置:WordPress 外掛 wp-slimstats 的方法 [get_browser][1]:
protected static function _get_browser(){
// Load cache
@include_once(plugin_dir_path( __FILE__ ).'databases/browscap.php');
// browscap.php contains $slimstat_patterns and $slimstat_browsers
$browser = array('browser' => 'Default Browser', 'version' => '1', 'platform' => 'unknown', 'css_version' => 1, 'type' => 1);
if (empty($slimstat_patterns) || !is_array($slimstat_patterns)) return $browser;
$user_agent = isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:'';
$search = array();
foreach ($slimstat_patterns as $key => $pattern){
if (preg_match($pattern . 'i', $user_agent)){
$search = $value = $search + $slimstat_browsers[$key];
while (array_key_exists(3, $value) && $value[3]) {
$value = $slimstat_browsers[$value[3]];
$search += $value;
}
break;
}
}
// Lots of other lines to relevant to the profiling results
}
這個類似於 PHP get_browser 的功能檢測瀏覽器的功能和作業系統。大多數指令碼執行時間都花在這個 foreach 迴圈中,評估所有這些 preg_match(約 8000 – 10000 頁每頁請求) 。這在 Linux 上大約需要 90ms,Windows 上需要 3000ms 。所有測試設定的結果相同 (圖片顯示兩次執行的資料):
當然,載入兩個巨大的陣列需要一些時間。評估正規表示式。但是我們期望他們在 Linux 和 Windows 上大致相同的時間。這是一個 linux vm 的分析結果 (僅一頁請求) 。差異很明顯:
另一個時間殺手實際上是 Object-Cache WordPress 使用的:
function get( $key, $group = 'default', $force = false, &$found = null ) {
if ( empty( $group ) )
$group = 'default';
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) )
$key = $this->blog_prefix . $key;
if ( $this->_exists( $key, $group ) ) {
$found = true;
$this->cache_hits += 1;
if ( is_object($this->cache[$group][$key]) )
return clone $this->cache[$group][$key];
else
return $this->cache[$group][$key];
}
$found = false;
$this->cache_misses += 1;
return false;
}
時間花在這個函式本身 (3 個指令碼執行) 中:
在 linux 上
最後一個真正的大時代殺手是翻譯。每個翻譯,從記憶體載入,在 WordPress 中從 0.2ms 到 4ms 的任何東西:
在 linux 上
2) 測試系統
為了確保虛擬化或 Apache 確實影響到這一點,我們在幾個設定中進行了測試。所有設定都停用了 Antivir:
-
Linux Debian,Apache 2& PHP 最新穩定版本。對於在虛擬機器中執行的開發人員,對於分段/即時伺服器,這是一樣的。作為所需效能的參考系。無論是在我們的辦公室還是在一些主機提供 (共享空間) 。 Windows 系統具有 4GB 和 8GB 的 RAM 之間,所有的記憶體使用率都在 50%以下。虛擬化永遠不會執行 Windows& Apache 在同一時間。
-
在 T-Systems(託管虛擬化伺服器) 上執行的 Life-Servers,在 VMWare Player
-
Win 2008 R2 。 Apache 2.2.25 + PHP 5.4.26 NTS,VC9 作為 fastcgi 模組
-
Win 2008 R2 。 Apache 2.2.25 + PHP 5.5.1 NTS,VC11 作為 fastcgi 模組
-
Win 2008 R2 。 Apache 2.2.25 + PHP 5.5.1 NTS,VC11 作為 apache 模組
-
Win 2008 R2,Apache 2.2.25 + PHP 5.5.11 TS,VC11 作為 apache 模組 (這是我在更新 2 中提到的快速)
-
-
在本地機器上,Host:OpenSuse,Virtualization:VMWare 播放器,與 @ T-Systems 相同。為了避免基礎設施影響我們:
-
Win 2008 R2 。 Apache 2.2.25 + PHP 5.4.26 NTS,VC9 作為 fastcgi 模組
-
Win 2008 R2 。 IIS7 + PHP 5.4.26 NTS,VC9 作為 fastcgi 模組 (帶和不帶 wincache)
-
Win 2012. IIS * + PHP 5.5.10 NTS,VC11 作為 fastcgi 模組 (帶和不帶 wincache)
-
-
在沒有虛擬化的本地機器上
-
Win 2008 R2 。 Apache 2.2.25 + PHP 5.4.26 NTS,VC9 作為 fastcgi 模組
-
如上所述的分析結果在不同的系統上是相同的 (〜 10%的推導) 。 Windows 總是比 Linux 慢的一個重要因素。
使用全新安裝的 WordPress& Slimstats 導致約相同的結果。重寫程式碼在這裡不是一個選項。
更新:同時我們發現了另外兩個 Windows 系統 (Windows 2008 R2,VM& Phys),這個完整的堆疊執行得相當快。相同的配置。
更新 2:在 Life-Servers 上執行 PHP 作為 apache 模組的速度比 fastcgi 方法稍快一些:下降到〜 2 秒,減少 50%。
記憶體不足
一段時間後,我們的 Live-Server 停止工作,觸發這些記憶體不足的例外:
PHP Fatal error: Out of memory (allocated 4456448) (tried to allocate 136 bytes)
PHP Fatal error: Out of memory (allocated 8650752) (tried to allocate 45 bytes)
PHP Fatal error: Out of memory (allocated 6815744) (tried to allocate 24 bytes)
這發生在隨機的指令碼位置。顯然,Zend 記憶體管理器不能分配更多的記憶體,儘管這些指令碼將被允許。當時如果事件發生,伺服器有大約 50%的可用 RAM(2GB +) 。所以伺服器實際上沒有用完 ram 。現在重新啟動 Apache /PHP 解決了這個問題。
不知道這個問題是否與這裡的效能問題有關。然而,由於這兩個問題似乎都是 memory 相關的,它包括在這裡。特別是我們將嘗試重現提供體面表現的 Windows-Tests 的設定。
3)Apache& PHP 配置
可能沒有任何常見的陷阱。 Output-Buffering 啟用 (預設),禁止多重映象覆蓋,… 如果有任何選項感興趣,我們將樂意提供它們。
httpd.exe -V 的輸出
Server version: Apache/2.4.7 (Win32)
Apache Lounge VC10 Server built: Nov 26 2013 15:46:56
Server's Module Magic Number: 20120211:27
Server loaded: APR 1.5.0, APR-UTIL 1.5.3
Compiled using: APR 1.5.0, APR-UTIL 1.5.3
Architecture: 32-bit
Server MPM: WinNT
threaded: yes (fixed thread count)
forked: no
Server compiled with....
-D APR_HAS_SENDFILE
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses disabled)
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D DYNAMIC_MODULE_LIMIT=256
-D HTTPD_ROOT="/apache"
-D SUEXEC_BIN="/apache/bin/suexec"
-D DEFAULT_PIDLOG="logs/httpd.pid"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_ERRORLOG="logs/error.log"
-D AP_TYPES_CONFIG_FILE="conf/mime.types"
-D SERVER_CONFIG_FILE="conf/httpd.conf"
mpm_winnt_module 配置:
<IfModule mpm_winnt_module>
ThreadsPerChild 150
ThreadStackSize 8388608
MaxConnectionsPerChild 0
</IfModule>
摘錄 php.ini:
realpath_cache_size = 12M
pcre.recursion_limit = 100000
4) 目前懷疑的原因
老假設:
All three examples heavily rely on big arrays and string operations. That some kind seems to be the common factory. As the implementation works ok’ish on Linux, we suspect this to be a memory problem on Windows. Given there is no database interaction at the pin-pointed locations, we don’t suspect the database or Server <-> PHP integration to be the problem. Somehow PHP’s memory interaction just seems to be slow. Maybe there is someone interfering with the memory on Windows making access dramatically slower?
舊假設 2:
As the same stack runs fine on other Windows machines we assume the problem to be somewhere in the Windows configuration.
新假設 3:
Actually I am out of assumptions. Why would run PHP that much slower as fastcgi then as apache module>
有關如何驗證這一點或在這裡找到真正問題的任何想法?任何幫助或解決這個問題的方向是非常受歡迎的。
最佳解決方法
Windows 在各種情況下都有許多限制,防止,保護,控制和使用計算機的服務/策略。
一個好的微軟認證專家將能夠在幾分鐘內解決您的問題,因為他們將有經驗來確定哪些設定/服務/策略來檢查和停用/啟用/更改設定,以便 PHP 指令碼執行得更快。
在我的 memory 中,我只能建議你檢查處理 RAM,硬碟驅動器訪問,環境變數,限制和安全 (如防火牆) 的所有內容。一切可能影響 php 指令碼的執行,從一些遠端過程呼叫策略開始,並以操作堆疊記憶體結束。
邏輯是 php.exe 呼叫一些外部的.dll 檔案來執行一些操作,可能會對作業系統完成的方式進行檢查,這樣可以透過這樣的.dll 緩慢傳送請求,並收到來自它的響應。如果.dll 使用硬碟來訪問某些東西 – 硬碟訪問策略進入現場。而且,RAM 中的所有內容如何位於 RAM 或 hard-drive 快取中。應用策略執行緒策略適用於應用程式的最大百分比限制。
我不是說 Windows-based 主機是壞的,只是為了一般的管理員設定正確很難。如果您擁有微軟專家,他可以將您的伺服器調整為與 Linux-based 伺服器一樣快。
次佳解決方法
-
啟用 APC,當使用 PHP5.4
-
如果您沒有注意到速度增益,當 APC 開啟時,某些配置錯誤
[APC]
extension=php_apc.dll
apc.enabled=1
apc.shm_segments=1
apc.shm_size=128M
apc.num_files_hint=7000
apc.user_entries_hint=4096
apc.ttl=7200
apc.user_ttl=7200
-
-
在 PHP 5.5
[Zend]上啟用 Zend Opcode
zend_extension=ext/php_zend.dll
zend_optimizerplus.enable=1
zend_optimizerplus.use_cwd=1
zend_optimizerplus.validate_timestamp=0
zend_optimizerplus.revalidate_freq=2
zend_optimizerplus.revalidate_path=0
zend_optimizerplus.dups_fix=0
zend_optimizerplus.log_verbosity_level=1
zend_optimizerplus.memory_consumption=128
zend_optimizerplus.interned_strings_buffer=16
zend_optimizerplus.max_accelerated_files=2000
zend_optimizerplus.max_wasted_percentage=25
zend_optimizerplus.consistency_checks=0
zend_optimizerplus.force_restart_timeout=60
zend_optimizerplus.blacklist_filename=
zend_optimizerplus.fast_shutdown=0
zend_optimizerplus.optimization_level=0xfffffbbf
zend_optimizerplus.enable_slow_optimizations=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1 -
停用 Wordpress 擴充套件 step-wise,找到記憶體使用怪物
-
設定 Wordpress:
define('WP_MEMORY_LIMIT', '128M');,除非您使用影像轉換外掛應該足夠了 -
在 php.ini
ini_set('memory_limit', -1);中設定無限記憶體 -
配置檔案沒有執行 Xdebug,這聽起來很瘋狂,但偵錯程式本身有很大的影響
-
使用
memory_get_usage並在整個系統上傳播呼叫來查詢程式碼位置,其中記憶體洩漏 -
給
zend.enable_gc=1一個嘗試,指令碼會更慢,但使用更少的記憶體 -
也許只是停用在 SlimStats 設定中檢查使用者瀏覽器。
-
如果這是不可能的,嘗試覆蓋 SlimStats getBrowser() 功能,使用 faster getBrowser() substitute
-
為了比較 user-agent 取樣器的速度,參見 https://github.com/quentin389/ua-speed-tests
第三種解決方法
我在 Github 上看了一下這個外掛:
https://github.com/wp-plugins/wp-slimstat
並且包含的違規檔案是在某種程度上已經被細化的檔案,真的是資料 (而不是程式碼),有 5 個變體,每個檔案大約是 400KB
還有 maxMind.dat 檔案是 400KB,雖然我不知道它是否同時使用。
您正在使用舊版本的外掛,3.2.3 版本,並且有一個更新的可能會解決您的問題。
比較差異是很困難的,因為作者或誰沒有保持 git 歷史的順序,所以我不得不手動區分檔案。大多數與_get_browser 相關的更改似乎都在新增快取。
可能的載入該檔案解析緩慢,但我希望 PHP 在兩個平臺上以相同的速率載入這兩個檔案,這些 IO 快取正在工作。
編輯看起來有點接近,可能無法解決你的問題。那些檔案基本上是大型正規表示式查詢表。你的 Linux 系統上有一個 APC 快取嗎? APC 快取可能會使 PHP 檔案快取 (儘管不是編譯的 regex 模式)
參考文獻
注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。




