ارتباط مایکرو سرویس های لاراولی با RabbitMQ

ارتباط مایکرو سرویس های لاراولی با RabbitMQوقتی از معماری یکپارچه (Monolith) استفاده می‌کنیم، توسعه دادن پروژه های بزرگ دست و پاگیر می‌شود. به عنوان یک توسعه دهنده لاراول ، این نکته کاملا چالش برانگیز است; زیرا لاراول به ما یک محیط توسعه کاملا فول استک را ارائه می دهد که در آن می توانیم با Front-end و همچنین Back-end در همان پروژه کار کنیم ، به عبارت دیگر یک برنامه یکپارچه (Monolithic application) داشته باشیم. اما اگر بخواهیم از معماری مایکرو سرویس ها بهره ببریم، مراحل کار به چه شکل خواهد بود؟ و ارتباط سرویس های ما به چه شکل برقرار می‌شود؟ در این مقاله با هم بررسی می‌کنیم.قبل از آنکه ادامه دهیم، لازم است تا به چند نکته دست و پا گیر در مورد معماری یکپارچه اشاره کنیم. در این معماری چون تمام عملکرد ها (functionalities) داخل یک پروژه هستند، حتی با کوچکترین تغییراتی باید اپلیکیشن را دوباره دپلوی کنیم، که ممکن است روی کل برنامه تأثیر بگذارد. در مقیاس بالا نگهداری چنین پروژه های بزرگی، مشکل تر می‌شود. در اینجاست که معماری مایکرو سرویس ها به کمک ما می‌آید و می‌توانیم عملکرد ها را به کامپوننت ها یا اجزای مستقل تفکیک کنیم..مسلما معماری مایکرو سرویس ها مناسب هر پروژه ای نیست.. اگر شما در حال خواندن این مقاله هستید، من فرض را بر این گرفته ام که پیشاپیش می‌دانید محل صحیح استفاده از این معماری کجاست و همچنین با نقاط ضعف و قوت آن آشنایی کامل دارید. پس بدون اتلاف وقت سراغ توضیح دادن یکی از قسمت های اصلی در این معماری می‌رویم.هنگام کار با این معماری باید زیرساختی را برای ارتباطات داخلی سرویس های خود مهیا کنیم. ۳ روش مطرحی که برای این مهم می‌توان استفاده کرد عبارتند از:وب سرویس های REST یا (Representational State Transfer)کانال RPC یا (Remote Procedure Call)مسیج بروکر ها (ابزارهایی مانند Apache Kafka ، RabbitMQ و غیره)ما در این مقاله قصد داریم استفاده از مسیج بروکر RabbitMQ را در معماری مایکرو سرویس ها بررسی کنیم. یک "مسیج بروکر" به عنوان یک واسطه برای سرویس های ما عمل می‌کند ، پیام ها را از یک سرویس (Publisher یا تولید کننده) دریافت می کند و آنها را برای انجام کار به دیگر سرویس ها (Subscriber یا مصرف کننده) تحویل می دهد. (در مقالات مختلف ممکن است این ۲ بخش Producer و Consumer نیز خوانده شوند)خوبی استفاده از RabbitMQ این است که پیام های ما را داخل یک صف قرار می‌دهد. پس اگر Subscriber مشغول باشد و یا ارتباط آن با شبکه قطع شده باشد، پیام ما در حافظه موقت RabbitMQ ذخیره می‌شود و هر زمان Subscriber دوباره آنلاین شود، پیام از حافظه موقت به مصرف کننده تحویل داده می‌شود.مطالبی که بررسی خواهیم کرد، شامل موارد زیر هستند:هدفسرویس ابری AMQPراه اندازی پروژه لاراولیاجرا کردن جاب Pingاجرا کردن جاب Userبررسی بهبود هاهدفبیایید با نتیجه آنچه در این مقاله خواهیم ساخت شروع کنیم. ما باید ۲ سرویس مجزا آماده کنیم. یکی از آنها به عنوان مصرف کننده یا Consumer عمل می کند و به هر پیام دریافتی گوش می دهد. و دیگری به عنوان تولید کننده ، Publisher و یا ناشری عمل می کند که برخی پیام ها را منتشر می‌کند.RabbitMq Services Testهمانطور که مشاهده می کنید ، ترمینال سمت راست به عنوان تهیه کننده / ناشر فعالیت می کند. ابتدا یک جاب به نام پینگ از طریق یک کامند در کنسول اجرا می‌شود ، و سپس جاب دیگری به نام یوزر اجرا شده و اطلاعات کاربر را از سرویس ۱ به سرویس ۲ منتقل می‌کند. پس وقتی مصرف کننده اطلاعاتی را از ناشر دریافت می‌کند، می‌تواند از آن اطلاعات در سیستم خودش استفاده کند.مسلما برای استفاده از RabbitMQ باید این ابزار را روی سرور و یا محیط توسعه خود نصب کنید، اگر قبلا این ابزار رو نصب کرده اید که هیچ و اگر هم نه، در پایان این مقاله به راحتی RabbitMQ را با استفاده از داکر راه اندازی می‌کنیم.سرویس ابری AMQPعلاوه بر نصب RabbitMQ روی سیستم لوکال یک راه استفاده دیگر هم داریم و آن استفاده از سرویس ابری AMQP به عنوان یک سرویس است که میتوانید از وب سایت CloudAMQP.com بهره ببرید. استفاده از این سیستم بسیار ساده است. کافیه ابتدا ثبت نام کنید و یک Instance برای خودتون بسازید. پس از طی این مراحل یک آدرس هاست، یوزر و پسورد و یک vhost دریافت می‌کنید که میتونید بلافاصله ازش استفاده کنیدcloudAMQPاین سرویس همچنین امکان استفاده از ابزار مدیریت RabbitMQ رو به شما میده، که می‌تونید از طریقش کانال های مختلفی بسازید و کانکشن ها و صف ها رو مدیریت کنید.cloudAMQP Managerراه اندازی پروژه لاراولینصب فریم‌ ورک لاراول رو بار ها با هم بررسی کردیم.. پس نصب ۲ پروژه مجزای لاراولی رو به عهده خودتون می‌گذارم. پس از نصب لاراول احتیاج داریم تا پکیج کارآمد vyuldashev/laravel-queue-rabbitmq رو که توسط Valdimir Yuldashev توسعه داده شده رو روی هر دو پروژه نصب کنیم:Installing RabbitMQ packageحال فایل config/queue.php را باز کرده و یک درایور جدید برای RabbitMQ تعریف کنید.Define a new Queue Connectionحالا باید چند متغیری که در تنظیمات بالا تعریف شد را در فایل ENV بگذارید و مقدار دهی کنید. همچنین باید کلید QUEUE_CONNECTION را برابر با نام کانکشنی که در بالا ساختیم (rabbitmq) ، قرار بدهید.Environment file Changesتنها نکته باقیمانده در این بخش این است که این تنظیمات را باید روی هر ۲ پروژه لاراولی اعمال کنید.اجرا کردن جاب Pingدر این قسمت ۲ جاب مختلف میسازیم تا عملکرد هر ۲ سیستم را تست کنیم. دقت کنید که هر کدام از جاب ها باید در داخل هر ۲ پروژه ساخته شوند. (که علتش رو توضیح می‌دهم) پس ابتدا دستور زیر رو در هر ۲ پروژه اجرا کنید:Make the Ping jobدر سرویس Publisher (سرویس ۱) بدنه این جاب خالی است و داخل متد handle هیچ کدی وجود نداره:Handle method in Ping job (Service 1)و در سرویس Subscriber (سرویس ۲) با استفاده از دستور اکو داریم دریافت شدن یک رویداد (Event) رو شبیه سازی می‌کنیم:Handle method in Ping job (Service 2)این در حالیست که در واقع داریم این رویداد رو از طریق سرویس ۱ اجرا و فراخوانی می‌کنیم. حال برای اینکه در سرویس ۲ بتوانیم به رویدادهای سرویس ۱ گوش کنیم، کافی است دستور زیر را در ترمینال سرویس ۲ اجرا کنیمArtisan Consume Commandحالا که سرویس ۲ آماده است، میتوانیم جاب PingJob رو از طریق سرویس ۱ اجرا (Dispatch) کنیم.راه اول دیسپچ کردن این جاب استفاده از Tinker و اجرای مستقیم این دستور هست:Dispatching the Ping jobو راه دوم دیسپچ کردن این جاب ساخت یک کامند Artisan و اجرای آن در کنسول هست:PingJob Commandحالا میتونید با اجرای دستور زیر در کنسول جاب رو اجرا کنید: Running PingJob Commandاجرا کردن جاب Userجاب قبلی مثال بسیار ساده ای بود، اما ما همچنین میتونیم داده ها رو به جاب ها پاس بدهیم و در سرویس Subscriber از اون داده ها استفاده کنیم. (کاری که در این جاب انجام خواهیم داد) پس در هر ۲ پروژه دستور زیر رو اجرا می‌کنیم:Make the User jobدرست مثل جاب قبلی، بدنه متد handle جاب ما در سرویس ۱ خالی است و تنها کاری که انجام میدیم، پاس دادن داده ها از طریق متد سازنده کلاس هست:Handle method in User job (Service 1)حالا در سرویس ۲ داخل متد handle داده ها رو به فرمت Json تبدیل میکنیم و اکو می‌کنیم... Handle method in User job (Service 2)<پرانتز>این جا (متد handle)‌ نقطه ای از کدهای ماست که میتونه اتفاق های جالبی بیفته، مثلا:یه سری داده توی دیتابیس ذخیره یا آپدیت بشه.یا یک Event داخلی در اپلیکیشن Trigger بشه و زنجیره ای از دستورات اجرا بشند.یا اینکه از مایکرو سرویس های دیگری استفاده کرد و سایر کارها رو به عهده اونها گذاشت. <پرانتز/>بسیار خوب، ادامه میدیم. برای اجرای جابی که ساختیم ، این بار احتیاج داریم تا از طریق متد سازنده اطلاعات کاربر رو در سرویس ۱ به جاب پاس بدیم. (به فرض اینکه حداقل ۱ کاربر توی دیتابیس داشته باشیم) پس از محیط Tinker استفاده میکنیم و دستورات زیر رو اجرا میکنیم:Dispatching the User jobتوجه داشته باشید که در سرویس ۲ باید کامند rabbitmq:consume اجرا شده باشه.. نکته دیگه اینکه در کد بالا داده ای (متغیر user) رو که به کلاس جاب پاس میدیم از نوع آرایه هست; اما چرا؟اگر آبجکت یک user رو پاس بدیم (مثلا با id شماره ۲) و چنین رکوردی داخل سرویس ۲ موجود نباشه، با یک اکسپشن روبرو میشیم چون که داده ها نمی‌تونند unserialize بشند. پس بهتره که داده ها رو به شکل آرایه پاس بدیم. همون طور که برای جاب اول یک دستور Artisan ساختیم، این کار رو برای جاب دوم هم انجام میدیم، تا بتونیم از کنسول این جاب رو اجرا کنیم.. حالا میتونیم داده ها رو به راحتی به مایکرو سرویس هامون ارسال کنیم.RabbitMq Services Testبررسی بهبود هاهندلر های کاستومشاید این سوال براتون مطرح بشه که چرا باید یک جاب رو در هر ۲ سرویس بسازیم؟ مشکل اینه که اگر ما جابی رو در سرویس Publisher دیسپچ کنیم و اون جاب در سرویس Consumer موجود نباشه، با اکسپشن روبرو میشیم چون اون کلاس/آبجکت در اون پروژه موجود نیست. اما یک راه حل هم برای این موضوع وجود داره. اگر سری به فایل config/queue.php و بخش درایور rabbitmq بزنید، این امکان وجود داره که از کلاس جاب کاستوم خودتون استفاده کنید. (هر رویداد یا پیامی که به سرویس گیرنده برسه، توسط کلاس جاب کاستوم خودتون مدیریت میشه)Custom Job Handlerبرای استفاده از کلاسهای جاب کاستوم، می‌تونید به مستندات پکیج در گیت هاب مراجعه کنید.۲. صف های سفارشیویژگی جالب دیگر صف های سفارشی هستند. می‌توانیم پیامی را در تنها یک صف خاص منتشر کنیم و یا به یک مایکرو سرویس ارسال کنیم. تنها لازم است تا یک صف سفارشی بسازید.RabbitMQ Custom Queuesاگر می خواهید برای یک مایکرو سرویس مشخص کنید که یک صف خاص، به عنوان مثال یک صف سفارشی را هدف قرار دهد ، کلید زیر را به فایل ENV اضافه کنید.Custom Queue Environmentسپس سرویس Consumer شما تنها به پیامی که روی صف سفارشی ارسال شود، گوش می‌دهد. حال برای ارسال پیام از سرویس Publisher روی یک صف سفارشی می‌توان از متد onQueue(‘queuename’) استفاده کرد.Dispatching a job on a specific queue۳. داکرایز کردن RabbitMQساده ترین روش اجرای RabbitMQ (مخصوصا در محیط توسعه) استفاده از داکر است. کافی است داکر روی سیستم شما نصب باشد، سپس می‌توانید به راحتی RabbitMQ را با دستور زیر بالا بیاورید:Dockerizing RabbitMQحال کافی‌ است به پورت 15672 روی localhost مراجعه کنید و پنل مدیریت صف های RabbitMQ را ببینید.۴. استفاده از Laravel Horizonاگر با Horizon آشنا باشید، میدانید که این پنل برای مدیریت صف های مربوط به Redis استفاده می‌شود، نکته جالب که شاید ندانید این که این پنل به راحتی با RabbitMQ نیز سازگار است. تنها کاری که باید انجام دهید افزودن کلید زیر به فایل ENV است.Laravel Horizon