טיפול יעיל ב-Backend איטי

בפוסט הבא אני רוצה להציג פתרון לבעיה שקיימת בהרבה אתרים, כשה-Time to first byte (הזמן העובר מתחילת החיבור לשרת עד שהביט הראשון נשלח מהשרת לדפדפן) גבוהה מדי.

היחס הממוצע בזמן המושקע בגלישה בעמוד בין ה-Backend (השרת מייצר את התוכן) ל-Frontend (הדפדפן מוריד את התוכן ופורס את העמוד) עומד על 20%/80%. כלומר, אם דף הבית (הדף עצמו, ללא תמונות/JS וכו') נטען תוך 2 שניות, 400 מילישנייה מוקדשים ליצירת העמוד.

הזמן האופטימלי ל-TTFB הוא 200 מילישנייה, אבל, כאשר באתר שלך יש:

  • שאילתות SQL מורכבות.
  • שאילתות SQL שלא כתובות כהלכה – לדוגמא: Order by rand().
  • שימוש ב-API בתוך הקוד (ה-TTFB של ה-API מאט את יצירת העמוד).
  • קוד בעייתי (sleep?).

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

כדי לייעל את טעינת העמוד, אנו מעוניינים לשלוח לדפדפן את ה-Header בזמן שהשרת ממשיך ביצירת ה-Body וה-Footer , בזמן הזה, הדפדפן כבר יוריד/יטען מה-Cache את קבצי ה-CSS, JS, תוספים חברתיים וכו' המופיעים ב-Header וטעינת האתר ופריסתו תהיה מהירה יותר.

כדי לשלוח את ה-Header, נשתמש בפונקציה Flush, ונמקמה בתחתית ה-Header – כדי לשלוח כמה שיותר תוכן לדפדפן.

 איך זה נראה בפועל?

כדי להציג את ההבדלים בין שימוש ב-Flush לבין עבודה ללא Flush, אציג את השינוי באתר Fmylife.co.il (אתר Development לפרויקט עתידי).

כמה מילים על Fmylife: הסקריפט בנוי ב-PHP, הטמפלייט ב-Smarty, ביצעתי מס' תיקונים ושינויים בסקריפט, בנוסף, מאחר ומדובר בסקריפט "חברתי" (במערכת יש שימוש ב-Google plus, FB Like ו-Twitter), האתר נטען תוך 6 שניות בממוצע (תלוי במצבי רוח של טוויטר).

זמן טעינת עמוד הבית ללא פונקציית Flush:

מקרא:

  • כתום – יצירת חיבור.
  • ירוק – TTFB.
  • כחול – זמן הורדה.
  • קו ירוק (בצמוד ל-1.2) – תחילת פריסת העמוד בדפדפן)

מבחינת ביצועים המצב לא מושלם, זמן ה-TTFB עומד על 500 מילישנייה ותחילת טעינת CSS הוא לאחר 700 מילישנייה מטעינת דף הבית, הדפדפן מתחיל לפרוס את העמוד לאחר 1.1 שנייה מתחילת הטעינה (מומלץ לסיים את טעינת הדף תוך 2 שניות). בשורה התחתונה – יש מה לשפר.

זמן טעינת עמוד הבית עם פונקציית Flush:

מקרא:

  • כתום – יצירת חיבור.
  • ירוק – TTFB.
  • כחול – זמן הורדה.
  • קו ירוק (בצמוד ל-1.0) – תחילת פריסת העמוד בדפדפן.

שימו לב להבדל, קודם, ללא Flush, אלמנט ה-CSS מתחיל להיטען בסביבות 0.72, עם Flush, הטעינה מתחילה ב-0.22, כחצי שנייה קודם לכן, אומנם זמן טעינת העמוד הוא כמעט אותו הדבר (ואם אני אחזור על הבדיקה 100 פעם, לפחות ב-20 השוואות שני העמודים ירדו באותו הזמן), אך הדף עם Flush נטען מוקדם יותר ומהר יותר כפי שתראו בפסקה הבאה.

השוואה בין שני המצבים

הסרטון הבא מציג את טעינת FMyLife ב-2 מצבים, בצד שמאל – באמצעות Flush, ובצד ימין, ללא Flush. טעינה מלאה של שני המצבים לוקחת אותו הזמן (בגלל טוויטר, FB וגוגל פלוס), אך שימו לב לטעינת התוכן בדף, בטעינה עם Flush תוכן האתר נטען מהר יותר.

לסיכום:

בפוסט הזה הצגתי שיפור של חצי שנייה בטעינת התוכן באמצעות הוספת שורה אחת ב-PHP בסוף ה-Header:

flush();

וב-Smarty:

{php}flush();{/php}

תכננו מראש את האתר שלכם לשימוש נכון בשאילתות SQL, קבלת נתונים מ-API חיצוני ע"י AJAX ובכלל, בנו אתר יעיל. 

ערב טוב,

אבי קינן.

*** בעלי שרתים: קראו את התגובה הראשונה ***

פורסם בקטגוריה האצת אתרים. אפשר להגיע לכאן עם קישור ישיר.

16 תגובות על טיפול יעיל ב-Backend איטי

  1. מאת Avi Keinan‏:

    מבחינת צד שרת, כמה הערות:

    1. Flush עובד עם Gzip, אבל -> שימו לב ש-Gzip buffer מוגדר כהלכה, אחרת שרת ה-Web ימתין לקבלת כל העמוד לפני דחיסה.
    2. שרתי Reverse Proxy – בידקו שהפרוקסי מעביר את התוכן ישירות ולא ממתין לקבלת כל העמוד.
    3. אינטרנט אקספלורר גירסאות 6 ו-7 ימתינו לקבלת 256 byte לפני שתתחיל פריסת העמוד, במידה וה-Header קטן מ-256 byte, הוסיפו רווחים ב-Header.

    אבי

  2. מאת זיו‏:

    מאד מעניין,
    אני אשמח לדעת יותר על כיצד אתה מבצע את בדיקות המהירות מצדך? האם יש כלים שבהם אתה משתמש לבדיקת המהירות באתרים?

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

    • מאת Avi Keinan‏:

      היי, הבדיקות שהצגתי פה בוצעו בעזרת Webpagetest.org, אני משתמש גם ב-zoompf וגם במודל של pingdom.

      אני מכיר את השימוש בפונקציות א-סינכרוניות (זה מיושם ב-Yui.co.il ו-Gif.co.il), אבל FMyLife עדיין בפיתוח אז כרגע זה לא הזמן ל-WPO.
      ה-Flush לא מעמיס על השרת, אומנם מבוצעת פעולת דחיסה פעמיים (במידה ומפעילים GZIP) אבל הדבר מינורי ולא נראה שזה משפיע על האתר.

      • מאת זיו‏:

        תודה על התשובה והאתר…

        יש לי עוד שאלה מה לגבי שימוש בפונקציה כאשר יש cache לאותו דף? בעצם מה שזה אומר שהפונציה תפעל רק בעת ההרצות החד פעמיות כאשר יש צורך בעדכון הcache .
        דבר שני רשמת –
        מבחינת צד שרת, כמה הערות:
        1. Flush עובד עם Gzip, אבל -> שימו לב ש-Gzip buffer מוגדר כהלכה, אחרת שרת ה-Web ימתין לקבלת כל העמוד לפני דחיסה.
        ——-> איפה אפשר לבדוק את זה?

        • מאת Avi Keinan‏:

          "כאשר יש cache" -> איפה?
          ב-Apache? ב-APC? ב-Reverse proxy?

          לגבי סעיף 1, אתה יכול לבדוק את זה בעזרת curl, העלתי סרטון קצר ליוטוב ( http://www.youtube.com/watch?v=G7U36dUOAbk ), מומלץ לצפות ב-HD, שמציג את הפלט של הפקודה:
          curl http://www.fmylife.co.il

          שים לב שהתוכן נשלח בשנייה 5, ואז יש הפסקה של שנייה (הוספתי sleep של שנייה כדי להציג בצורה ברורה) ולאחר מכן שאר התוכן נשלח.
          במידה וה-gzip buffer היה מונע flush, לא הייתה עצירה של שנייה אלא כל התוכן היה מודפס ישירות.

  3. מאת טל‏:

    מה עם כל הקוד שלי נמצא עוד לפני הheader? זה לא יעזור לדחוף flush נכון?

  4. מאת טל‏:

    היי אבי, כוונתי הייתה שכל הקוד PHP (שמכיל API ושאילתות) כתוב לפני הHEADER כי חלקים מהHEADER תלויים בתוצאות של הקוד (למשל הtitle של הPAGE תלוי בשם שנשלף מהDB).

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

    (מקווה שאני מובן)

    תודה!

    • מאת Avi Keinan‏:

      היי טל,

      אתה תצטרך למקם מחדש את הקוד,
      קודם DB של TITLE (ורק של TITLE), flush, לאחר מכן קוד נוסף, Flush והלאה.

      שים לב שאם אתה משתמש ב-API ולא שומר את התוצאות ב-cache כלשהו, האתר שלך מושפע מביצועי ה-API.

      אבי.

  5. מאת טל‏:

    ד"א, איך זה מסתדר עם ob_start?

  6. פינגבאק: פורום הסקופים של רוטר – יכול להיטען מהר יותר? | אבי קינן – מחשבים וכו'

  7. מאת משה‏:

    מתנגד עקרונית לרעיון, על אף שהוא משפר ביצועים יפה.

    בסביבת ASP.net, אחד הדברים החשובים שהוחלו לעומת ASP/PHP הוא הפרדה בין קוד לעיצוב. הפרדה בין קובץ ה-cs/vb לבין העיצוב של הדף (HTML/CSS/JS וכו').

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

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

    • מאת אבי קינן‏:

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

להגיב על Avi Keinan לבטל

האימייל לא יוצג באתר. שדות החובה מסומנים *