在更早以前我們怎麼實現縮圖機制,當使用者上傳一張檔案,後端會固定將圖片縮圖成各種前端網頁需要的大小,不管前端頁面是否有使用,後端都會先產生好,這有什麼缺陷?
- 佔用硬碟空間大小
- 前端又需要另外一種格式的縮圖?
第二個問題比較麻煩,當前端需要另一種縮圖格式,後端就要開始掃描系統的全部圖片,再重新產生一次。非常耗費後端系統效能。後來才改成透過 URL 定義長寬來決定即時縮圖,在 Go 語言內可以選擇使用 picfit 來當作後端即時的縮圖機。本篇則是要提供另一種解法,就是使用 Nginx 搭配 image_filter 外掛來達成即時縮圖機制。
使用 image_filter
來看看縮圖網址
1
| http://foobar.org/image_width/bucket_name/image_name
|
- image_width: 圖片 width
- bucket_name: 圖片目錄或 AWS S3 bucket
- image_name: 圖片檔名
其中 bucket 可以是 AWS S3。底下是 Nginx 的簡單設定:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| server {
listen 80 default_server;
listen [::]:80 default_server;
server_name ${NGINX_HOST};
location ~ ^/([0-9]+)/(.*)$ {
set $width $1;
set $path $2;
rewrite ^ /$path break;
proxy_pass ${IMAGE_HOST};
image_filter resize $width -;
image_filter_buffer 100M;
image_filter_jpeg_quality ${JPG_QUALITY};
expires ${EXPIRE_TIME};
}
}
|
我們可以設定 expires 來讓使用者存在瀏覽器端,這樣下次瀏覽網頁的時候都可以使用快取機制。可以看到 IMAGE_HOST
可以是 AWS S3 URL。
- 先從
IMAGE_HOST
下載圖片 - Nginx 執行縮圖
- 儲存圖片在使用者 browser 端
到這邊會有個問題,假設有一萬個使用者在不同的地方同時連線,Nginx 就需要處理 1 萬次,可以直接用 vegeta 來 Benchmark 試試看
1
2
3
4
5
6
7
8
9
| $ echo "GET http://localhost:8002/310/test/26946324088_5b3f0b1464_o.png" | vegeta attack -rate=100 -connections=1 -duration=1s | tee results.bin | vegeta report
Requests [total, rate] 100, 101.01
Duration [total, attack, wait] 8.258454731s, 989.999ms, 7.268455731s
Latencies [mean, 50, 95, 99, max] 3.937031678s, 4.079690985s, 6.958110121s, 7.205018428s, 7.268455731s
Bytes In [total, mean] 4455500, 44555.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:100
Error Set:
|
上面數據顯示每秒打 100 次連線,ngix 需要花費 8 秒多才能執行結束。而延遲時間也高達 3 秒多。
加入 proxy cache 機制
透過 proxy cache 機制可以讓 nginx 只產生一次縮圖,並且放到 cache 目錄內可以減少短時間的不同連線。但是 image_filter 無法跟 proxy cache 同時處理,所以必須要拆成兩個 host 才可以達到此目的,如果沒有透過 proxy cache,你也可以用 cloudflare CDN 來達成此目的。請參考線上設定
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
| proxy_cache_path /data keys_zone=cache_zone:10m;
server {
# Internal image resizing server.
server_name localhost;
listen 8888;
# Clean up the headers going to and from S3.
proxy_hide_header "x-amz-id-2";
proxy_hide_header "x-amz-request-id";
proxy_hide_header "x-amz-storage-class";
proxy_hide_header "Set-Cookie";
proxy_ignore_headers "Set-Cookie";
location ~ ^/([0-9]+)/(.*)$ {
set $width $1;
set $path $2;
rewrite ^ /$path break;
proxy_pass ${IMAGE_HOST};
image_filter resize $width -;
image_filter_buffer 100M;
image_filter_jpeg_quality ${JPG_QUALITY};
}
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name ${NGINX_HOST};
location ~ ^/([0-9]+)/(.*)$ {
set $width $1;
set $path $2;
rewrite ^ /$path break;
proxy_pass http://127.0.0.1:8888/$width/$path;
proxy_cache cache_zone;
proxy_cache_key $uri;
proxy_cache_valid 200 302 24h;
proxy_cache_valid 404 1m;
# expire time for browser
expires ${EXPIRE_TIME};
}
}
|
測試數據
這邊使用 minio 來當作 S3 儲存空間,再搭配 Nginx 1.3.9 版本來測試上面設定效能。底下是 docker-compose 一鍵啟動
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
| version: '2'
services:
minio:
image: minio/minio
container_name: minio
ports:
- "9000:9000"
volumes:
- minio-data:/data
environment:
MINIO_ACCESS_KEY: YOUR_MINIO_ACCESS_KEY
MINIO_SECRET_KEY: YOUR_MINIO_SECRET_KEY
command: server /data
image-resizer:
image: appleboy/nginx-image-resizer
container_name: image-resizer
ports:
- "8002:80"
environment:
IMAGE_HOST: http://minio:9000
NGINX_HOST: localhost
volumes:
minio-data:
|
用 docker-compose up 可以將 nginx 及 minio 服務同時啟動,接著打開 http://localhost:9000 上傳圖片,再透過 vegeta 測試數據:
1
2
3
4
5
6
7
8
9
| $ echo "GET http://localhost:8002/310/test/26946324088_5b3f0b1464_o.png" | vegeta attack -rate=100 -connections=1 -duration=1s | tee results.bin | vegeta report
Requests [total, rate] 100, 101.01
Duration [total, attack, wait] 993.312255ms, 989.998ms, 3.314255ms
Latencies [mean, 50, 95, 99, max] 3.717219ms, 3.05486ms, 8.891027ms, 12.488937ms, 12.520428ms
Bytes In [total, mean] 4455500, 44555.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:100
Error Set:
|
執行時間變成 993.312255ms
,Latency 也降到 3.717219ms
,效能提升了很多。透過簡單的 docker 指令就可以在任意機器架設此縮圖機。詳細步驟請參考 nginx-image-resizer
1
2
3
4
| $ docker run -e NGINX_PORT=8081 \
-e NGINX_HOST=localhost \
-e IMAGE_HOST="http://localhost:9000" \
appleboy/nginx-image-resizer
|
附上程式碼請參考 nginx-image-resizer
See also