مقدمه ای دربارهی تکنولوژیهای RabbitMQ و Kafka
من ارسلان میربزرگی، میخواهم در این مقاله اطلاعات کاملی را در مورد تکنولوژیهای RabbitMQ و Kafka، کاربردها و مزیتهای آنها با شما صحبت کنم. به دلایلی، بسیاری از Developer ها بر این باورند که تکنولوژیهای RabbitMQ و Kafka میتوانند جایگزین یکدیگر شوند. اگرچه این مورد در برخی موارد صادق است، اما تفاوتهای اساسی مختلفی بین این پلتفرم ها وجود دارد.
درنتیجه، سناریوهای مختلف به راهحل متفاوتی نیاز دارند و انتخاب اشتباه ممکن است به شدت بر توانایی شما در طراحی، Develop و حفظ راهحل نرم افزاری تأثیر بگذارد.
در ادامه به معرفی الگوهای پیامرسانی Asynchronous و سپس معرفی RabbitMQ، Kafka، ساختارهای داخلی آنها، تفاوتهای اساسی بین این پلتفرمها، مزایا و معایب مختلف آنها و نحوه انتخاب بین این دو پرداخته شده است.
الگوهای پیام رسانی Asynchronous
پیام رسانی Asynchronous، یک طرح پیام رسانی است که در آن تولید پیام توسط Producers از پردازش آن توسط Consumer جدا میشود. در سیستمهای پیام رسان، معمولاً دو الگوی پیام رسان اصلی داریم: 1) صف انتظار پیام (queue) 2) انتشار / اشتراک.
صف انتظار پیام (Message queueing)
در الگوی ارتباط با روش Message-queuing، Queue ها از نظر زمانی Producer را از Consumer جدا میکنند. چندین Producer میتوانند به Queue یکسان پیام ارسال کنند. بااینحال، هنگامیکه یک Consumer پیامی را پردازش میکند، آن پیام قفل یا از Queue خارج میشود و دیگر در دسترس نیست. درواقع یک Consumer فقط میتواند پیام خاصی را پردازش کند.
اگر Consumer نتواند پیام خاصی را پردازش کند، پلتفرم پیام رسانی معمولاً پیام را به Queue ای که در دسترس سایر Consumer ها قرار گرفته است بر میگرداند. علاوه بر جداسازی زمانی، Queue ها به ما اجازه میدهند Producer ها و Consumer ها را بهطور مستقل دسته بندی کنیم. همچنین میتوانیم درجهای از Fault-tolerance را در برابر خطاهای پردازش فراهم آوریم.
انتشار / اشتراک (Publish/subscribe)
در الگوی ارتباطی انتشار / اشتراک (یا pub/sub )، یک پیام میتواند هم زمان توسط چندین Consumer دریافت و پردازش شود.
این الگو به یک Publisher اجازه میدهد، بهعنوان مثال، به همهی Subscription ها اطلاع دهد که چه اتفاقی در سیستم افتاده است.
بهطور کلی، دو نوع Subscription وجود دارد:
- Subscription زودگذر که در آن اشتراک فقط تا زمانی که Consumer فعال و در حال اجرا باشد فعال است. به محض خاموش شدن Consumer، اشتراک و پیامهای پردازش نشده وی از بین میرود.
- Subscription بادوام تا وقتی که اشتراک بهطور کامل حذف نشود، حتی اگر Consumer خاموش شد، پلتفرم پیام را حفظ میکند و پردازش پیام میتواند بعداً ادامه یابد.
RabbitMQ
RabbitMQ، پیادهسازی یک واسط تبادل پیام است که اغلب بهعنوان Service bus شناخته میشود. RabbitMQ از هر دو الگوی پیامرسانی توصیفشده در بخش قبلی پشتیبانی میکند. سایر واسط های محبوب تبادل پیام شامل ActiveMQ،ZeroMQ، Azure Service Bus و Amazon Simple Queue Service (SQS) است. همه این واسط ها اشتراکهای زیادی با هم دارند و بسیاری از مفاهیم توضیح داده شده در این بخش برای اکثر آن ها قابل استفاده است.
Queue ها
RabbitMQ از پیامهای Classic که از Queue خارج میشوند پشتیبانی میکند. یک Developer Queue های نامگذاری شده ای را تعریف میکند که Publisher ها میتوانند پیامهای خود را به آن ها ارسال کنند. Consumer ها نیز از همان Queue ها برای برداشتن پیام به منظور پردازش استفاده میکنند.
تبادل پیام (Message exchanges)
RabbitMQ با استفاده از Message exchange روش pub/sub را پیاده سازی میکند. یک Publisher بدون اینکه بداند Subscriber های این پیام ها چه کسانی هستند، پیامهای خود را در Message exchange منتشر میکند. هر Consumer که مایل به Subscribe در Exchange باشد، یک Queue ایجاد میکند. سپس Message exchange، پیامهای تولید شده را برای استفادهی Consumer در Queue قرار میدهند. همچنین پیامها برای برخی Subscriber ها، بر اساس Rule هایی که تعیین میکنیم، فیلتر میشوند.
توجه داشته باشید که RabbitMQ از هر دو Subscription زودگذر و بادوام پشتیبانی میکند. Consumer میتواند نوع Subscription موردنظر خود را که میخواهد از طریق RabbitMQ’s API استفاده کند، تعیین کند.
همچنین با توجه به معماری RabbitMQ، میتوانید یک رویکرد ترکیبی ایجاد کنید. به این معنا که برخی از Subscriber هایی که باهم کار میکنند. باهم گروه های Consumer به شکل Consumer های رقابتی، برای پردازش یک Queue خاص تشکیل میدهند. بهاینترتیب، الگوی pub/sub پیادهسازی شدهاست. در عین حال به برخی از مشترکان اجازه داده میشود معیار خود برای مدیریت پیامهای دریافتی را ارائه دهند.
Apache Kafka
Apache Kafka یک واسطهی تبادل پیام نیست، بلکه یک Streaming platform توزیع شده است. برخلاف RabbitMQ که بر اساس Queue و Exchange است، لایه ذخیرهسازی Kafka با استفاده از یک Transaction log تقسیم شده اجرا میشود. Kafka همچنین یک Streams API برای پردازش Stream ها در Real time و یک Connectors API برای یکپارچه سازی آسان با منابع مختلف داده فراهم میکند.
فروشندگان Cloud راهحلهای جایگزینی برای لایه ی ذخیرهسازی Kafka ارائه میدهند. این راهحلها شامل Azure Event Hubs و AWS Kinesis Data Streams هستند. همچنین گزینههای Cloud-specific و Open-source برای قابلیت پردازش Stream در Kafka وجود دارد.
Topic
Kafka مفهوم Queue را اجرا نمیکند. در عوض، مجموعهای از Record ها را در دستههایی بانام Topic ذخیره میکند. برای هر Topic، Kafka یک گزارش پارتیشن بندی شده از پیامها را حفظ میکند. هر پارتیشن یک ترتیب خاص و تغییر ناپذیر از Record ها است که پیامها بهطور مداوم به آن اضافه میشوند.
Kafka هنگام ورود پیامها آنها را به پارتیشنهایشان اضافه میکند و به طور پیشفرض، برای پخش یکنواخت پیام ها در میان پارتیشنها از Partitioner از نوع Round-robin استفاده میکند.
Producer ها میتوانند این کار را برای ایجاد Logical stream پیام تغییر دهند. به عنوان مثال، در یک برنامهی Multitenant، ممکن است بخواهید Logical stream پیام را با توجه به Tenant ID هر پیام ایجاد کنید. در یک سناریوی IoT، ممکن است نیاز شود Identity map هر Producer به طور مداوم در یک پارتیشن خاص وجود داشتهباشد. باید اطمینان حاصلشود که برای Consumer، تحویل همهی پیامهایی که از Logical stream یکسان به پارتیشن یکسان میروند تضمین میشود.
Consumer ها با ثبت و نگهداری Offset Indx در پارتیشنها، پیامها را بهصورت پیدرپی خوانده و استفاده میکنند. یک Consumer میتواند چندین Topic را استفاده کند و تعداد پارتیشنهای موجود را درجه بندی کنند. در نتیجه، هنگام ایجاد یک Topic، باید میزان انتظار توان عملیاتی از Messaging در آن Topic را بهدقت بررسی کرد. گروهی از Consumer ها که برای استفاده از یک Topic باهم کار میکنند، گروه Consumer نامیده میشود. Kafka’s API بهطورمعمول تعادل پردازش پارتیشن بین Consumer ها در یک گروه Consumer و ذخیرهی Offset های فعلی Consumer ها را کنترل میکند.
پیادهسازی الگوهای پیامرسانی با Kafka
Map های پیادهسازی Kafka کاملاً مطابق با الگوی pub/sub است. Producer میتواند به یک Topic خاص پیام ارسال کند و چندین گروه Consumer میتوانند از یک پیام یکسان استفاده کنند. هر گروه Consumer میتواند به صورت جداگانه برای مدیریت Load، دسته بندی شود. از آنجا که Consumer ها، Offset مربوط به پارتیشن خود را ثبت میکنند، میتوانند یک Subscription بادوام داشته باشند که در هنگام Restart یا Subscription زودگذر، Offset خود را حفظ کند. پس از هر بار که شروع به کار میکند، Offset را از بین برده و از آخرین رکورد در هر پارتیشن Restart کند.
با اینحال، برای الگوی Message-Queuing استفاده از Kafka خیلی مناسب نیست. البته، برای رقابت با الگوی Classic message queuing فقط میتوان یک Topic با یک گروه Consumer داشت؛ که باز هم این مشکلات متعددی در پی دارد.
Kafka بدون در نظر گرفتن اینکه Consumer ها از پیامها استفاده کردهاند یا خیر، پیامها را تا یک دوره ی زمانی از پیش تعریف شده در پارتیشنها نگه میدارد. این به این معنی است که Consumer ها میتوانند پیامهای قدیمی را دوباره بخوانند. علاوه بر این، Developer ها میتوانند از لایه ی ذخیرهسازی Kafka برای پیادهسازی مکانیسمهایی مانند Event sourcing و audit Logs استفاده کنند.
و در آخر
درحالی که RabbitMQ و Kafka گاهی قابل جایگزینی هستند، اما پیادهسازی و اجرای آنها با یکدیگر بسیار متفاوت است. درنتیجه، نمیتوانیم آنها را باهم در یک گروه از ابزارها قرار دهیم. یکی از آنها واسطه پیام است و دیگری یک Streaming platform توزیع شده است. در نتیجه باید این تفاوتها را شناخته و سپس بررسی کنیم که برای یک مسئلهی خاص کدام یک از این راهحلها را استفاده کنیم.