WordPress 效能最佳化:Apache Prefork 與 Event MPM 實測比較
前言
最近在接手一個舊的 WordPress 專案後,發現網站在使用上有些效能瓶頸。由於原本的專案是使用 Apache 作為 Web Server,為了確保最高的相容性,決定延續使用 Apache 架構,調整設定達到最大的效能
這篇文章記錄了過程中透過官方提供的 Docker 映像檔,測試 Apache 的 Prefork 與 Event 模式在 WordPress 上的表現,了解如何選擇適合的 Apache 設定
什麼是 Apache MPM?
Apache MPM 決定了 Web Server 如何處理多個使用者的連線請求,以下是兩種主要模式的差異:
Prefork MPM
- 每個請求會產生一個獨立的 Apache 子行程(process)處理
- 設定簡單
- 穩定性與相容性高,但記憶體消耗較大、擴展性較差
適合:較小流量的 WordPress 網站或舊專案相容性需求高的情況
Event MPM
- 採用非同步事件驅動(event-driven)模式處理請求
- 使用少量執行緒同時處理多個連線
- 效能高、記憶體使用效率佳,需搭配 PHP-FPM 使用
- 設定較複雜,需拆分 Apache 與 PHP 處理邏輯
適合:中大型網站或追求高效能的 WordPress 部署
如何在 Docker 中切換與使用?
1. 使用 Prefork 的方式
Docker 官方映像:wordpress:6-php8.4
這個映像檔內建 Apache + PHP Module,預設使用 Prefork 模式。
1 | docker-compose.yml: |
設定上較簡單,只要用這個映像檔即可開始使用。
2. 使用 Event 的方式
Docker 官方映像需要調整為:
wordpress:6-php8.4-fpm
(PHP-FPM)httpd:2.4
(Apache Event)
需自訂 Apache 設定檔,啟用 mpm_event_module
並設定 proxy:fcgi://
將 PHP 請求轉送給 PHP-FPM。
1 | docker-compose.yml: |
這樣的架構效能高,但需要較多設定。
Prefork vs Event 效能比較(實測)
- Apple Macbook M1 Pro
- 透過 k6 壓力測試工具模擬 20 位使用者同時瀏覽網站,測試時間為 30 秒。
項目 | Prefork | Event |
---|---|---|
總請求數 | 1676 | 3241 |
平均回應時間 | 358.94 毫秒 | 185.7 毫秒 |
每秒請求數 | 55.59 req/s | 107.36 req/s |
結果:Event MPM 效能是 Prefork 的兩倍,回應速度也快一倍以上
小結
- Prefork 適合穩定、安全、設定簡單的情況
- Event MPM 搭配 PHP-FPM 適合追求效能情況
- Docker 官方映像檔都有提供支援,選擇哪一種主要看對於專案需求與技術熟悉程度
Reference
- https://inbound.technology/%E5%A6%82%E4%BD%95%E5%A2%9E%E5%8A%A0-apache-web-server-%E5%8F%AF%E4%BB%A5%E6%89%BF%E5%8F%97%E7%9A%84%E8%AB%8B%E6%B1%82%E9%87%8F%EF%BC%9F/
- https://hub.docker.com/_/wordpress
- https://docs.bitnami.com/aws/infrastructure/lapp/administration/understand-event-mpm/
- https://www.datadoghq.com/blog/monitoring-apache-web-server-performance/#event-mpm
補充
- 完整 docker-compose.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150services:
# Apache prefork MPM with PHP module
wordpress-prefork:
image: wordpress:6-php8.4
cpus: 2
container_name: wordpress-prefork
environment:
WORDPRESS_DB_HOST: db-prefork
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
- wp_prefork_data:/var/www/html
ports:
- "8080:80"
depends_on:
- db-prefork
# Database for prefork setup
db-prefork:
image: mysql:8.0
container_name: mysql-prefork
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
volumes:
- db_prefork_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
cap_add:
- SYS_NICE
# Apache event MPM with PHP-FPM
wordpress-event:
image: wordpress:6-php8.4-fpm
cpus: 2
container_name: wordpress-fpm
environment:
WORDPRESS_DB_HOST: db-event
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
- wp_event_data:/var/www/html
depends_on:
- db-event
# Database for event setup
db-event:
image: mysql:8.0
container_name: mysql-event
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
volumes:
- db_event_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
cap_add:
- SYS_NICE
# Apache with event MPM for PHP-FPM
apache-event:
image: httpd:2.4
container_name: apache-event
ports:
- "8081:80"
volumes:
- wp_event_data:/var/www/html
depends_on:
- wordpress-event
command: >
bash -c "cat > /usr/local/apache2/conf/httpd.conf << 'EOL'
ServerRoot /usr/local/apache2
Listen 80
# 加載基本模塊
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so
LoadModule rewrite_module modules/mod_rewrite.so
# 服務器配置
ServerAdmin admin@localhost
ServerName localhost
User www-data
Group www-data
# 日誌配置
LogLevel warn
ErrorLog /proc/self/fd/2
CustomLog /proc/self/fd/1 combined
# 文檔根目錄設置
DocumentRoot \"/var/www/html\"
<Directory \"/var/www/html\">
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
DirectoryIndex index.php
</Directory>
# PHP-FPM 代理設置
<FilesMatch \\.php$>
SetHandler \"proxy:fcgi://wordpress-event:9000\"
</FilesMatch>
# MIME 類型
TypesConfig conf/mime.types
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
# 基本安全設置
ServerTokens Prod
ServerSignature Off
EOL
echo 'Checking loaded modules:'
httpd -M | grep mpm
echo 'Starting Apache with event MPM...'
httpd-foreground"
volumes:
wp_prefork_data:
wp_event_data:
db_prefork_data:
db_event_data: