التحسين: تكوين خادم الويب Nginx لتحسين أداء RPS في HTTP API



قبل توسيع نطاق البنية التحتية الخاصة بك وتوسيع نطاقها ، فإن الخطوة الأولى هي التأكد من استخدام الموارد بشكل صحيح وأن تكوين التطبيق لا يعيق أدائه. الهدف الرئيسي للفريق الهندسي هو ضمان التشغيل المستمر دون انقطاع لأي نظام مصمم ومُنشَر بأقل قدر من الموارد.



واجهنا المشكلة المذكورة أعلاه حيث كان يتم استخدام نظامنا المنشور يوميًا من قبل مليون مستخدم متصلين في دفعات من وقت لآخر. هذا يعني أن نشر خوادم متعددة أو توسيع نطاقها لن يكون الحل الأفضل في هذه الحالة.



تتناول هذه المقالة ضبط 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 network
nginx.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 مرة ) ، مما يوفر إنتاجية عالية. من خلال ضبط الإعدادات الافتراضية ، يمكنك تحسين أداء النظام.



لننهي المقال باقتباس:

اجعلها تعمل أولاً. ثم افعلها بشكل صحيح. ثم قم بالتحسين. - كينت بيك

المصادر






All Articles