"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > إنشاء تطبيقات عالية الأداء ومتكاملة باستخدام React وNode.js وMongoDB: رحلة في قابلية التوسع والسرعة والحلول

إنشاء تطبيقات عالية الأداء ومتكاملة باستخدام React وNode.js وMongoDB: رحلة في قابلية التوسع والسرعة والحلول

تم النشر بتاريخ 2024-11-02
تصفح:243

Building High-Performance Full-Stack Apps with React, Node.js & MongoDB: A Journey in Scalability, Speed & Solutions

تفتح تطبيق الإنتاج الخاص بك وتلاحظ أنه يتوقف عن العمل. الواجهة الأمامية لا تستجيب. انتهت مهلة واجهات برمجة التطبيقات الخلفية. يبدو أن استعلامات MongoDB تعمل إلى أجل غير مسمى. يمتلئ صندوق الوارد الخاص بك بشكاوى المستخدمين. يتجمع فريقك معًا في محاولة لفرز الموقف.

هل كنت هناك؟ نعم، أنا أيضًا.

أنا أحد كبار مطوري Full Stack وقد سئمت من التطبيقات الجيدة بينما تستخدمها فقط كمستخدم واحد أو عندما تكون مساحة المشكلة بسيطة ولكن بعد ذلك تذبل وتنهار تحت حركة مرور حقيقية أو مهمة أكثر تطلبًا قليلاً.

ابق معي، وسأشرح لك كيفية معالجة هذه المخاوف باستخدام React وNode.js وMongoDB.

لن أقدم لك فقط برنامجًا تعليميًا قديمًا عاديًا آخر، بل سأشارك قصة. قصة حول كيفية معالجة مشكلات العالم الحقيقي وكيفية إنشاء تطبيق سريع وقابل للتطوير يمكنه اجتياز اختبار الزمن وأي شيء يطرحه.

1: عندما أصبحت React عنق الزجاجة

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

ومع ذلك، لم يمض وقت طويل قبل أن نبدأ في تلقي الشكاوى: كان التطبيق يتم تحميله ببطء شديد، وكانت التحولات متعثرة، وكان المستخدمون يشعرون بالإحباط بشكل متزايد. على الرغم من معرفة أن الميزات الجديدة كانت مفيدة، إلا أنها أدت عن غير قصد إلى حدوث مشكلات في الأداء. كشف تحقيقنا عن مشكلة: كان التطبيق يجمع جميع مكوناته في حزمة واحدة، مما أجبر المستخدمين على تنزيل كل شيء في كل مرة يصلون فيها إلى التطبيق.

الإصلاح: قمنا بتنفيذ مفهوم مفيد للغاية يسمى التحميل البطيء. لقد خطرت ببالي هذه الفكرة من قبل، ولكنها كانت بالضبط ما نحتاجه. لقد قمنا بتجديد بنية التطبيق بالكامل، مع التأكد من أنه لا يقوم إلا بتحميل المكونات الضرورية عند الحاجة.

إليك لمحة عن كيفية تنفيذنا لهذا الحل:

const Dashboard = React.lazy(() => import('./Dashboard'));
const Profile = React.lazy(() => import('./Profile'));

Loading...}>
  

النتيجة: كان تأثير هذا التغيير رائعًا. لقد شهدنا انكماشًا هائلاً بنسبة 30% في مجموعتنا وشهد المستخدمون تحميلًا أوليًا أسرع بكثير. أفضل ما في الأمر هو أن المستخدمين لم يكن لديهم أي فكرة عن استمرار تحميل أجزاء معينة من التطبيق، وقد استخدمنا تطبيق Suspense بحكمة وأظهرنا رسالة تحميل بسيطة وغير تدخلية.

2: ترويض وحش إدارة الدولة في رد الفعل

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

لذا، قضيت بعض الوقت في إنشاء إثبات المفهوم (POC) للحصول على بديل أفضل. لقد قمت بتوثيق الأمر وتسهيل العديد من اجتماعات تبادل المعرفة حول الشكل الذي قد يبدو عليه هذا النهج. قررنا في النهاية كمجموعة تجربة React Hooks (واستخدام Reducer على وجه الخصوص) كحل مقترح لإدارة الحالة لأننا أردنا في النهاية تعليمات برمجية أبسط وأقل من البصمة الهائلة لوقت التشغيل التي كانت الإصدارات الأحدث من Redux تتزايد عليها مع العديد من العناصر الأصغر حجمًا القائمة بذاتها الدول.

لم يكن التحول الذي أعقب ذلك أقل من ثوري. لقد وجدنا أنفسنا نستبدل العشرات من أسطر التعليمات البرمجية النموذجية بمنطق ربط موجز وسهل الفهم. فيما يلي مثال توضيحي لكيفية تنفيذ هذا النهج الجديد:

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count   1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

const CounterContext = React.createContext();

function CounterProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    
      {children}
    
  );
}

النتيجة: كان تأثير هذا التحول عميقًا وبعيد المدى. أصبح تطبيقنا أكثر قابلية للتنبؤ به وأسهل في التفكير فيه. سمحت قاعدة التعليمات البرمجية، التي أصبحت الآن أصغر حجمًا وأكثر بديهية، لفريقنا بالتكرار بوتيرة أسرع بكثير. ولعل الأهم من ذلك هو أن المطورين المبتدئين لدينا قد أبلغوا عن تحسن ملحوظ في قدرتهم على التنقل وفهم قاعدة التعليمات البرمجية. وكانت النتيجة النهائية وضعًا مربحًا للجانبين: تعليمات برمجية أقل يجب صيانتها، وعدد أقل من الأخطاء التي يجب القضاء عليها، وفريق تطوير أكثر سعادة وإنتاجية بشكل ملحوظ.

3: التغلب على ساحة المعركة الخلفية — تحسين واجهات برمجة تطبيقات Node.js لتحقيق أعلى مستوى من الأداء

على الرغم من أننا تمكنا من إدخال الكثير من التحسينات على الواجهة الأمامية لدينا، إلا أنه بعد فترة وجيزة واجهنا العديد من المشكلات في الواجهة الخلفية. أصبح أداء واجهة برمجة التطبيقات لدينا سيئًا وكان هناك عدد قليل من نقاط النهاية على وجه الخصوص والتي بدأت في الأداء بشكل سيء. تقوم نقاط النهاية هذه بإجراء سلسلة من الاستدعاءات لخدمات مختلفة تابعة لجهات خارجية ومع تزايد قاعدة المستخدمين، لم يكن النظام قادرًا على التعامل مع هذا الحمل.

لقد كان من المنطقي جدًا معرفة الخطأ: لم نكن متوازيين! أي أنه تم التعامل مع الطلبات الواردة إلى كل نقطة نهاية بطريقة تسلسلية، أي أن كل مكالمة تالية ستنتظر حتى تكتمل المكالمة السابقة. في هذا النظام واسع النطاق (مائة ألف طلب)، ثبت أنه كارثي.

الحل: لإصلاح هذه المشكلة، قررنا إعادة كتابة الكثير من التعليمات البرمجية الخاصة بنا واستخدام قوة Promise.all() لتقديم طلب واجهة برمجة التطبيقات بطريقة متزامنة. وهذا يعني أنك تطلق طلبات متعددة ولا يتعين عليك الانتظار حتى تنتهي كل مكالمة لبدء المكالمة التالية.

للقيام بذلك، نحن لا نطلق استدعاء واجهة برمجة التطبيقات (API)، وننتظر حتى ينتهي، ونقوم بإجراء استدعاء آخر وما إلى ذلك...

بدلاً من ذلك ببساطة باستخدام Promise.all()، تم إطلاق كل شيء مرة واحدة وبشكل أسرع بكثير.

إليك لمحة عن كيفية تنفيذنا لهذا الحل:

const getUserData = async () => {
  const [profile, posts, comments] = await Promise.all([
    fetch('/api/profile'),
    fetch('/api/posts'),
    fetch('/api/comments')
  ]);
  return { profile, posts, comments };
};

النتيجة: كان تأثير هذا التحسين فوريًا وجوهريًا. لقد لاحظنا انخفاضًا ملحوظًا بنسبة 50% في أوقات الاستجابة، وأظهرت الواجهة الخلفية لدينا مرونة محسنة بشكل ملحوظ في ظل الأحمال الثقيلة. لم يعد المستخدمون يواجهون أي تأخيرات محبطة، وشهدنا انخفاضًا كبيرًا في عدد مهلات الخادم. لم يؤدي هذا التحسين إلى تحسين تجربة المستخدم فحسب، بل سمح أيضًا لنظامنا بمعالجة حجم أكبر بكثير من الطلبات دون المساس بالأداء.

4: مهمة MongoDB - ترويض وحش البيانات

مع اكتساب تطبيقنا قوة جذب ونمو قاعدة مستخدمينا بشكل كبير، كان علينا أن نواجه عقبة جديدة: كيف يمكنك توسيع نطاق بياناته؟ بدأ مثيل MongoDB الذي كان سريع الاستجابة في الاختناق عند الاضطرار إلى التعامل مع ملايين المستندات. الاستعلامات التي كانت يتم تشغيلها بالمللي ثانية كانت تستغرق ثوانٍ حتى تكتمل - أو انتهت مهلتها.

لقد أمضينا بضعة أيام في البحث في أدوات تحليل أداء MongoDB وحددنا الشخص السيئ الكبير: الاستعلامات غير المفهرسة. كانت بعض استعلاماتنا الأكثر شيوعًا (مثل طلبات ملفات تعريف المستخدمين) تفحص مجموعات كاملة يمكنهم من خلالها استخدام الفهارس الصلبة.

الحل: من خلال المعلومات التي كانت لدينا، علمنا أن كل ما يتعين علينا القيام به هو إنشاء فهارس مركبة في تلك الحقول الأكثر طلبًا وأن هذا من شأنه إصلاح وقت عمليات البحث عن نص قاعدة البيانات لدينا إلى الأبد. وإليك كيفية قيامنا بذلك عندما يتعلق الأمر بحقلي "اسم المستخدم" و"البريد الإلكتروني".

db.users.createIndex({ "username": 1, "email": 1 });

النتيجة: كان تأثير هذا التحسين رائعًا. الاستعلامات التي كانت تستغرق في السابق ما يصل إلى ثانيتين لتنفيذها، أصبحت الآن تكتمل في أقل من 200 مللي ثانية - وهو تحسن في الأداء بمقدار عشرة أضعاف. استعادت قاعدة البيانات الخاصة بنا استجابتها السريعة، مما يسمح لنا بالتعامل مع حجم أكبر بكثير من حركة المرور دون أي تباطؤ ملحوظ.

ومع ذلك، لم نتوقف عند هذا الحد. وإدراكًا لاحتمال استمرار مسار النمو السريع لدينا، فقد اتخذنا تدابير استباقية لضمان قابلية التوسع على المدى الطويل. قمنا بتنفيذ عملية التقسيم لتوزيع بياناتنا عبر خوادم متعددة. سمح لنا هذا القرار الاستراتيجي بالتوسع أفقيًا، مما يضمن نمو قدرتنا على التعامل مع البيانات جنبًا إلى جنب مع قاعدة المستخدمين الآخذة في الاتساع.

5. تبني الخدمات المصغرة – حل لغز قابلية التوسع

مع استمرار تكاثر قاعدة مستخدمينا، أصبح من الواضح أكثر فأكثر أننا لم نحتاج فقط إلى توسيع نطاق بنيتنا التحتية، ولكن كان علينا تطوير تطبيقنا حتى نتمكن من التوسع بثقة. لقد تناسبتنا الهندسة المعمارية المتجانسة جيدًا عندما كنا فريقًا أصغر، ولكن مع مرور الوقت أصبح الأمر مرهقًا للغاية. كنا نعلم أننا بحاجة إلى اتخاذ هذه الخطوة والبدء في البناء نحو بنية الخدمات الصغيرة - وهي مهمة مرهقة لأي فريق هندسي، ولكنها مهمة تتمتع بقدر كبير من قابلية التوسع والموثوقية.

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

إليك لمحة عن كيفية تنفيذنا لهذا الحل:

const amqp = require('amqplib/callback_api');

amqp.connect('amqp://localhost', (err, conn) => {
  conn.createChannel((err, ch) => {
    const queue = 'task_queue';
    const msg = 'Hello World';

    ch.assertQueue(queue, { durable: true });
    ch.sendToQueue(queue, Buffer.from(msg), { persistent: true });
    console.log(`Sent ${msg}`);
  });
});

النتيجة: بدا التحول نفسه جنبًا إلى جنب مع الاتصالات التي تم إجراؤها من خلال RabbitMQ مثل السحر من وجهة نظرنا ... والأرقام أكدت ذلك !!! لقد أصبحنا مالكين محظوظين للخدمات الصغيرة المترابطة بشكل غير محكم حيث يمكن توسيع نطاق كل خدمة بمفردها. فجأة، لم تكن الارتفاعات الحقيقية في حركة المرور في منطقة نظام أسماء النطاقات الملموسة تنطوي على خوف من تعطل النظام (بغض النظر عن عملية الخدمة التي تطلب نفس الشيء لأنها دائمًا متسلسلة) ولكنها عملت بشكل جيد، حيث رفعت الأجزاء/العمليات المتبقية أيديها بهدوء قائلة " أستطيع أن أنام يا عزيزي. أصبحت الصيانة أيضًا أسهل وأقل إشكالية بينما كانت إضافة ميزات جديدة أو تحديثات أسرع وأكثر ثقة.

الخلاصة: تخطيط دورة للابتكار المستقبلي

كانت كل خطوة في هذه الرحلة المثيرة بمثابة درس يذكرنا بأن تطوير المكدس الكامل هو أكثر من مجرد كتابة التعليمات البرمجية. إنه فهم المشكلات المعقدة المترابطة ثم حلها - بدءًا من جعل واجهاتنا الأمامية أسرع وإنشاء واجهات خلفية لتحمل حالات الفشل، وحتى التعامل مع قواعد البيانات التي تتوسع أثناء انفجار قاعدة المستخدمين الخاصة بك.

بينما نتطلع إلى النصف الثاني من عام 2024 وما بعده، لن يتباطأ الطلب المتزايد على تطبيقات الويب. إذا واصلنا التركيز على بناء تطبيقات قابلة للتطوير وتحسين الأداء ومصممة بشكل جيد، فسنكون في وضع يسمح لنا بحل أي مشكلة اليوم - والاستفادة من تلك التحديات الأخرى في مستقبلنا. لقد أثرت هذه التجارب الواقعية بشكل كبير على كيفية تعاملي مع التطوير الشامل - ولا أستطيع الانتظار لأرى أين ستستمر هذه التأثيرات في دفع صناعتنا!

ولكن ماذا عنك؟ هل واجهت عوائق مماثلة أو حالفك الحظ مع طرق إبداعية أخرى للتغلب على هذه المشكلات؟ أود أن أسمع قصصك أو أفكارك - أخبرني بذلك في التعليقات أو تواصل معي!

بيان الافراج تم إعادة إنتاج هذه المقالة على: https://dev.to/mukhilpadmanabhan/building-high-performance-full-stack-apps-with-react-nodejs-mongodb-a-journey-in-scalability-speed-solutions-3fk0؟ 1. في حالة وجود أي انتهاك، يرجى التواصل مع [email protected] لحذفه.
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3