使用步驟說明
原本使用 PHP 來做檔案下載控制,包括限制下載速率,同一IP的連線數目。可是,CPU 使用率會飆高,記憶體的使用率也不低。試過 Nginx,Apache 的 event 模式,結果都差不多。後來,看到可以設定 X-SendFile header,交由 Apache 控制下載,今天花了一天的時間,終於搞定。
需要安裝的模組,包括
- mod_limitipconn: 限制個別IP的連線數
- mod_ratelimit: 限制下載的速度,如 150 為 150KB/s
- mod_xsendfile: X-Sendfile 模組
<Directory "/var/www/html/vod_dl">
<Files *.mp4>
ForceType application/octet-stream
Header set Content-Disposition attachment
</Files>
<IfModule mod_limitipconn.c>
# limit concurrent connection for 2
MaxConnPerIP 2
</IfModule>
<IfModule mod_ratelimit.c>
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 850
</IfModule>
</Directory>
<Files *.mp4>
ForceType application/octet-stream
Header set Content-Disposition attachment
</Files>
<IfModule mod_limitipconn.c>
# limit concurrent connection for 2
MaxConnPerIP 2
</IfModule>
<IfModule mod_ratelimit.c>
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 850
</IfModule>
</Directory>
有可能需要設定 XSendFilePath,例如 "XSendFilePath /home/video_data",不然會出現 "The given path was above the root path .... " 的錯誤。
因為我的下載程式的 url 是長的這樣,
http://10.161.81.118/dl_video.php?fn=105S101.mp4
設定時,需針對 dl_video.php 做設定,因此下面的設定也會套用到像 dl_videx_xxx.php 的程式。
<Files dl_video*>
<IfModule mod_xsendfile.c>
# 啟用 xsendfile
XSendFile On
SetEnv MOD_X_SENDFILE_ENABLED 1
</IfModule>
<IfModule mod_limitipconn.c>
# 限制每一IP同時連線的數目
MaxConnPerIP 8
</IfModule>
<IfModule mod_ratelimit.c>
# 限制下載速率 850KB/s
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 850
</IfModule>
</Files>
PHP 的程式長得像下面這樣,只要設定 header,就會轉由 Apache 控制下載。
// 權限檢查 .....
// 檔案檢查 .....
// .....
header("X-Sendfile: {$fullname}");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($fname) . '"');
注意,$fullname 是檔案系統中的實際目錄名稱,例如
/var/www/html/mp4_files/105S101.mp4這樣子,就算開到 1千多個檔,CPU 的使用率不高,記憶體大概也只在 500MB左右。
Nginx 也有類似功能,不過它是接受URI,正如一般的 request,而非目錄檔名,我個人覺得比較不盡理想。
使用 docker
原先的環境是使用 CentOS,但其已於數年前被宣告死亡,只能再找其他途徑。使用 docker.hub 的 PHP 官方 image -- php:7.4.33-apache-bullseye,bullseye 為 Debian 當前的舊的穩定(oldstable)版。
Debian 官方沒有 mod_limitipconn,必須自己下載編譯安裝。變通的方式,是在 docker 中編譯好,把用到的檔案 copy 出來。
apt install apache2-dev wget
wget https://dominia.org/djao/limit/mod_limitipconn-0.24.tar.bz2
tar -xvf mod_limitipconn-0.24.tar.bz2
cd mod_limitipconn-0.24
make
make install
--
## 需要的檔案是下面兩個
/usr/lib/apache2/modules/mod_limitipconn.so
/etc/apache2/mods-available/limitipconn.load
Dockerfile 如下。
# Debian 當前的舊的穩定(oldstable)版
FROM php:7.4.33-apache-bullseye
# Install required extensions
# libicu-dev -- required by php ext: intl
# libpq-dev -- required by php ext: pgsql pdo_pgsql
# libonig-dev -- required by php ext: mbstring
RUN apt-get update && \
apt-get -y install libapache2-mod-xsendfile \
libicu-dev libpq-dev libonig-dev \
&& docker-php-ext-install intl pdo \
mysqli pdo_mysql pgsql pdo_pgsql \
mbstring
COPY conf/ocw_vod_dl.conf /etc/apache2/conf-enabled
COPY conf/mod_limitipconn.so /usr/lib/apache2/modules
COPY conf/limitipconn.load /etc/apache2/mods-available
RUN a2enmod headers limitipconn ratelimit xsendfile
EXPOSE 80
CMD ["apache2-foreground"]
docker-compose_tst.yml 如下。
# version: '2'
services:
ocw-dl:
container_name: ocw-dl-server
build: .
domainname: ocw.aca
hostname: ocw-dl-server
working_dir: /var/www/html
volumes:
- ../web:/var/www/html
- /ext_hd8t/ajax/wk_backup/ocw_bak/vod:/var/www/html/vod_dl
ports:
- 8380:80
以上為 2024-01-23 更新,陽明山下雪之日。
沒有留言:
張貼留言