
قبل توسيع نطاق البنية التحتية الخاصة بك وتوسيع نطاقها ، فإن الخطوة الأولى هي التأكد من استخدام الموارد بشكل صحيح وأن تكوين التطبيق لا يعيق أدائه. الهدف الرئيسي للفريق الهندسي هو ضمان التشغيل المستمر دون انقطاع لأي نظام مصمم ومُنشَر بأقل قدر من الموارد.
واجهنا المشكلة المذكورة أعلاه حيث كان يتم استخدام نظامنا المنشور يوميًا من قبل مليون مستخدم متصلين في دفعات من وقت لآخر. هذا يعني أن نشر خوادم متعددة أو توسيع نطاقها لن يكون الحل الأفضل في هذه الحالة.
تتناول هذه المقالة ضبط Nginx لتحسين الأداء ، أي لزيادة RPS (الطلبات في الثانية) في HTTP API. حاولت إخبارك عن التحسين الذي طبقناه في النظام المنشور لمعالجة عشرات الآلاف من الطلبات في الثانية دون إهدار قدر هائل من الموارد.
خطة العمل: تحتاج إلى تشغيل واجهة برمجة تطبيقات HTTP (مكتوبة بلغة Python باستخدام القارورة) ، بالوكيل مع Nginx ؛ النطاق الترددي العالي مطلوب. سيتغير محتوى API على فترات زمنية ليوم واحد.
عملية
الاسم الأمثل
لتحقيق أفضل نتيجة ؛ الاستخدام الأكثر كفاءة للموقف أو المورد.
استخدمنا المشرف لبدء خادم WSGI بالتكوينات التالية:
- Gunicorn مع عمال Meinhold
- عدد العمال: عدد وحدات المعالجة المركزية * 2 + 1
- اربط المقبس بعنوان Unix بدلاً من IP ، سيؤدي ذلك إلى زيادة السرعة قليلاً .
يبدو أمر المشرف كما يلي:
gunicorn api:app --workers=5 --worker-
class=meinheld.gmeinheld.MeinheldWorker --bind=unix:api.sock
لقد حاولنا تحسين تكوين Nginx وفحصنا ما هو الأفضل بالنسبة لنا.
لتقييم أداء API ، استخدمنا wrk بالأمر التالي:
wrk -t20 -c200 -d20s http://api.endpoint/resource
التكوين الافتراضي
أجرينا أولاً اختبار تحميل لواجهة برمجة التطبيقات دون أي تغييرات وحصلنا على الإحصاءات التالية:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 192.48ms 274.78ms 1.97s 87.18%
Req/Sec 85.57 29.20 202.00 72.83%
33329 requests in 20.03s, 29.59MB read
Socket errors: connect 0, read 0, write 0, timeout 85
Requests/sec: 1663.71
Transfer/sec: 1.48MB
تحديث التكوين الافتراضي
لنقم بتحديث إعداد Nginx الافتراضي ، أي nginx.conf في /etc/nginx/nginx.conf
worker_processes auto;
#or should be equal to the CPU core, you can use `grep processor /proc/cpuinfo | wc -l` to find; auto does it implicitly.
worker_connections 1024;
# default is 768; find optimum value for your server by `ulimit -n`
access_log off;
# to boost I/O on HDD we can disable access logs
# this prevent nginx from logging every action in a log file named `access.log`.
keepalive_timeout 15;
# default is 65;
# server will close connection after this time (in seconds)
gzip_vary on;
gzip_proxied any;
gzip_comp_level 2;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
# reduces the data that needs to be sent over the networknginx.conf (/etc/nginx/nginx.conf)
بعد التغييرات ، نقوم بتشغيل فحص التكوين:
sudo nginx -t
إذا نجح الفحص ، يمكنك إعادة تشغيل Nginx ليعكس التغييرات:
sudo service nginx restart
باستخدام هذا التكوين ، أجرينا اختبار الحمل لواجهة برمجة التطبيقات وحصلنا على النتيجة التالية:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 145.80ms 237.97ms 1.95s 89.51%
Req/Sec 107.99 41.34 202.00 66.09%
42898 requests in 20.03s, 39.03MB read
Socket errors: connect 0, read 0, write 0, timeout 46
Non-2xx or 3xx responses: 2
Requests/sec: 2141.48
Transfer/sec: 1.95MB
أدت هذه التكوينات إلى تقليل المهلات وزيادة RPS (الطلبات في الثانية) ، ولكن ليس كثيرًا.
مضيفا Nginx Cache
نظرًا لأنه في حالتنا ، سيتم تحديث محتوى نقطة النهاية في فاصل زمني ليوم واحد ، فإن هذا يخلق بيئة مناسبة للتخزين المؤقت لاستجابات API.
لكن إضافة ذاكرة التخزين المؤقت تجعلها غير صالحة ... هذه واحدة من اثنتين من الصعوبات هنا.
في علوم الكمبيوتر ، هناك تعقيدان فقط: إبطال ذاكرة التخزين المؤقت وتسمية الأشياء. - فيل كارلتون
نختار حلاً بسيطًا لمسح دليل ذاكرة التخزين المؤقت باستخدام cronjob بعد تحديث المحتوى على نظام المصب.
بعد ذلك ، سيقوم Nginx بكل العمل الشاق ، لكننا الآن نحتاج إلى التأكد من أن Nginx جاهز بنسبة 100٪!
لإضافة التخزين المؤقت إلى Nginx ، تحتاج إلى إضافة عدة توجيهات إلى ملف ضبط Nginx.
قبل ذلك ، نحتاج إلى إنشاء دليل لتخزين بيانات ذاكرة التخزين المؤقت:
sudo mkdir -p /data/nginx/cache
تغييرات تكوين Nginx:
proxy_cache_path /data/nginx/cache keys_zone=my_zone:10m inactive=1d;
server {
...
location /api-endpoint/ {
proxy_cache my_zone;
proxy_cache_key "$host$request_uri$http_authorization";
proxy_cache_valid 404 302 1m;
proxy_cache_valid 200 1d;
add_header X-Cache-Status $upstream_cache_status;
}
...
}
التخزين المؤقت للطلبات الوكيل (تكوين Nginx)
بعد تغيير التكوين هذا ، قمنا بتحميل API واختبرنا وحصلنا على النتيجة التالية:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 6.88ms 5.44ms 88.91ms 81.36%
Req/Sec 1.59k 500.04 2.95k 62.50%
634405 requests in 20.06s, 589.86MB read
Requests/sec: 31624.93
Transfer/sec: 29.40MB
وبالتالي ، حصلنا على زيادة في الأداء بمقدار 19 ضعفًا تقريبًا عن طريق إضافة التخزين المؤقت.
ملاحظة من خبير Timeweb : من
المهم أن تتذكر أن استعلامات التخزين المؤقت التي تكتب إلى قاعدة البيانات ستؤدي إلى استجابة مخزنة مؤقتًا ، ولكن لا يكتب إلى قاعدة البيانات.
ذاكرة التخزين المؤقت Nginx في ذاكرة الوصول العشوائي (ذاكرة الوصول العشوائي)
لنأخذ خطوة أخرى إلى الأمام! حاليًا ، يتم تخزين بيانات ذاكرة التخزين المؤقت الخاصة بنا على القرص. ماذا لو حفظنا هذه البيانات في ذاكرة الوصول العشوائي؟ في حالتنا ، بيانات الاستجابة محدودة وليست كبيرة.
لذلك ، تحتاج أولاً إلى إنشاء دليل حيث سيتم تثبيت ذاكرة التخزين المؤقت RAM:
sudo mkdir -p /data/nginx/ramcache
لتحميل الدليل الذي تم إنشاؤه في ذاكرة الوصول العشوائي باستخدام tmpfs ، استخدم الأمر:
sudo mount -t tmpfs -o size=256M tmpfs /data/nginx/ramcache
هذا يتصاعد / data / nginx / ramcache في ذاكرة الوصول العشوائي ، ويخصص 256 ميجا بايت.
إذا كنت تعتقد أنك تريد تعطيل ذاكرة التخزين المؤقت لذاكرة الوصول العشوائي ، فما عليك سوى تشغيل الأمر:
sudo umount /data/nginx/ramcache
لإعادة إنشاء دليل ذاكرة التخزين المؤقت في ذاكرة الوصول العشوائي تلقائيًا بعد إعادة التشغيل ، نحتاج إلى تحديث ملف / etc / fstab . أضف السطر التالي إليه:
tmpfs /data/nginx/ramcache tmpfs defaults,size=256M 0 0
ملاحظة: يتعين علينا أيضًا تسجيل قيمة proxy_cache_path بالمسار إلى ramcache ( / data / nginx / ramcache ).
بعد تحديث التكوين ، أجرينا اختبار تحميل API مرة أخرى وتلقينا النتيجة التالية:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 5.57ms 5.69ms 277.76ms 92.94%
Req/Sec 1.98k 403.94 4.55k 71.77%
789306 requests in 20.04s, 733.89MB read
Requests/sec: 39387.13
Transfer/sec: 36.62MB
أدى تخزين ذاكرة التخزين المؤقت في ذاكرة الوصول العشوائي إلى تحسن كبير بنحو 23 مرة .
سجل الوصول المخزن
نحتفظ بسجل للوصول إلى التطبيقات التي تم إنشاء وكيل لها ، ولكن يمكنك أولاً حفظ السجل في مخزن مؤقت ثم كتابته على القرص فقط:
- إذا كان السطر التالي من السجل لا يتناسب مع المخزن المؤقت
- إذا كانت البيانات الموجودة في المخزن المؤقت أقدم من المحدد في معلمة التدفق .
سيؤدي هذا الإجراء إلى تقليل معدل التسجيل الذي يتم إجراؤه مع كل طلب. للقيام بذلك، نحن بحاجة فقط لإضافة عازلة و تدفق المعلمات مع القيمة المناسبة في access_log التوجيه :
location / {
...
access_log /var/log/nginx/fast_api.log combined buffer=256k flush=10s;
error_log /var/log/nginx/fast_api.err.log;
}
سجل المخزن المؤقت قبل
كتابته على القرص وهكذا ، وفقًا للتكوين أعلاه ، سيتم تخزين سجلات الوصول مؤقتًا وحفظها على القرص فقط عندما يصل المخزن المؤقت إلى 256 كيلو بايت أو إذا كانت البيانات المخزنة أقدم من 10 ثوانٍ.
ملاحظة: الاسم log_format مدمج هنا .
بعد اختبار الإجهاد المتكرر ، حصلنا على النتيجة التالية:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.21ms 3.19ms 84.83ms 83.84%
Req/Sec 2.53k 379.87 6.02k 77.05%
1009771 requests in 20.03s, 849.31MB read
Requests/sec: 50413.44
Transfer/sec: 42.40MB
أدى هذا التكوين إلى زيادة كبيرة في عدد الطلبات في الثانية ، حوالي 30 مرة مقارنة بالمرحلة الأولية.
انتاج |
ناقشنا في هذه المقالة عملية تحسين تكوين Nginx لتحسين أداء RPS. تمت زيادة RPS من 1663 إلى ~ 50413 ( زيادة بنحو 30 مرة ) ، مما يوفر إنتاجية عالية. من خلال ضبط الإعدادات الافتراضية ، يمكنك تحسين أداء النظام.
لننهي المقال باقتباس:
اجعلها تعمل أولاً. ثم افعلها بشكل صحيح. ثم قم بالتحسين. - كينت بيك