چگونه زمان. بعد از () می تواند باعث نشت حافظه در GO و نحوه رفع آنها شود

من به عنوان یک توسعه دهنده که با کتابخانه Tonutils-Go همکاری می کند تا با Ton blockchain ارتباط برقرار کند ، من زمان قابل توجهی را صرف ساخت پروژه های جانبی مختلف کردم. با گذشت زمان ، من یک الگوی نگران کننده را متوجه شدم: خدمتی که من با استفاده از چارچوب Echo ساخته ام ، بیشتر و بیشتر منابع را مصرف می کردم ، حتی وقتی که ترفندهای جزئی و بهینه سازی کردم.
زمان بررسی: حالت کارآگاه در 🕵🏻♂
من تحقیقات خود را با تلاش برای استراتژی های مختلف شروع کردم و تنظیمات کوچکی را برای مشخص کردن علت اصلی انجام دادم. آیا نشت گوروتین وجود داشت؟ من به ندرت از goroutines در کد خود استفاده می کردم ، به جز در یک بخش کوچک. آیا مسئله می تواند در سؤالات من باشد؟ شاید گورم چیزی را به هم ریخت؟
من گیر افتادم. بنابراین ، من تصمیم گرفتم که با استفاده از PPROF ، ابزار پروفایل از Google ، عمیق تر به اشکال زدایی بپردازم تا به پایین آن برسم.
طولی نکشید که من چیزی غیر منتظره را کشف نکردم: نشت گوروتین. اما چگونه می تواند باشد؟ من حتی مستقیم از goroutines استفاده نمی کردم!
من عمیق تر و عمیق تر حفر کردم و در نهایت ، به مقصر: Time.Newtimer () گیر افتادم.
صبر کنید … چی؟ من از هیچ تایمر در کد خود استفاده نمی کردم! بعد از گذراندن وقت بیشتر تحقیق ، فهمیدم که مشکل با کد خودم نیست-این ناشی از کتابخانه های شخص ثالث است که من از آنها استفاده می کردم. در حالی که دانستن اینکه من مقصر نبودم ، آرامش داشت ، اما نمی توانستم کمک کنم اما احساس ناامیدی می کنم. چگونه می توانم با چیزی که هیچ کنترلی بر آن نداشتم ، مسئله ای را برطرف کنم؟
علت اصلی: نشت از زمان. بعد ()
پس از تحقیقات بیشتر ، فهمیدم که این یک مسئله شناخته شده در GO است: منابع ایجاد شده توسط زمان. بعد از () هرگز زباله جمع آوری نمی شوند. این می تواند منجر به نشت حافظه شود ، به ویژه هنگامی که تایمرها متوقف نشوند یا به درستی اداره شوند.
{را انتخاب کنید
مورد <-time.after (time.second):
// بعد از 1 ثانیه کاری انجام دهید.
مورد <-ctx.done ():
// پس از اتمام زمینه کاری انجام دهید.
// منابع ایجاد شده توسط زمان. بعد از () زباله جمع آوری نمی شود
}
خوشبختانه ، من یک راه حل توصیه شده از سایر توسعه دهندگان پیدا کردم. آنها پیشنهاد کردند که از یک زمان استفاده کنید. newtimer () با یک زمینه برای اطمینان از اینکه تایمر به درستی مدیریت و تمیز شده است:
تأخیر: = time.newtimer (time.second)
{را انتخاب کنید
مورد <-delay.c:
// بعد از یک ثانیه کاری انجام دهید.
مورد <-ctx.done ():
// هنگامی که متن به پایان رسید کاری انجام دهید و تایمر را متوقف کنید.
اگر! leado.stop () {
// اگر تایمر متوقف شده است ، از کانال بخوانید.
<-delay.c
}
}
گامی به سوی یک راه حل: کمک به tonutils-go
مصمم برای حل مسئله ، من درخواست کشش را به مخزن Tonutils-Go ارسال کردم. به تسکین من ، نویسنده پروژه اصلاح را بررسی و پذیرفت! می توانید درخواست کشش را در اینجا بیابید: PR #297 در Tonutils-Go.
آیا نسخه جدید Golang آن را برطرف می کند؟
جالب اینجاست که من این رفع را با نسخه جدیدتر GO آزمایش کردم ، امیدوارم که این مسئله به این مسئله بپردازد ، اما فهمیدم که این رفتار بدون تغییر باقی مانده است. به نظر می رسد ، با وجود برخی به روزرسانی ها ، این مشکل در موارد خاص همچنان ادامه دارد. می توانید اینجا را بررسی کنید.
قرار گرفتن در معرض PPROF برای نظارت بر تولید و تجسم نشت حافظه
از آنجا که شناسایی و حل مسئله نیاز به نظارت بر برنامه به مرور زمان داشت ، تصمیم گرفتم آن را در NGINX در معرض دید قرار دهم تا امکان دسترسی به PPROF فراهم شود. این امر به من این امکان را می دهد تا میزان استفاده از حافظه و هرگونه نشت احتمالی را که ممکن است در طول ترافیک تولید واقعی ایجاد شود ، پیگیری کنم. این نباید همیشه در معرض اشکال زدایی قرار گیرد.
من نقطه پایانی PPROF را روی سرور خود تنظیم کردم و سپس تماشای برنامه را در زیر بار واقعی شروع کردم. من می دانستم که برخی از نشت ها می توانند برای تجلی زمان لازم باشند ، بنابراین مشاهده برنامه در طی درخواست های تولید عادی مهم است.
برای تجسم عملکرد و استفاده از حافظه ، من با استفاده از Homebrew Graphviz را روی Mac خود نصب کردم:
تولید آبرو Graphviz
با نصب Graphviz ، من می توانم تجسم مفیدی از تخصیص حافظه و فعالیت گوروتین ایجاد کنم ، که به این نکته کمک می کند تا در محل مصرف منابع و آزاد نشود.
در مرحله بعد ، من از ابزار Go PPROF برای ضبط و تجزیه و تحلیل داده های حافظه و goroutine استفاده کردم. در اینجا نحوه انجام این کار آورده شده است:
برای تخصیص حافظه:
Go Tool PPROF -Http =: 8080 https://production.com/debug/pprof/allocs
و برای پروفایل goroutine:
Go Tool PPROF -Http =: 8080 https://production.com/debug/pprof/goroutine
با اجرای این دستورات ، توانستم یک رابط وب را باز کنم که به من اجازه می داد تا بصری تخصیص حافظه برنامه و حالت های گوروتین را کشف کنم. این به من بینش ارزشمندی در مورد جایی که همه چیز اشتباه می شود و کجا می توانم بیشتر بهینه سازی کنم ، به من داد.
با قرار گرفتن در معرض PPROF به این روش ، من توانستم ترافیک تولید را رصد کنم و نشت هایی را که در غیر این صورت در یک محیط محلی سخت بود ، ردیابی کنم. این یک تکنیک ساده و در عین حال مؤثر برای تشخیص مشکلات عملکرد ظریف است که فقط در شرایط دنیای واقعی ظاهر می شوند.
قبل از آن:
بعد از:
بعد از نظارت بیشتر ، فهمیدم تعداد بیشتری از آنها وجود دارد و باید آنها را پیدا کنم و برطرف کنم! اما امیدوارم که این امر به شما کمک کند تا در مورد این موضوع اطلاعات بیشتری کسب کنید.
کدهای مثال از این وبلاگ است.
github من: https://github.com/iw4p