چگونه من با استفاده از کد Node.js 10،000 دلار در صورتحساب AWS پس انداز کردم

روزی که صورتحساب AWS من را شوکه کرد
صبح روز دوشنبه معمولی بود و من قهوه می خوردم و استفاده از AWS ماه گذشته را مرور کردم. ناگهان قلبم ضرب و شتم کرد. این لایحه به تعدادی رسیده بود که می توانست به راحتی یک سفر آخر هفته به بالی را پوشش دهد! وحشت وارد شده است. چگونه اینقدر زیاد شد؟ آیا سرورهای من شبانه رمزنگاری می کردند؟ من به عنوان شخصی که مسئول حمایت از یک برنامه پر ترافیک است ، می دانستم که باید این کار را برطرف کنم قبل از اینکه مدیر من تصمیم بگیرد که من را با یک جایگزین ارزان تر جایگزین کند (شاید Chatgpt .. فقط شوخی کنید :)).
این داستان این است که من چگونه قبض های AWS خود را 80 ٪ کاهش دادم ، نه با کاهش ترافیک یا کاهش موارد ، بلکه به سادگی با بهینه سازی کد Node.js.
مشکل: کد نفخ منتهی به استفاده بیش از حد از منابع
بعد از ساعت ها اشکال زدایی ، دلایل اصلی را شناسایی کردم. برنامه node.js ما ، هرچند کاربردی ، ناکارآمدی پنهان داشت:
- پرس و جوهای بیش از حد پایگاه داده
- نشت حافظه
- رسیدگی نادرست عملیات async
- وسط نرم افزار بهینه شده
این ممکن است برای توسعه دهندگان فصلی آشنا به نظر برسد. در صورت عدم پرداختن به آنها ، آنها منجر به ارائه بیش از حد سرورها می شوند و هم از محاسبات و هم استفاده از حافظه استفاده می کنند.
اصلاحات: نکات عملی برای بهینه سازی Node.js
در اینجا نحوه رفع هر مسئله و کاهش چشمگیر هزینه ها آورده شده است. این مراحل همچنین به شما در مصاحبه ها می بخشد ، جایی که چنین چالش های بهینه سازی متداول است.
1. نمایش داده های پایگاه داده را به حداقل برسانید
مسئله: هر تماس API در حال ساخت چندین نمایش داده های دفع شده از پایگاه داده بود. به عنوان مثال:
const getUserOrders = async (userId) => {
const user = await db.query(`SELECT * FROM users WHERE id = ${userId}`);
const orders = await db.query(`SELECT * FROM orders WHERE user_id = ${userId}`);
return { user, orders };
};
هر تماس به پایگاه داده ، تأخیر و بار غیر ضروری را معرفی می کند. در سناریوهای پر ترافیک ، این تماس ها پیچیده و منجر به تخریب عملکرد و هزینه های بالاتر پایگاه داده می شود.
رفع: از پیوندها استفاده کنید و فقط زمینه های لازم را انتخاب کنید. چندین تماس به یک پرس و جو بهینه شده را کاهش دهید:
const getUserOrders = async (userId) => {
return await db.query(`
SELECT users.name, orders.id, orders.amount
FROM users
JOIN orders ON users.id = orders.user_id
WHERE users.id = ${userId}`);
};
این رویکرد تمام اطلاعات لازم را در یک زمان بازیابی می کند. این سفر به دور شبکه را کاهش می دهد و از مکانیسم های کارآمد اتصال به پایگاه داده استفاده می کند ، که برای چنین عملیاتی بهینه شده اند.
شیرجه عمیق: بهینه سازی پرس و جو یک مهارت مهم است. نمایش داده های ضعیف مکتوب می تواند منجر به پاسخ آهسته و افزایش مصرف منابع شود. توضیح دهید که چرا نمایه سازی ، برنامه ریزی پرس و جو و کاهش تماس های اضافی مهم است. ابزارهایی مانند توضیح در SQL می توانند به تجسم عملکرد پرس و جو کمک کنند.
بینش مصاحبه: بسیاری از مصاحبه کنندگان در مورد بهینه سازی پرس و جو سؤال می کنند. نشان دادن این مثال توانایی شما در شناسایی ناکارآمدی ها و برطرف کردن آنها را به طور مؤثر نشان می دهد.
2. نشت حافظه را برطرف کنید
مسئله: ما در حال ذخیره کردن داده ها بودیم اما هرگز آن را پاک نمی کردیم و باعث می شد سرور به طور مکرر خراب شود. این مثال ساده لوحانه را در نظر بگیرید:
const cache = {};
app.get('/store', (req, res) => {
const key = req.query.key;
cache[key] = req.query.value;
res.send('Stored in cache');
});
این مثال ممکن است در ابتدا بی ضرر به نظر برسد ، اما با افزایش برنامه و پردازش هزاران درخواست ، Skyrockets استفاده از حافظه. بدون هیچ مکانیسمی برای پاک کردن داده های بلااستفاده ، سرور به یک بمب زمان تیک زدن تبدیل می شود.
رفع: از یک کتابخانه ذخیره مناسب مانند Redis یا پیاده سازی TTL (زمان به زندگی) استفاده کنید:
const NodeCache = require('node-cache');
const myCache = new NodeCache({ stdTTL: 3600 }); // 1 hour TTL
app.get('/store', (req, res) => {
const key = req.query.key;
const value = req.query.value;
myCache.set(key, value);
res.send('Stored in cache');
});
این رفع TTL را برای حذف خودکار داده های قدیمی ، آزاد کردن حافظه بدون مداخله دستی معرفی می کند.
شیرجه عمیق: نشت حافظه در Node.js همچنین می تواند از شنوندگان رویداد یا استفاده نامناسب بسته شود. ابزارهایی مانند Devtools Chrome یا کتابخانه هایی مانند Heapdump می توانند به شناسایی نشت کمک کنند. همیشه مصرف پشته را در تولید کنترل کنید.
3. عملیات async را بهینه کنید
مسئله: برخی از کارکردها از نظر ساختاری ضعیف بودند و منجر به مسدود کردن عملیات شدند. به عنوان مثال:
app.get('/process', async (req, res) => {
const data = await fetchData(); // Heavy operation
res.send(data);
});
در سناریوهایی که FetchData مدت زمان زیادی طول می کشد ، سرور به سایر درخواست ها پاسخگو نیست. این می تواند منجر به تنگناها و تجربه کاربر تخریب شده شود.
رفع: عملیات دسته ای را انجام دهید و در صورت امکان از جریان استفاده کنید:
app.get('/process', (req, res) => {
const stream = fetchDataStream(); // Returns a readable stream
stream.pipe(res); // Stream data directly to response
});
با پخش داده ها ، رفتار غیر مسدود کننده و زمان پاسخ سریعتر را برای کاربران تضمین می کنید. جریان ها به ویژه برای مجموعه داده های بزرگ یا پردازش پرونده مفید هستند.
شیرجه عمیق: طبیعت رویداد محور Node.js باعث می شود جریان ها به ابزاری قدرتمند تبدیل شوند. درک کنید که چگونه فشار فشار کار می کند و چرا برای مدیریت جریان داده مهم است. برای استفاده از ماژول جریان از ماژول جریان استفاده کنید.
نکته سرگرم کننده: همیشه رفتار Async vs Sync را در مصاحبه ها توضیح دهید. چگونه جریان ها می توانند داده های بزرگ را به طور کارآمد کنترل کنند.
4. میانه نرم افزار حسابرسی
مسئله: میان افزار غیر ضروری برای هر درخواست ، حتی در صورت عدم نیاز ، در حال اجرا بود.
app.use((req, res, next) => {
console.log('Middleware running for every request');
next();
});
این عمل به سربار غیر ضروری ، به ویژه برای مسیرهای پر ترافیک اضافه می کند. برای جلوگیری از چرخه محاسبات هدر رفته ، باید از میان افزار استفاده شود.
رفع: میانه وسایل را به صورت انتخابی اعمال کنید:
const specificMiddleware = (req, res, next) => {
console.log('Middleware running only for /specific endpoint');
next();
};
app.use('/specific', specificMiddleware);
این تضمین می کند که میانه نرم افزار فقط در صورت لزوم اجرا می شود ، بار سرور را کاهش می دهد و بهره وری را بهبود می بخشد.
شیرجه عمیق: سفارشات میانی در Express.js اهمیت دارد. از دنباله مناسب برای رسیدگی به خطا و عملکرد اطمینان حاصل کنید. همچنین ، اگر مهم نباشد ، هرگونه واسطه (مانند ورود به سیستم) را در تولید غیرفعال کنید.
نتیجه: یک برنامه لاغر و مقرون به صرفه.
پس از اجرای این تغییرات ، آنچه اتفاق افتاده است:
- تعداد نمونه EC2 کاهش یافته است: ما از 10 نمونه t2.large به 3 نمونه t2.medium رسیدیم.
- هزینه های پایگاه داده کاهش یافته است: بهینه سازی پرس و جو ، RDS را از 3 به 1 کاهش داد.
- زمان پاسخ بهبود یافته است: تأخیر API 40 ٪ کاهش یافته و تجربه کاربر را افزایش می دهد.
- بیل AWS 80 ٪ کاهش یافته است: نتیجه نهایی و رضایت بخش ترین.
نتیجه گیری: اصلاحات کوچک ، پس انداز بزرگ
این سفر ارزش بهینه سازی کد را به من آموخت. پرتاب منابع بیشتر در یک مشکل آسان است ، اما مهارت واقعی در شناسایی و رفع ناکارآمدی ها نهفته است. این که آیا شما در حال آماده سازی برای مصاحبه هستید یا مدیریت یک برنامه زنده ، به یاد داشته باشید: پاک کننده ، کد کارآمدتر پول صرفه جویی می کند و شما را به یک توسعه دهنده بهتر تبدیل می کند.
بنابراین دفعه بعد که یک لایحه AWS را می بینید ، وحشت نکنید. در عوض ، یک قهوه را بگیرید ، به کد حفر کنید و بهینه سازی کنید. شما ممکن است به اندازه کافی برای آن سفر بالی صرفه جویی کنید!
برنامه نویسی مبارک! 😊