למה לסגור אובייקטי חיבור?
כולנו כתבתנו את שורות הקוד הבאות מספיק פעמים כדי לזכור אותן בע"פ:
1 2 3 4 |
|
ופה ושם אנחנו גם כתבנו את השורות הבאות:
1 2 3 4 |
|
האם אנחנו יודעים מה בפועל קורה לאחר כל שורה? מדוע יש צורך גם לסגור, וגם לאפס את אובייקט החיבור? האם באמת צריך לבצע את זה? הסקריפטים שלנו, כך הרושם הכללי מכתיב, מתפקדים בלי שום בעיות אפילו אם אנחנו שוכחים מדי פעם לסגור את האובייקטים – אז למה צריך את זה בכלל? הלא יהיה קל יותר ויעיל יותר לפתוח חיבור אחד, גלובלי, לכל האתר – מאשר לפתוח ולסגור אובייקטים בכל עמוד ועמוד? זה נשמע הגיוני, אז למה לא?
כנראה שאין מודעות מספקת לנושא אובייקט החיבור, שכן הטעות הנפוצה של חסך בסגירת האובייקטים שלנו גורמת להרבה מהבעיות שאנחנו סובלים מהן, מבלי לדעת זאת אפילו.
איטיות בקבלת דפים (קליינט שולח בקשה לשרת, והוא צריך לחכות זמן ארוך לפני שהוא בכלל רואה את תחילת הפלט), איטיות בעיבוד דפים (שמצריכה אתחול כדי לפתור את הבעיה הלא מובנת), קריסת האתר (וטענת חברת ההוסטינג שהבעיה היא שלנו) הן ההשלכות הנפוצות של טיפול לא נכון באובייקטי החיבור.
העניין העצוב הוא, שברוב הסיטואציות העניין מחוץ לשליטתנו. אם מדובר בהוסטינג משותף עם עוד 100 אתרים שלא דואגים לטפל באובייקטים שלהם, דליפות זיכרון יגרמו להאטה משמעותית בשרת – וזאת המציאות. יחד עם זאת, זו לא סיבה לא לסגור את האובייקטים שלנו כמו שצריך, כי אם 100 האתרים האחרים זורקים, זה לא אומר שגם אנחנו צריכים לתרום להאטת השרת, ומכאן, האטת האתר שלנו.
בריכת החיבורים
כשאנו מנסים להתחבר למסד נתונים לראשונה, השרת צריך להתחבר לשרת שבו מאוכסן המסד נתונים ולעבור אימות משתמש לפני שיוכל לקבל חיבור מאומת – ובהתאם למהירות הרשת – יצירת החיבור היא מטלה שעשויה להיות ארוכה. כדי לחסוך בזמן, כל שרת IIS מציג בפנינו את השימוש בעקרון שנקרא Connection Pooling. (אני לא יודע אם כשהטביעו את המושג Connection Pooling דמיינו בריכה עם מופעי אובייקטים מתרחצים בה, אבל נשתמש בדימוי הזה לצורך העניין.) העיקרון של הבריכה מכתיב שניתן למחזר חיבורים מאומתים בין מספר משתמשים שונים, כדי לא לעבור את התהליך שוב ושוב עבור כל משתמש נפרד.
מה מתרחש בפועל? בפעם הראשונה שקליינט נכנס לאתר שלנו ומבקש ליצור חיבור למסד נתונים, הסקריפט שלנו בודק אם יש מתרחצים בבריכה. כיוון שזהו הקליינט הראשון שלנו, הבריכה אמנם מלאה בחיבורים – אבל אף אחד מהם אינו מתאים למסד נתונים שלנו. IIS יוזם חיבור למסד נתונים וממשיך להריץ את הסקריפט – עד הרגע שנתקל בשורה הקריטית – objConn.Close.
בשלב הזה IIS נותן לחיבור מגבת ובגד ים, ונותן לחיבור להעביר את הזמן בזולה. בשעה שהחיבור שלנו מתדיין עם החיבורים של אתרים אחרים לגבי כמה פרברטי הוא הרעיון שלפני שדרדסית הגיעה לא היתה שום דרדסית נקבה, ומה ניתן להסיק מכך – IIS ממשיך לעבד את בקשת העמוד ועובר לבקשה של הקליינט הבא.
אחרי עשר שניות, קליינט חדש מגיע לאתר שלנו ושולח בקשה שדורשת חיבור למסד נתונים. IIS שוב מגיע ומתבונן במתרחצים – והפעם מוצא את החיבור המאומת שלנו. בשלב הזה החיבור קם, מתנגב, אומר יפה שלום, והולך לעשות את שלו. כשהסקריפט נתקל שוב בשורה objConn.Close, החיבור חוזר למים.
כך קורה, שחיבורים מאומתים ממוחזרים בין קליינטים שונים מבלי צורך באימות מחדש בכל פעם. מדובר בחסכון בזמן ובמשאבים מבחינת השרת – או במילה אחת, יעילות.
חיבור יכול להישאר בבריכה שלו עד שעוברת הגבלת הזמן שלו (דקה אם איני טועה) או עד שיש יותר מדי חיבורים בבריכה, ולאחד מהם אין מקום יותר – ואז הוא נסגר. לגמרי. בפעם הבאה ש- IIS יסתכל על הבריכה, שוב פעם יראה בריכה מלאה בחיבורים שמהם אף אחד אינו מתאים למסד הנתונים שלנו – ויזום חיבור חדש.
חסכון במשאבים
עכשיו אנחנו כבר מבינים מה קורה אחרי כל שורה. objConn.Open מחפש חיבור שניתן יהיה למחזר, ואם אין – יוצר אחד חדש. objConn.Close מכניס את החיבור לבריכה, לשימוש מאוחר. set objConn = nothing לוקח את מופע האובייקט, לא את החיבור עצמו, ומאפס אותו כדי לחסוך במשאבים של השרת.
זהי טעות נפוצה לחשוב ש- IIS יודע לטפל במשאבים שאובייקטים תופסים ביעילות. למעשה ה- Garbage Collector של IIS מוכר בחוסר יעילותו במלחמה נגד דליפות זיכרון. כדי להיות בטוח שהסקריפטים שלנו מנצלים כמה שפחות משאבים, ו"פוצעים" את השרת כמה חלש שאפשר – אנחנו צריכים לזכור לאפס את המופעים שלנו. בכך אנו מגדילים את אורך הזמן בין אתחול לאתחול של השרת, ומשאירים את האתר שלנו יותר זמן באוויר.
כדי להיות מסוגלים לתת לשרת לתפקד כפי שהוא תוכנן לעבוד, עלינו לוודא שאנחנו פועלים כפי שמיקרוסופט רצו שנעבוד. שימוש באובייקטים גלובלים ברמת האפליקציה הורסים את עקרון Connection Pooling וגם יוצרים צוואר-בקבוק במקרה ויש מספר מבקרים בו-זמנית באתר שלנו, שכן כולם ינסו להשתמש באובייקט החיבור הגלובלי, והוא יוכל לשרת רק קליינט אחד בכל זמן נתון. אם המטרה שלנו היתה לחסוך במשאבים ולהפוך את הסקריפט שלנו ליותר קל ולוגי, למעשה השגנו בדיוק את המטרה ההפוכה.
לכן אסור לנו לשכוח לסגור את האובייקט טרם איפוסו ל- nothing, והכוונה היא בכל המקרים: אם אתם יודעים שסקריפט עומד להסתיים לרוץ – בין אם בכך שפשוט נגמרות השורות בקובץ, או בין אם ביוזמתנו ע"י Response.Redirect או Response.End – עלינו לסגור את אובייקט החיבור כדי שהחיבור המאומת יכנס לבריכה. אם לא נסגור אותו, גם החיבור וגם האובייקט יישארו "תלויים" בזיכרון של השרת, מבלי כל אפשרות להיות שמישים. או במילים אחרות, דליפות זיכרון והאטת ביצועים.
תגובות בפייסבוק