محتوای آموزشی وبلاگ نتآرام
سایت بالا میآید، اما برنامه گاهی نمیتواند به درگاه پرداخت، API یا پایگاه داده وصل شود. شاید در گزارش برنامه پیام
Cannot assign requested address را ببینید و چند دقیقه بعد همهچیز موقتاً بهتر شود. یکی از علتهای ممکن، تمام شدن پورتهای موقتی است که لینوکس برای برقراری اتصال خروجی انتخاب میکند.برای فهمیدن موضوع، پورت موقت را مثل شماره صندلی یک مراجعهکننده در نظر بگیرید. وقتی برنامه یک اتصال تازه باز میکند، سیستم معمولاً یکی از این شمارهها را به سمت سرور خودتان اختصاص میدهد. اگر برنامه با سرعت زیادی اتصال بسازد و ببندد، یا اتصالها بیش از حد باز بمانند، برای مقصدی خاص ممکن است شماره قابل استفاده پیدا نشود. اما دیدن تعداد زیاد TIME_WAIT بهتنهایی ثابت نمیکند پورتها تمام شدهاند. باید خطا، مقصد و روند اتصالها را کنار هم ببینیم.
این راهنما برای مدیری است که به SSH و دسترسی sudo روی سرور لینوکس خود دسترسی دارد. دستورها را روی همان سرور یا همان محیط شبکهای اجرا کنید که برنامه داخل آن مشکل دارد؛ شبکه کانتینر ممکن است از شبکه میزبان جدا باشد. پیش از تغییر تنظیمات، مقدارهای قبلی را یادداشت کنید و یک راه دسترسی جایگزین مانند کنسول ارائهدهنده در اختیار داشته باشید.
۱. اول مطمئن شوید مشکل از مقصد یا شبکه نیست
زمان خطا، نام سرویس و آدرس مقصد را از گزارش برنامه یادداشت کنید. در دستورهای زیر
کد / دستورmyapp یک نمونه است؛ نام واقعی سرویس خود را قرار دهید. برای دیدن گزارش نیم ساعت اخیر در SSH بنویسید:sudo journalctl -u myapp --since '30 minutes ago' --no-pager
اگر سرویس با systemd اجرا نمیشود، گزارش را در ابزار اجرای برنامه یا مسیر لاگ آن پیدا کنید. دنبال ساعت دقیق و متن خطا بگردید. مثلاً
Connection refused معمولاً به معنی نپذیرفتن اتصال در مقصد است و با کمبود پورت یکی نیست؛ خطای گواهی HTTPS نیز مسیر بررسی جداگانهای دارد. پیام Cannot assign requested address میتواند به پورت موقت مربوط باشد، اما آدرس محلی نامعتبر هم میتواند علت باشد.آدرسهای فعال سرور را با دستور زیر ببینید:
کد / دستورip -br address
اگر برنامه را مجبور کردهاید از یک IP مشخص خارج شود، مطمئن شوید همان IP واقعاً روی کارت شبکه مربوط وجود دارد. برای دیدن مسیر به مقصد، IP نمونه را با IP واقعی مقصد جایگزین کنید:
کد / دستورip route get 192.0.2.10
در خروجی،
dev رابط شبکه و src آدرس مبدأ انتخابشده را نشان میدهد. آدرس 192.0.2.10 فقط نمونه آموزشی است. اگر مسیر یا IP مبدأ اشتباه است، ابتدا همان را با مدیر شبکه یا تنظیمات سرویس اصلاح کنید؛ بزرگ کردن محدوده پورت، مسیر اشتباه را درست نمیکند.۲. محدوده پورت و تعداد اتصالها را ببینید
این دستور محدوده فعلی پورتهایی را نشان میدهد که سیستم میتواند خودکار انتخاب کند:
کد / دستورsysctl net.ipv4.ip_local_port_range
مثلاً خروجی
کد / دستور32768 60999 به معنی آغاز و پایان محدوده است. این عدد را برای برگرداندن تنظیمات یادداشت کنید. دستور بعدی پورتهای رزروشده را نشان میدهد:sysctl net.ipv4.ip_local_reserved_ports
خروجی خالی یعنی برای این تنظیم، پورتی رزرو نشده است. پورتهای رزروشده در انتخاب خودکار کنار گذاشته میشوند. این تنظیم را برای آزمایش پاک نکنید؛ ممکن است برنامه دیگری به آن وابسته باشد.
حالا خلاصه وضعیت socketها را ببینید:
کد / دستورss -s
برای شمردن اتصالهای TCP در حالت TIME_WAIT و اتصالهای برقرار، این دو دستور را جداگانه اجرا کنید:
کد / دستورss -Htan state time-wait | wc -l
کد / دستورss -Htan state established | wc -l
هر عدد فقط یک عکس لحظهای است. در زمان خطا و دو دقیقه بعد دوباره آنها را بگیرید و یادداشت کنید. TIME_WAIT بخش طبیعی پایان برخی اتصالهای TCP است؛ صرفاً زیاد بودنش به معنی خرابی نیست. همچنین تعداد کل اتصالها را نمیتوان مستقیم با اندازه محدوده مقایسه کرد و حکم داد که همه پورتها پر شدهاند: ترکیب IP و پورت مبدأ و مقصد، استفاده مجدد از پورت برای مقصدهای متفاوت، فضای شبکه و روش اتصال برنامه اهمیت دارند.
برای دیدن جزئیات اتصالها، دستور زیر را اجرا کنید:
کد / دستورsudo ss -tanp
ستونهای
Local Address:Port و Peer Address:Port طرف سرور شما و مقصد را نشان میدهند. ستون process در صورت دسترسی کافی نام برنامه را نشان میدهد، ولی برای بعضی وضعیتها مانند TIME_WAIT ممکن است نام پردازش دیده نشود. اگر تعداد زیادی اتصال به یک مقصد و پورت مشخص میرود، نام آن مقصد و برنامه مسئول را یادداشت کنید؛ برای نمونه API پرداخت و پورت ۴۴۳. محتوای این خروجی میتواند IP و نام برنامههای داخلی را آشکار کند، پس آن را عمومی منتشر نکنید.برای بررسی اتصالهای مرتبط با مقصدی که از پورت HTTPS استفاده میکند:
کد / دستورsudo ss -tanp '( dport = :443 )'
این فیلتر همه مقصدهای پورت ۴۴۳ را نشان میدهد، نه فقط یک سایت. آدرس طرف مقابل را در خروجی بخوانید. اگر برنامه در Docker یا فضای شبکه جداست، همین بررسی را با ابزارهای موجود داخل محیط آن نیز انجام دهید؛ اعداد میزبان و کانتینر الزاماً یکسان نیستند.
۳. علت رایج را در خود برنامه برطرف کنید
اگر برنامه برای هر درخواست یک اتصال تازه میسازد، بهترین شروع معمولاً استفاده دوباره از اتصالها و محدود کردن درخواستهای همزمان است. این تغییر باید در همان برنامه یا کتابخانهای انجام شود که اتصال را باز میکند؛ یک تنظیم عمومی در لینوکس جای آن را نمیگیرد.
برای نمونه، اگر برنامه پایتون از کتابخانه
کد / دستورrequests استفاده میکند، میتوانید یک Session را در طول چند درخواست به یک مقصد نگه دارید:import requests
url = 'https://api.example.com/health'
with requests.Session() as session:
for _ in range(3):
response = session.get(url, timeout=(3, 10))
response.raise_for_status()
print(response.status_code)
این نمونه را در یک فایل آزمایشی روی سرور ذخیره کنید،
url را با نشانی مجاز سرویس خود عوض کنید و با پایتونی که کتابخانه Requests در آن نصب است اجرا کنید. Session امکان استفاده دوباره از اتصال مناسب را فراهم میکند؛ عددهای timeout بهترتیب سقف انتظار اتصال و خواندن را تعیین میکنند. سرور مقابل هم باید اتصال پایدار را بپذیرد؛ استفاده دوباره در همه شرایط تضمینشده نیست. اگر stream=True دارید، پاسخ را کامل بخوانید یا ببندید تا اتصال بیدلیل اشغال نماند. در برنامه واقعی خطاها را مدیریت کنید و داده محرمانه را در لاگ چاپ نکنید.همزمانی را هم با تنظیمات صف یا worker برنامه کنترل کنید. مثلاً اگر یک کار هر دقیقه ده هزار درخواست را یکباره به یک API میفرستد، اجرای دستههای کوچکتر با فاصله و سقف همزمانی مشخص فشار را کاهش میدهد. تعداد مناسب به ظرفیت برنامه و محدودیت API بستگی دارد؛ قبل و بعد از تغییر، سرعت پاسخ و خطاها را اندازه بگیرید تا صرفاً مشکل را پنهان نکرده باشید.
۴. اگر ظرفیت محدوده واقعاً کم است
فقط زمانی به تغییر محدوده فکر کنید که پس از بررسی، الگوی اتصالها و خطای برنامه با کمبود پورت سازگار باشد و استفاده دوباره از اتصال یا کاهش همزمانی کافی نباشد. محدوده جدید باید با سیاست فایروال، پورتهای سرویس، رزروهای موجود و محدودیتهای شبکه سازگار باشد. یک مقدار مناسب برای همه سرورها وجود ندارد.
برای مثال، اگر مقدار فعلی شما
کد / دستور32768 60999 است و مدیر شبکه تأیید کرده بازه پایینتر با سرویسها و قوانین شما تداخل ندارد، میتوان در زمان کمترافیک موقتاً این مقدار را امتحان کرد:sudo sysctl -w 'net.ipv4.ip_local_port_range=10000 60999'
دستور فقط تنظیم جاری همان فضای شبکه را تغییر میدهد و لزوماً بعد از راهاندازی مجدد باقی نمیماند. دوباره
کد / دستورsysctl net.ipv4.ip_local_port_range را اجرا کنید؛ باید بازه جدید را ببینید. اگر مشکل بدتر شد، با جایگزین کردن اعداد ثبتشده از سرور خودتان مقدار قبلی را برگردانید؛ برای مثال اگر واقعاً مقدار قبلی همان ۳۲۷۶۸ تا ۶۰۹۹۹ بوده است:sudo sysctl -w 'net.ipv4.ip_local_port_range=32768 60999'
این تغییر را بدون آزمون و هماهنگی دائمی نکنید. کاهش زمان TIME_WAIT با دستورهای پراکنده اینترنتی، خالی کردن همه اتصالها یا خاموش کردن فایروال ممکن است ارتباط سالم کاربران را مختل کند و جای تشخیص درست را نمیگیرد.
۵. اگر هنوز خطا باقی است
اگر جدول رهگیری اتصال پر شده باشد، مسئله با محدوده پورت برنامه یکی نیست. برای پیام
conntrack table full از راهنمای عیبیابی جدول conntrack کمک بگیرید. در شبکهای که چند ماشین از یک NAT یا IP خروجی مشترک استفاده میکنند، کمبود ظرفیت ممکن است در دروازه NAT باشد؛ در این حالت افزایش بازه داخل VPS بهتنهایی کافی نیست. ساعت خطا، IP و پورت مقصد و شمار تقریبی اتصالها را به مدیر شبکه بدهید تا ظرفیت و گزارش همان دروازه را بررسی کند.پس از اصلاح برنامه یا تنظیم تأییدشده، همان درخواست واقعی را در ساعت مشابه با بار عادی امتحان کنید. موفقیت یعنی خطای قبلی تکرار نشود، پاسخ سرویس مقصد درست باشد و نرخ خطا و الگوی اتصالها نسبت به قبل بهتر شود. فقط باز شدن صفحه اصلی سرور کافی نیست، چون شاید مشکل فقط هنگام اتصال به یک API رخ دهد. برای سرویس پرترافیک، سرور مجازی ایران و در بارهایی که به منابع اختصاصی نیاز دارند سرور اختصاصی ایران را بر اساس نیاز واقعی بررسی کنید؛ تعویض سرور بهتنهایی کد پراتصال را اصلاح نمیکند.
برای جزئیات فنی میتوانید مستندات تنظیمات شبکه کرنل لینوکس و راهنمای Session در Requests را ببینید.
اشتراکگذاری:
لینکدینتلگرامایکس
مقاله قبلی
مقاله بعدی
دیدگاهها (۰)
دیدگاههای تاییدشده و پاسخهای تیم نتآرام
