Node.js שוחרר לראשונה בשנת 2009 ומאז זכה לאימוץ נרחב בקהילת המפתחים בשל קלות השימוש, המהירות והסקלביליות שלו. הוא משמש לבניית שרתי אינטרנט, כלי שורת פקודה, יישומי שולחן עבודה והתקני IoT.
במאמר הזה נצלול לעומק ונעבור על הארכיטקטורה מאחורי הקלעים, ונבין באילו מקרים כדאי לנו להשתמש ב- Node.js (ומתי לא).
*הבהרה: המאמר הזה הוא חלק שני בסדרה. מומלץ לקרוא לפני כן את המדריך הקודם: מבוא ל- Backend | מאחורי הקלעים של צד-השרת. בנוסף, מומלץ לקרוא את המאמר JavaScript – מתחת למכסה המנוע | מדריך למתקדמים.
תוכן עניינים
Node.js היא סביבת זמן ריצה חוצה פלטפורמות בקוד פתוח המאפשרת למפתחים להריץ קוד JavaScript מחוץ לדפדפן אינטרנט. הסביבה נבנתה על גבי מנוע V8 JavaScript של גוגל ומשתמש במודל I/O מונע אירועים (event-driven), לא חוסם (non-blocking) שהופך אותו למתאים ביותר לבניית יישומי רשת סקיילביליים בעלי ביצועים גבוהים.
ארכיטקטורת Node.js
Node.js בנוי על גבי מספר רכיבים, כולל Node.js , V8 , libuv ו- C++ .
- Node.js :Node.js היא סביבת זמן הריצה שמבצעת קוד JavaScript מחוץ לדפדפן אינטרנט.
- V8 :V8 הוא מנוע JavaScript בעל ביצועים גבוהים שממיר קוד JavaScript לקוד מכונה.
- libuv :libuv היא ספרייה חוצת פלטפורמות המספקת לולאת אירועים (event loop), יכולות קלט/פלט (I/O)* אסינכרונית ו-thread pool.
- C++: סביבת Node.js כתובה ב-C++, מה שמאפשר לה להיות יעילה ובעלת ביצועים גבוהים במיוחד.
*הערה: בהקשר של Node.js המונח I/O (קלט/פלט: I= input , O = output) מתייחס בדרך כלל לאופן שבו סביבת זמן הריצה של Node.js מטפלת בפעולות קלט ופלט כגון קריאה וכתיבת קבצים, ביצוע בקשות רשת וטיפול בקלט משתמש.
Node.js event loop
ה-event loop היא הליבה של Node.js ואחראית לטיפול בבקשות מהלקוח וביצוע callbacks. כאשר מתקבלת בקשה, היא מתווספת ל-event loop, וכאשר callback מבוצע, הוא מתווסף חזרה לevent loop.
ה-event loop משתמשת בתור לניהול סדר הביצוע של callbacks, והיא משתמשת בתבנית ה-event emitter* כדי להודיע למנויים כאשר מתרחשים אירועים.
*הערה: בבסיסה, תבנית ה- event emitter כוללת שני מרכיבים עיקריים: event emitter ומאזינים לאירועים. ה-event emitter הוא אובייקט שפולט אירועים, ו- event listeners הם פונקציות הרשומות לטיפול באירועים ספציפיים. כאשר מתרחש אירוע, ה- event emitter מודיע לכל מאזיני האירועים הרשומים על ידי קריאה לפונקציות המשויכות להם. מאזיני האירועים יכולים לאחר מכן לבצע את כל הפעולות הנדרשות בתגובה לאירוע
Processes, Threads, and the Thread Pool
Node.js משתמש ב-event loop עם תהליכון יחידי כדי לטפל בבקשות, אך הוא משתמש גם ב-Thread Pool כדי לטפל בפעולות יקרות שלא ניתן לטפל בהן על ידי event loop.
כאשר נתקלים בפעולה יקרת משאבים, כגון קריאת קובץ או בקשת רשת, Node.js מעביר את הפעולה ל-worker thread במאגר ה-threads, אשר מבצע את הפעולה ומחזיר את התוצאה ל-event loop הראשי.
זה מאפשר ל-Node.js לטפל במספרים גדולים של חיבורים במקביל מבלי לחסום את ה-event loop.
אירועים וארכיטקטורה מוּנַעַת אירועים (Events and Event-driven Architecture)
אירועים הם מושג ליבה ב-Node.js ומשמשים ליישום ארכיטקטורה מונעת אירועים.
אירוע הוא אות לכך שהתרחשה פעולה מסוימת, כגון קובץ שנקרא, בקשת רשת שהושלמה או טיימר שפג תוקפו.
ב-Node.js, אירועים מיושמים באמצעות מחלקת ה-EventEmitter, המאפשרת למנויים לרשום event liteners ולקבל התראות כאשר מתרחשים אירועים.
ארכיטקטורה מונעת אירועים מתאימה היטב לבניית יישומים סקיילביליים ובעלי ביצועים גבוהים מכיוון שהיא מאפשרת I/O (קלט/פלט) אסינכרוני ולא חוסם ומנתקת את הרכיבים של אפליקציה.
Blocking vs Non-Blocking Code
קוד שאינו חוסם חיוני ב-Node.js מכיוון שהוא מאפשר לה לטפל במספרים גדולים של חיבורים במקביל מבלי לחסום את ה-event loop. לכן חשוב להבין את ההבדל בין קוד חוסם לקוד שאינו חוסם.
קוד חוסם הוא קוד שמחכה לסיום פעולה מסוימת לפני מעבר לשורת הקוד הבאה. לדוגמה, נראה את הקוד הבא שקורא קובץ מהדיסק:
בדוגמה זו, readFileSync היא מתודה סינכרונית כך שהיא חוסמת את ה-event loop עד לקריאת הקובץ, ומונעת ביצוע של פעולות אחרות. תארו לעצמכם אם הקובץ הזה היה קובץ גדול מאוד לקריאה מהדיסק, אז פעולה זו עלולה היתה להימשך זמן רב.
קוד לא חוסם, לעומת זאת, אינו חוסם את ה-event loop ומאפשר המשך פעולות אחרות בזמן שהפעולה הנוכחית מתבצעת. לדוגמה, נראה את הקוד הבא שקורא קובץ עם המתודה האסינכרונית readFile:
בדוגמה זו, מתודת readFile לוקחת פונקציית callback המופעלת כאשר הקובץ נקרא, ומאפשרת לפעולות אחרות להמשיך בזמן קריאת הקובץ.
מה אנחנו לא יכולים לעשות עם Node.js
בעוד ש-Node.js היא סביבת זמן ריצה חזקה ויעילה שמתאימה היטב לבניית יישומי רשת סקיילביליים וביצועים גבוהים, ישנם סוגים מסוימים של יישומים שעבורם היא עשויה להיות לא הבחירה הטובה ביותר.
הנה כמה דוגמאות מהחיים האמיתיים לסוגי היישומים שעבורם Node.js היא אולי לא הבחירה הטובה ביותר:
- יישומים הדורשים כוח עיבוד רב: כפי שצויין קודם לכן, ייתכן ש-Node.js אינה מתאימה ליישומים הדורשים כוח עיבוד רב. לדוגמה, יישומים הכוללים משימות חישוביות כבדות כמו קידוד וידאו, למידת מכונה או סימולציות מדעיות עלולות לא להתבצע היטב ב-Node.js. במקרים אלה, שפות תכנות כגון C++, Java או Python עשויות להיות מתאימות יותר.
- יישומים עתירי זיכרון: ל-Node.js יש תקורה גבוהה יחסית של זיכרון עקב השימוש במנוע V8 JavaScript, שאולי אינו אידיאלי עבור יישומים הדורשים זיכרון רב. לדוגמה, יישומים הכוללים עיבוד כמויות גדולות של נתונים או הפקת דוחות גדולים עשויים שלא לספק ביצועים טובים ב-Node.js. במקרים אלו, שפות תכנות כגון Go או Rust, המיועדות לשימוש יעיל בזיכרון, עשויות להתאים יותר.
- יישומי שולחן עבודה: בעוד ש-Node.js משמשת לעתים קרובות לבניית יישומי רשת ושרתי אינטרנט, ייתכן שהיא אינה הבחירה הטובה ביותר לבניית יישומי שולחן עבודה. הסיבה לכך היא שיישומי שולחן העבודה דורשים בדרך כלל ממשקי משתמש מורכבים יותר ועשויים לכלול לוגיקה ומניפולציית מידע מורכבת יותר מאשר יישומי רשת. עבור יישומי שולחן עבודה, שפות תכנות כגון C#, Java או Python עשויות להתאים יותר.
באופן כללי, בגלל ש-Node.js היא single thread נחשב מקובל לומר ש-Node.js לא מתאימה כשיש חישוב כבד, כגון משחקים, עיבוד תמונה או ML. אפליקציות כלליות אפשר לבנות עם Node.js. לדוגמה, VS Code בנויה עם Node.js. - יישומי גרפיקה בזמן אמת: Node.js עשוי שלא להתאים ליישומים הדורשים עיבוד גרפי בזמן אמת, כגון משחקי תלת מימד או יישומי מציאות מדומה. הסיבה לכך היא שיישומים כאלה דורשים גישה ברמה נמוכה לחומרה ולעיתים קרובות כרוכים באלגוריתמים גרפיים מורכבים שעשויים לא להתבצע בצורה מיטבית ב-Node.js. עבור יישומים כאלה, שפות תכנות כגון C++ או אפילו מנועי משחק מיוחדים עשויים להתאים יותר.
בסך הכל, בחירת שפת התכנות וסביבת זמן הריצה תלויה במגוון גורמים, לרבות דרישות האפליקציה, הניסיון והמומחיות של צוות הפיתוח וזמינות הספריות והמסגרות.
יתרונות השימוש ב-Node.js
Node.js מציע מספר יתרונות על פני טכנולוגיות מסורתיות בצד השרת כגון PHP, Ruby ו-Java. חלק מהיתרונות הללו כוללים:
- מהירות וסקיילביליות: Node.js בנוי על גבי V8, מנוע JavaScript בעל ביצועים גבוהים, המאפשר לו לבצע קוד JavaScript הרבה יותר מהר מאשר טכנולוגיות מסורתיות בצד השרת.
- קלט/פלט לא חוסם (Non-blocking I/O): Node.js משתמש במודל קלט/פלט לא חוסם המאפשר לו להתמודד עם מספר רב של חיבורים בו-זמניים מבלי לחסום את ה-event loop, מה שהופך אותו לסקיילבילי ויעיל ביותר.
- JavaScript: סביבת Node.js מאפשרת למפתחים להשתמש באותה שפת תכנות (JavaScript) הן בצד הלקוח והן בצד השרת, מה שמקל על פיתוח ותחזוקה של יישומי אינטרנט.
- מערכת אקולוגית עשירה: ל-Node.js יש מערכת אקולוגית עשירה של מודולים וספריות שניתן לשלב בקלות ביישומי אינטרנט, מה שהופך את הפיתוח למהיר ויעיל יותר.
דוגמאות למקרים שבהם מומלץ להשתמש ב-Node.js
הנה כמה דוגמאות מהחיים האמיתיים לסוגי היישומים המתאימים היטב ל-Node.js:
- יישומי אינטרנט בזמן אמת: Node.js אידיאלית לבניית יישומי אינטרנט בזמן אמת הדורשים הרבה אינטראקטיביות וזמני תגובה מהירים, כגון יישומי צ'אט, פלטפורמות מדיה חברתית וכלי שיתוף פעולה. לדוגמה, יישומים כמו Slack, Trello ו-Asana משתמשים כולם ב-Node.js כדי להפעיל את תכונות שיתוף הפעולה שלהם בזמן אמת.
- יישומי סטרימינג: Node.js מתאימה היטב לבניית יישומי סטרימינג הדורשים עיבוד בזמן אמת של כמויות גדולות של נתונים, כגון שירותי הזרמת וידאו, שירותי הזרמת מוזיקה ופלטפורמות משחק מקוונות. לדוגמה, יישומים כמו Netflix ו-Twitch משתמשים כולם ב-Node.js כדי להפעיל את שירותי הסטרימינג שלהם.
- ממשקי API ומיקרו-סרביסים: Node.js אידיאלית לבניית ממשקי API ומיקרו-סרביסים הדורשים עיבוד בצד שרת מהיר וסקיילבילי. לדוגמה, יישומים כמו PayPal, LinkedIn ואובר משתמשים כולם ב-Node.js כדי להפעיל את ממשקי ה-API והמיקרו-סרביסים שלהם.
- יישומי Single page: סביבת Node.js מתאימה היטב לבניית יישומי SPA הדורשים ממשקי משתמש מהירים ומגיבים. לדוגמה, יישומים כמו PayPal, LinkedIn ו-Walmart משתמשים כולם ב-Node.js כדי להפעיל את היישומים שלהם בעמוד אחד.
- יישומי IoT: סביבת Node.js אידיאלית לבניית יישומי האינטרנט של הדברים (IoT) הדורשים עיבוד בזמן אמת של נתוני חיישנים וסוגים אחרים של זרמי נתונים. לדוגמה, יישומים כמו Nest, Raspberry Pi ו-Electrical Imp משתמשים כולם ב-Node.js כדי להפעיל את פלטפורמות ה-IoT שלהם.
בסך הכל, Node.js היא סביבת זמן ריצה רב-תכליתית ועוצמתית שמתאימה היטב לבניית מגוון רחב של יישומים, במיוחד אלה הדורשים עיבוד צד שרת מהיר וסקיילבילי, עיבוד נתונים בזמן אמת וממשקי משתמש מהירי תגובה.
כמה מילים לסיום
לסיכום, Node.js היא סביבת זמן ריצה רבת עוצמה ויעילה שמתאימה היטב לבניית שרתים סקיילביליים ובעלי ביצועים גבוהים. היא משתמשת במודל קלט/פלט מונחה אירועים שאינו חוסם המאפשר לה להתמודד עם מספר רב של חיבורים בו-זמניים מבלי לחסום את ה-event loop, מה שהופך אותה לבחירה האידיאלית לבניית יישומי אינטרנט בזמן אמת, יישומי צ'אט, שרתי משחקים ויישומים אחרים עתירי רשת.