چرا Code-level Monolith معماری برنده است؟ (درسهایی از Grafana Loki)
دوراهی مونولیت یا میکروسرویس:
از یک طرف، مونولیت (Monolith) ساده است اما اگر بد نوشته شود به "کد اسپاگتی" تبدیل میشود.
از طرف دیگر، میکروسرویس (Microservices) مقیاسپذیر است اما شما را در جهنمی از پیچیدگیهای شبکه، دیپلوی و مدیریت ۵۰ کانتینر مختلف غرق میکند.
اما اگر راه سومی وجود داشته باشه چی؟ راهی که در آن کدتان را "مثل میکروسرویس" مینویسید، اما آن را "مثل مونولیت" اجرا میکنید.
در معماری Code-level Monolith، شما مرزهای سرویسهایتان را کاملا رعایت میکنید. یعنی سرویس
اما در زمان بیلد (Build Time)، به جای اینکه آنها را در کانتینرهای جداگانه بستهبندی کنید، همه را در یک فایل اجرایی (Binary) واحد لینک میکنید.
شعار این معماری:
> *"میکروسرویس در توسعه، مونولیت در اجرا."*
جادوی Grafana Loki و Tempo
بهترین مثال زنده این معماری در دنیا، ابزارهای شرکت Grafana Labs (مانند Loki برای لاگ، Tempo برای تریس و Mimir برای متریک) هستند.
سورس کد Grafana Loki از اجزای مختلفی تشکیل شده است:
*
*
*
نکته نبوغآمیز اینجاست: همه اینها در یک کدبیس و یک فایل باینری هستند.
1. حالت (All-in-One):
وقتی میخواهید Loki را روی لپتاپ یا سرور کوچک خود اجرا کنید، دستور زیر را میزنید:
در این حالت، تمام اجزا در یک پروسه اجرا میشوند. ارتباط بین
* تاخیر: صفر نانوثانیه.
* پیچیدگی: صفر.
2. حالتِ اسکیل بالا (Microservices):
وقتی ترافیک شما میلیونی میشود، همان فایل باینری را با فلگ متفاوتی اجرا میکنید:
حالا این باینری فقط نقش Ingester را بازی میکند و بقیه کدها خاموش میشوند. در این حالت، ارتباطات به صورت خودکار به gRPC/HTTP تغییر میکند.
چرا باید به این روش فکر کنید؟
1. حذف سربار شبکه (Zero Latency):
در میکروسرویس، دادهها باید Serialize شوند، به شبکه بروند و Deserialize شوند. در Code-level Monolith، این فقط یک جابجایی اشارهگر (Pointer) در حافظه است. سرعت اجرای شما وحشتناک بالا میرود.
2. دیپلوی آسان (Operational Simplicity):
برای شروع پروژه، نیازی به Kubernetes و مدیریت ۱۰ تا فایل YAML ندارید. یک فایل باینری را کپی و اجرا میکنید.
3. انعطافپذیری (Agility):
شما امروز نمیدانید پروژه شما چقدر بزرگ میشود. با این روش، شما امروز ساده شروع میکنید، اما کدتان "Ready to Scale" است. هر زمان لازم شد، با تغییر کانفیگ، مونولیت را میشکنید.
چطور پیادهسازی کنیم؟ (مثال Go)
کلید کار در استفاده از Interface هاست.
به جای اینکه سرویس A مستقیماً با gRPC کلاینتِ سرویس B صحبت کند، با یک اینترفیس صحبت میکند.
* **در حالت Monolith: پیادهسازی اینترفیس، مستقیماً متد سرویس B را صدا میزند.
* در حالت Microservice: پیادهسازی اینترفیس، یک درخواست gRPC میفرستد.
—-
این مقاله به خوبی پیاده سازیش رو توضیح میده:
بیایید فرض کنیم اینکه برنامه میکروسرویس باشد یا مونولیت، فقط یک جزئیات پیادهسازی است
سوال:
در گوئیک کانکت چطور میشه به code level monolith رسید؟
https://github.com/syntaxfa/quick-connect
#code_level_monolith
@Syntax_fa
دوراهی مونولیت یا میکروسرویس:
از یک طرف، مونولیت (Monolith) ساده است اما اگر بد نوشته شود به "کد اسپاگتی" تبدیل میشود.
از طرف دیگر، میکروسرویس (Microservices) مقیاسپذیر است اما شما را در جهنمی از پیچیدگیهای شبکه، دیپلوی و مدیریت ۵۰ کانتینر مختلف غرق میکند.
اما اگر راه سومی وجود داشته باشه چی؟ راهی که در آن کدتان را "مثل میکروسرویس" مینویسید، اما آن را "مثل مونولیت" اجرا میکنید.
در معماری Code-level Monolith، شما مرزهای سرویسهایتان را کاملا رعایت میکنید. یعنی سرویس
Auth و سرویس Order کدهای کاملا جداگانهای دارند (درست مثل میکروسرویس).اما در زمان بیلد (Build Time)، به جای اینکه آنها را در کانتینرهای جداگانه بستهبندی کنید، همه را در یک فایل اجرایی (Binary) واحد لینک میکنید.
شعار این معماری:
> *"میکروسرویس در توسعه، مونولیت در اجرا."*
جادوی Grafana Loki و Tempo
بهترین مثال زنده این معماری در دنیا، ابزارهای شرکت Grafana Labs (مانند Loki برای لاگ، Tempo برای تریس و Mimir برای متریک) هستند.
سورس کد Grafana Loki از اجزای مختلفی تشکیل شده است:
*
Ingester (دریافت لاگ)*
Distributor (توزیع بار)*
Querier (جستجو)نکته نبوغآمیز اینجاست: همه اینها در یک کدبیس و یک فایل باینری هستند.
1. حالت (All-in-One):
وقتی میخواهید Loki را روی لپتاپ یا سرور کوچک خود اجرا کنید، دستور زیر را میزنید:
./loki -target=allدر این حالت، تمام اجزا در یک پروسه اجرا میشوند. ارتباط بین
Distributor و Ingester از طریق Function Call در حافظه رم انجام میشود.* تاخیر: صفر نانوثانیه.
* پیچیدگی: صفر.
2. حالتِ اسکیل بالا (Microservices):
وقتی ترافیک شما میلیونی میشود، همان فایل باینری را با فلگ متفاوتی اجرا میکنید:
./loki -target=ingesterحالا این باینری فقط نقش Ingester را بازی میکند و بقیه کدها خاموش میشوند. در این حالت، ارتباطات به صورت خودکار به gRPC/HTTP تغییر میکند.
چرا باید به این روش فکر کنید؟
1. حذف سربار شبکه (Zero Latency):
در میکروسرویس، دادهها باید Serialize شوند، به شبکه بروند و Deserialize شوند. در Code-level Monolith، این فقط یک جابجایی اشارهگر (Pointer) در حافظه است. سرعت اجرای شما وحشتناک بالا میرود.
2. دیپلوی آسان (Operational Simplicity):
برای شروع پروژه، نیازی به Kubernetes و مدیریت ۱۰ تا فایل YAML ندارید. یک فایل باینری را کپی و اجرا میکنید.
3. انعطافپذیری (Agility):
شما امروز نمیدانید پروژه شما چقدر بزرگ میشود. با این روش، شما امروز ساده شروع میکنید، اما کدتان "Ready to Scale" است. هر زمان لازم شد، با تغییر کانفیگ، مونولیت را میشکنید.
چطور پیادهسازی کنیم؟ (مثال Go)
کلید کار در استفاده از Interface هاست.
به جای اینکه سرویس A مستقیماً با gRPC کلاینتِ سرویس B صحبت کند، با یک اینترفیس صحبت میکند.
* **در حالت Monolith: پیادهسازی اینترفیس، مستقیماً متد سرویس B را صدا میزند.
* در حالت Microservice: پیادهسازی اینترفیس، یک درخواست gRPC میفرستد.
—-
این مقاله به خوبی پیاده سازیش رو توضیح میده:
بیایید فرض کنیم اینکه برنامه میکروسرویس باشد یا مونولیت، فقط یک جزئیات پیادهسازی است
سوال:
در گوئیک کانکت چطور میشه به code level monolith رسید؟
https://github.com/syntaxfa/quick-connect
#code_level_monolith
@Syntax_fa
👍11❤🔥5❤2🔥1