Brotli وGzip للمتوسط: قلّل حجم الاستجابة 77%
مستوى القارئ: متوسط
هتكسب من المقال ده طريقة عملية تقلّل حجم الاستجابات قبل ما تفكر تزود سيرفرات أو تغيّر الكود الأساسي.
المشكلة باختصار
لو عندك API بيرجع JSON كبير، أو موقع بيبعت ملفات CSS وJavaScript لكل زائر، جزء كبير من زمن التحميل بيضيع في نقل bytes على الشبكة. الافتراض هنا إن عندك خدمة Node.js أو NGINX قدام تطبيق ويب، وحجم الاستجابة النصية بين 100KB و1MB.
سيناريو واقعي: Dashboard داخل شركة SaaS بيعمل 12 طلب API عند الفتح. كل طلب بيرجع JSON متوسطه 820KB. على اتصال 4G متوسط، نقل 9.8MB ممكن يزود وقت أول تحميل من 1.1 ثانية إلى 4.6 ثانية. الضغط مش هيحل query بطيئة، لكنه هيقلّل تكلفة النقل فورًا.
الفكرة بمثال بسيط
ركز في المثال ده: عندك ملف تقرير فيه نفس الكلمات متكررة آلاف المرات. بدل ما تبعت كل كلمة كما هي، الضغط يبعت قاموس صغير يقول: الكلمة دي اتكررت هنا وهنا وهنا. المتصفح يفك القاموس ويرجع الملف زي ما كان.
علميًا، المتصفح يرسل هيدر Accept-Encoding فيه الخوارزميات التي يدعمها، مثل br وgzip. السيرفر يختار واحدة ويرد بـ Content-Encoding. Brotli غالبًا يعطي حجمًا أقل للملفات النصية، خصوصًا CSS وJS وJSON، لكن تكلفة CPU أعلى عند مستويات الضغط العالية.
إعداد NGINX قابل للنسخ
أفضل طريقة تبدأ بها: فعّل Gzip في NGINX لأنه موجود ومدعوم على نطاق واسع. لو عندك module Brotli جاهز في بيئتك، فعّله للملفات النصية فقط.
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml;
brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/javascript application/json image/svg+xml;الـ trade-off هنا واضح: بتكسب نقل بيانات أقل وزمن تحميل أسرع، وبتخسر CPU إضافي على السيرفر. مستوى 5 مناسب كبداية. مستوى 11 في Brotli ممكن يضغط أكثر، لكنه بطيء جدًا للاستجابات الديناميكية.
إعداد Express لو الضغط عند التطبيق
لو NGINX مش موجود، استخدم middleware داخل Express. ده مناسب للتطبيقات الصغيرة، لكنه أقل مثالية لو عندك reverse proxy يقدر يعمل نفس الشغل مرة واحدة لكل الخدمات.
npm install compressionimport express from "express";
import compression from "compression";
const app = express();
app.use(compression({ threshold: 1024 }));
app.get("/api/report", async (req, res) => {
res.json({ rows: await loadReportRows() });
});
app.listen(3000);بدل ما تضغط كل شيء، ابدأ بالاستجابات الأكبر من 1KB. ضغط payload صغير جدًا ممكن يضيف overhead أكبر من المكسب.
إزاي تقيس قبل وبعد
ما تعتمدش على الإحساس. قس حجم النقل وزمن الاستجابة. المثال التالي يطلب Brotli ثم Gzip ويطبع الحجم النهائي الذي وصل للعميل.
URL="https://example.com/api/report"
curl -s -H "Accept-Encoding: br" -o /tmp/br.out -w "brotli: %{size_download} bytes, %{time_total}s\n" "$URL"
curl -s -H "Accept-Encoding: gzip" -o /tmp/gzip.out -w "gzip: %{size_download} bytes, %{time_total}s\n" "$URL"
curl -s -H "Accept-Encoding:" -o /tmp/raw.out -w "raw: %{size_download} bytes, %{time_total}s\n" "$URL"في اختبار داخلي على JSON حجمه 820KB، النتيجة كانت تقريبية: بدون ضغط 820KB، Gzip حوالي 260KB، Brotli حوالي 190KB. يعني Brotli قلّل النقل 77%، وGzip قلّله 68%. لو CPU زاد 6% فقط تحت الحمل، القرار غالبًا يستحق.
ما يجب الانتباه له
- لا تضغط الصور والفيديو. PNG وJPEG وWebP مضغوطين بالفعل غالبًا.
- استخدم
Vary: Accept-Encodingحتى لا يخلط الكاش بين نسخة مضغوطة ونسخة غير مضغوطة. - لا تستخدم مستويات ضغط عالية للاستجابات الديناميكية تحت ضغط كبير.
- راقب CPU وP95 latency بعد التفعيل، مش متوسط زمن الطلب فقط.
اللي بيحصل فعلاً إن بعض الفرق تفعّل Brotli على مستوى 11 لكل API. النتيجة: حجم أقل، لكن P95 يزيد لأن السيرفر بقى يقضي وقتًا أطول في الضغط. أفضل طريقة: مستوى متوسط، وقياس قبل وبعد.
متى لا تستخدم هذه الطريقة
لا تستخدم الضغط لو أغلب الاستجابات أقل من 1KB، أو لو الخدمة CPU-bound بالفعل، أو لو المحتوى صور وفيديو، أو لو عندك CDN يضغط الملفات الثابتة بكفاءة وأنت تكرر نفس الضغط داخل التطبيق. في الحالات دي هتكسب قليل وتخسر تعقيد وCPU.
مصادر
- MDN Content-Encoding: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Encoding
- MDN Accept-Encoding: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding
- NGINX gzip module: https://nginx.org/en/docs/http/ngx_http_gzip_module.html
- Express compression middleware: https://expressjs.com/en/resources/middleware/compression.html
الخطوة التالية
افتح خدمة واحدة عندك، قس حجم أكبر endpoint نصي بـ curl، ثم فعّل Gzip بمستوى 5 لمدة يوم وراقب الحجم وCPU وP95. لو النقل قل أكثر من 50% وCPU زاد أقل من 10%، خلّي الإعداد شغال.