כוונון ביצועי nginx: הגדרת Timeouts לחיבורים כדי להתמודד עם קפיצות תנועה בלי לאבד בקשות

כמה הגדרות timeout ב-nginx קובעות אם השרת שלכם יתמודד בהצלחה עם קפיצת תנועה או יתחיל לאבד בקשות אמיתיות. הנה איך לכוון אותן נכון.

בדיוק קיבלתם קפיצה בתנועה. אולי זה השקת מוצר, פוסט שהפך ויראלי, או קמפיין פרסומי שעבד קצת יותר מדי טוב. ובמקום לחגוג, אתם צופים בלוגים שלכם מתמלאים בשגיאות 502 ו-504. השרת לא חסר משאבים. הוא פשוט מנתק חיבורים לפני שהיה להם סיכוי בכלל להסתיים.

זאת אחת הבעיות הנפוצות והכי ניתנות לפתרון בתחום כוונון ביצועי nginx. כמה הגדרות timeout קובעות כמה סבלני השרת שלכם עם לקוחות איטיים, שרתי upstream איטיים, ופרצי בקשות בו-זמניים. אם מגדירים אותן לא נכון, מאבדים תנועה לגיטימית בדיוק ברגעים שאתם הכי פחות יכולים להרשות לעצמכם.

למה Timeouts חשובים יותר ממה שחושבים

Nginx צריך לקבל החלטות כל הזמן: כמה זמן לחכות שלקוח ישלח בקשה מלאה, כמה זמן להשאיר חיבור פתוח בין בקשות, כמה זמן לחכות שהשרת האחורי שלכם (PHP-FPM, אפליקציית Node, מה שלא יהיה) יענה. כל אחת מההחלטות האלה נשלטת על ידי ערך timeout, ורוב ברירות המחדל נבחרו עבור מקרה שימוש כללי, לא עבור דפוס התנועה הספציפי שלכם.

בעומס רגיל, ברירות המחדל של ה-timeouts כמעט לא גורמות לבעיות. בקפיצת תנועה, הן הופכות לנטל. לקוחות איטיים עם חיבורי סלולר לא יציבים לוקחים יותר זמן לשלוח בקשות. השרת האחורי שלכם לוקח יותר זמן לענות כי הוא מטפל ביותר עבודה בו-זמנית. אם ה-timeouts שלכם אגרסיביים מדי, nginx מתחיל לסגור חיבורים שהיו מצליחים אם רק היה להם עוד כמה שניות סבלנות.

הגדרות ה-Timeout המרכזיות

אלה ההגדרות שהכי חשובות בזמן קפיצות תנועה:

  • client_header_timeout - כמה זמן nginx מחכה שהלקוח ישלח את כל כותרת הבקשה
  • client_body_timeout - כמה זמן הוא מחכה לגוף הבקשה (חשוב לתנועה עתירת POST כמו תהליכי תשלום או שליחת טפסים)
  • keepalive_timeout - כמה זמן חיבור לא פעיל נשאר פתוח, ממתין לבקשה נוספת
  • proxy_connect_timeout - כמה זמן nginx מחכה ליצור חיבור עם האפליקציה ה-upstream שלכם
  • proxy_read_timeout - כמה זמן הוא מחכה שה-upstream ישלח נתונים בחזרה
  • send_timeout - כמה זמן nginx מחכה בין כתיבות מוצלחות ללקוח

תצורת התחלה סבירה לרוב אפליקציות הווב תחת תנועה בינונית-גבוהה נראית כך:

http { client_header_timeout 15s; client_body_timeout 15s; keepalive_timeout 30s; send_timeout 15s; location / { proxy_connect_timeout 5s; proxy_read_timeout 30s; proxy_send_timeout 30s; } }

שימו לב ש-proxy_connect_timeout קצר. אם השרת האחורי שלכם לא מצליח אפילו לקבל חיבור תוך 5 שניות, חכיה ארוכה יותר לא תעזור, זה בדרך כלל סימן שמשהו כבר עמוס מדי. אבל proxy_read_timeout ארוך יותר, כי שרת אחורי עמוס אולי באמת צריך עוד כמה שניות לסיים שאילתה איטית או לרנדר דף תחת עומס.

Keepalive: החרב הפיפיות

חיבורי keepalive מקטינים עומס על ידי כך שהם מאפשרים ללקוח לעשות שימוש חוזר באותו חיבור TCP למספר בקשות, וזה חשוב מאוד בזמן קפיצת תנועה כשאתם לא רוצים לשלם את העלות של handshake חדש של TCP ו-TLS לכל בקשה בודדת. אבל אם keepalive_timeout מוגדר גבוה מדי, חיבורים לא פעילים מצטברים ותופסים משאבי worker שמבקרים חדשים צריכים.

לרוב האתרים, משהו בין 15 ל-30 שניות הוא הנקודה האופטימלית. תהליכי תשלום באתרי מסחר עם הרבה קריאות AJAX רציפות יכולים להרוויח מחלון קצת ארוך יותר. בלוג פשוט או אתר שיווקי כמעט אף פעם לא צריך יותר מ-15.

בדקו גם את הגדרות ה-keepalive של ה-upstream בנפרד. אם nginx עושה proxy ל-PHP-FPM או לאפליקציית Node, אתם רוצים חיבורים מתמשכים גם לשרת האחורי שלכם:

upstream backend { server 127.0.0.1:8000; keepalive 32; } server { location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; } }

זה לבד יכול לקצר משמעותית את ה-TTFB בתקופות של עומס גבוה כי nginx לא כל הזמן פותח מחדש חיבורים לשרת האפליקציה שלכם. כיסינו את הנושא הרחב יותר של שימוש חוזר בחיבורים באיך חיבורי Keep-Alive מצמצמים את העומס שמאט כל בקשה, אם אתם רוצים להבין את המנגנון לעומק.

Worker Connections ותור ה-Backlog

Timeouts לבדם לא יצילו אתכם אם nginx לא מצליח לקבל חיבורים חדשים מספיק מהר מלכתחילה. שתי הגדרות עובדות יחד עם כוונון ה-timeouts שלכם:

  • worker_connections - מספר מקסימלי של חיבורים בו-זמניים לכל תהליך worker
  • listen ... backlog= - כמה חיבורים ממתינים מערכת ההפעלה תשמור בתור לפני שהיא מתחילה לדחות חדשים
events { worker_connections 4096; multi_accept on; } server { listen 443 ssl backlog=2048; }

אם תור ה-backlog שלכם קטן מדי, חיבורים נכנסים נדחים לגמרי בזמן קפיצת תנועה, ושום הגדרת timeout לא תתקן את זה כי החיבור אפילו לא מקבל סיכוי להגיע ל-timeout. אנחנו נכנסים לעומק יותר לנושא גודל תהליכי ה-worker באיך להגדיר תהליכי Worker ב-Nginx לתפוקה מקסימלית.

הגבלת קצב בלי להעניש משתמשים אמיתיים

קפיצת תנועה ממבקרים אמיתיים לעולם לא צריכה להתבלבל עם מתקפה, אבל השניים יכולים להיראות דומים ברמת החיבור. שימוש עדין ב-limit_req יכול להגן על השרת האחורי שלכם בלי לדחות פרצי תנועה לגיטימיים:

limit_req_zone $binary_remote_addr zone=general:10m rate=20r/s; location / { limit_req zone=general burst=40 nodelay; }

ערך ה-burst חשוב כאן. אם מגדירים אותו נמוך מדי, מכירת פלאש הופכת לגל של שגיאות 503 עבור אנשים שפשוט לחצו על כמה קישורים מהר. אם אתם רוצים שליטה מדויקת יותר על אילו בקשות מוגבלות בקצב על בסיס מקור או דפוסי בקשה, שם הגדרה טובה של חומת אש לאפליקציות ווב (WAF) מוכיחה את עצמה, כי היא יכולה להחיל כללים חכמים יותר מספירת חיבורים גולמית.

מעקב אחרי המדדים הנכונים

אי אפשר לכוון בעיניים עצומות. לפני שמשנים משהו בסביבת הייצור, שלפו בסיס מדידה מהלוגים של nginx:

  • זמן תגובה של ה-upstream ($upstream_response_time בפורמט הלוג שלכם)
  • זמן בקשה ($request_time)
  • ספירת שגיאות 502/504 לאורך חלון זמן נע
  • חיבורים פעילים מ-stub_status

הוסיפו את זה להגדרות ה-nginx שלכם כדי לקבל סטטיסטיקות חיבור חיות:

location /nginx_status { stub_status; allow 127.0.0.1; deny all; }

אם אתם רואים שגיאות 504 עולות בעוד שה-CPU והזיכרון נשארים נמוכים, זו כמעט תמיד בעיית timeout, לא בעיית קיבולת. אם אתם רואים CPU תקוע ב-100 אחוז, timeouts לא יצילו אתכם, אתם צריכים יותר משאבים או caching טוב יותר ב-upstream. שכבת caching ברמת השרת טובה יכולה להוריד עומס עצום מהשרת האחורי שלכם בזמן קפיצות תנועה, מה שבתורו אומר שערכי ה-timeout הקיימים שלכם מפסיקים להיות תחת לחץ מלכתחילה.

רשימת בדיקה מעשית לכוונון

  • הגדירו client_body_timeout ו-client_header_timeout ל-10-15 שניות, לא ברירת המחדל של 60 שניות
  • שמרו על proxy_connect_timeout קצר (3-5 שניות) כי חיבור איטי בדרך כלל מסמן בעיה אמיתית
  • תנו ל-proxy_read_timeout מספיק מקום עבור נקודת הקצה הלגיטימית האיטית ביותר שלכם, לא המהירה ביותר
  • הפעילו keepalive ל-upstream כך שחיבורי השרת האחורי שלכם ינוצלו שוב תחת עומס
  • קבעו את גודל תור ה-backlog כך שיתאים לפרצי החיבורים הבו-זמניים הצפויים
  • השתמשו בהגבלת קצב עם ערך burst נדיב, לא חומה קשיחה

אף אחד מהשינויים האלה לא דורש חומרה חדשה. הם רק דורשים להתאים את התצורה שלכם לאיך שהתנועה שלכם באמת מתנהגת, במקום להשאיר את nginx על הגדרות ברירת המחדל מהיצרן. אם אתם באחסון VPS מנוהל, סוג כזה של כוונון בדרך כלל מטופל עבורכם כחלק מניהול שוטף של השרת, אבל שווה להבין מה קורה מתחת למכסה המנוע כדי שתדעו אילו שאלות לשאול כשקפיצת תנועה מגיעה.

השורה התחתונה

קפיצות תנועה לעיתים רחוקות הן הבעיה האמיתית. timeouts מוגדרים לא נכון הופכים עלייה זמנית לגל של בקשות כושלות שמשתמשים בפועל שמים לב אליהן. הקדישו עשרים דקות לבדוק את ההגדרות האלה מול דפוסי התנועה האמיתיים שלכם, ותאבדו הרבה פחות בקשות בפעם הבאה שמשהו הופך לוויראלי.