Message Broker (کارگزار یا واسط پیام)، نرمافزاری است که برنامهها، سیستمها و خدمات را قادر میسازد با یکدیگر ارتباط برقرار کرده و اطلاعات مبادله کنند. Brokerهای پیام از فناوریهای مهم مرتبط با وب هستند. یک Broker پیام OSS خوب (OSS شامل نرمافزار و سختافزاری است که موجب ادغام سیستمها و فرآیندهای تجاری میشود) باید اصول نسبتاً سادهای را رعایت کند:
- وقتی تعداد زیادی پیام پشت سر هم وجود دارد رفتار مناسبی داشته باشد.
- بتواند یک cluster (خوشه) ایجاد کند.
- در صورت خرابی یک گره (node یا گره، یک دستگاه یا نقطهی داده در یک شبکهی بزرگتر است) در یک خوشه، سعی کند از دادهها محافظت کند.
- هرگز ناشران را مسدود نکند، حتی اگر این کار باعث از دست دادن دادهها بشود.
در ادامه این مقاله چند broker پیام با معیارهای مطرح شده را معرفی کرده و توضیح مختصری در مورد هر یک ارائه می دهیم:
RabbitMQ
RabbitMQ یک Broker پیام معروف و محبوب است و ویژگیهای قدرتمند زیادی دارد. مستندات وب سایت RabbitMQ عالی است و کتابهای زیادی برای آن در دسترس است. RabbitMQ به زبان Erlang نوشته شده است که یک زبان برنامه نویسی پرکاربرد نیست، اما به خوبی با چنین وظایفی سازگار است. شرکت Pivotal توسعه و نگهداری RabbitMQ را انجام میدهد. در این مقاله نسخهی 3.2.2 در سرورهای CentOS 6 بررسی شده است.
(برای آشنایی بیشتر با RabbitMQ و نحوه کار آن می توانید به مقاله یک راهنمای سریع برای درک RabbitMQ در سایت سکان آکادمی مراجعه کنید.)
ویژگیهای RabbitMQ:
- نصب ربیت ام کیو آسان است. ما نسخهی R14B زبان Erlang را از epel و RabbitMQ rpm نصب کردیم. تنها مشکل کوچکی که وجود داشت این بود که سرور انتظار دارد "127.0.0.1" در etc/hosts/ گنجانده شود که ماشینهای مجازی OpenStac ای که ما استفاده کردیم نتوانستند این کار را بکنند.
- تعمیر و رفع خطای آن نیز آسان است. همچنین افزونهی مدیریت را هم نصب و فعال کردیم.
پیکربندی RabbitMQ در فایل rabbitmq.config انجام شده است و دارای تعداد زیادی پارامتر قابل تنظیم است. ما از مقادیر پیشفرض استفاده کردیم. از نظر API کلاینت، RabbitMQ از لیستی طولانی از زبانها پشتیبانی میکند و برخی از پروتکلهای استاندارد مانند STOMP (یک پروتکل ساده مانند HTTP است که برای تعامل با Broker پیام بکار میرود) همراه با یک افزونه، در دسترس هستند. صفها و موضوعات را میتوان با رابط وب یا مستقیماً از طریق API کلاینت ایجاد کرد. اگر بیش از یک node دارید، میتوان آنها را خوشهبندی کرد و سپس صفها و موضوعات را در سرورهای دیگر کپی کرد.
ما چهار صف ایجاد کردیم، یک کلاینت Ruby نوشتیم و شروع کردیم به درج پیام و با استفاده از رشتههای متعدد، نرخ انتشاری در حدود 20k/s دریافت کردیم، اما با توجه به آنچه فهمیدیم، به دلیل vm_memory_high_watermark (یک مقدار درصدی مربوط به کنترل جریان حافظه در RabbitMQ است) با چند مشکل در نوشتن روی دیسک مواجه شدیم که با توجه به نیازهای ما اتفاق خوبی نیست. همچنین، برخی از قسمتها حتی اگر یک صف بادوام (durable) باشند همیشه در حافظه نگه داشته میشوند؛ بنابراین، با وجود این که فضای دیسک زیادی داشتیم، استفاده از حافظه افزایش یافت و در نهایت به معیار vm_memory_high_watermark رسید. همچنین بار CPU در طول بارگذاری بسیار زیاد بود (بین 40٪ تا 50٪ در ماشین مجازی 8 هستهای).
اگرچه نیازهای ما برآورده نشد، اما یک صف تکراری را روی دو node تنظیم کردیم و چند میلیون شی را درج کردیم. یکی از دو node را کنار زدیم و درج حتی سریعتر شده بود؛ اما بعد متوجه شدیم که اشتباه کردیم. ما node را دوباره راه اندازی کردیم و درخواست همگام سازی مجدد دادیم، ولی یا ما آن را به درستی تنظیم نکردیم یا همگامسازی مجدد، ضعیف اجرا شده بود، اما همگامسازی مجدد بسیار طول کشید و هرچه پیشرفت میکرد کندتر میشد. تا 58%، 17 ساعت در حال اجرا بود.
از مزایای آن میتوان به ویژگیهای بسیار و عملکرد مناسب اشاره کرد، اما رفتار آن سازگار و برآوردهکننده انتظارات نیست.
Kafka
Kafka، در اصل توسط LinkedIn طراحی شده است. این Broker به زبان جاوا نوشته و به بنیاد نرمافزار آپاچی اهدا شده است. گاهی اوقات شما به یک فناوری نگاه میکنید و میبینید که هر آنچه نیاز دارید در آن رعایت شده. در مورد Kafka حداقل برای هدفی که داشتیم، میتوانستیم این را بگوییم. چیزی که در مورد کافکا بسیار خاص است، معماری آن است، پیامها را در فایلهای مسطح (فایل مسطح یا Flat File نوعی پایگاه داده است که دادهها را در قالب متن ساده ذخیره میکند) ذخیره میکند و مصرفکنندگان (consumers) پیامهایی را بر اساس اُفسِت (offset یک شناسه منحصر به فرد است که به پارتیشن ها اختصاص داده شده است) query (درخواست) میکنند. آن را مانند یک سرور MySQL (تولیدکننده) در نظر بگیرید که پیامها را در binlogs خودش ذخیره میکند (SQL را بهروزرسانی میکند) و slaveهای (پارتیشن هایی که به تبع پارتیشن لیدر کار میکنند) مصرفکننده پیامهایی را بر اساس افست، درخواست میکنند.
این سرور بسیار ساده است و فقط به مصرفکنندگان اهمیت نمیدهد. همین سادگی آن را فوقالعاده سریع و کممصرف میکند. پیامهای قدیمی را میتوان بر اساس زمان (مانند expire_logs_days) و یا بر اساس استفاده از فضای ذخیرهسازی حفظ کرد.
اگر سرور آنچه که در هر موضوع مصرف (consume) شده، پیگیری نکند، چگونه میتوانید چندین مصرفکننده داشته باشید؟ Zookeeper عنصر مورد نیاز ما است. سرور کافکا از Zookeeper برای عضویت در خوشه و مسیریابی استفاده میکند در حالی که مصرفکنندگان میتوانند از Zookeeper یا چیز دیگری برای همزمانی نیز استفاده کنند. نمونهی مصرفکنندهای که با سرور ارائه شده، از Zookeeper استفاده میکند، بنابراین شما میتوانید بسیاری از نمونهها را راهاندازی کنید و آنها به طور خودکار همگام سازی میشوند. برای کسانی که Zookeeper را نمیشناسند، Zookeeper یک سیستم ذخیرهسازی توزیعشدهی همزمان بسیار در دسترس است. اگر Corosync را میشناسید، Zookeeper تا حدودی همان عملکرد را ارائه میدهد.
از نظر ویژگی و قابلیت، Kafka آنقدرها هم عالی نیست. هیچ بخش فرانتاند داخلیای برای آن وجود ندارد، اگرچه تعداد کمی در اکوسیستم آن موجود است. مسیریابی و قوانین خاصی وجود ندارد و آمار بدست آمده فقط با JMX است. اما در مورد عملکرد، ما به سرعت انتشار 165 هزار پیام در ثانیه در یک رشته، و در کل به 3M پیام در ثانیه رسیدیم که شگفت انگیز است و این بدون هماهنگی Zookeeper بود. همچنین حافظه و استفاده از CPU متوسط بود.
برای تست خوشهبندی، یک صف تکراری ایجاد، چند پیام را درج، کپی را متوقف و چند میلیون پیام دیگر را وارد کردیم و کپی را دوباره راهاندازی کردیم. فقط چند ثانیه تا همگامسازی دوباره، طول کشید.
بنابراین، Kafka به خاطر عملکرد درخشان، استفاده کم از منابع و تناسب خوب با انتظارات، بسیار مناسب است.
ActiveMQ
ActiveMQ یکی دیگر از broker های منتخب در این زمینه با مجموعهای از ویژگیهای چشمگیر است. ActiveMQ بیشتر شبیه به RabbitMQ است تا Kafka و مانند Kafka به زبان جاوا نوشته شده است. HA (High availability یا دسترسی بالا، سیستم هایی را توصیف میکند که به اندازه کافی قابل اعتماد هستند تا به طور مداوم بدون از کار افتادگی کار کنند) را میتوان توسط backend ذخیرهسازی و ارائه کرد، levelDB از تکرار پشتیبانی میکند، اما با آن مشکلاتی داشتیم. الزامات و انتظارات ما برای HAی کامل نیست، ما نسخهی پشتیبان ذخیرهسازی را فقط برای اطمینان از اینکه ناشران هرگز مسدود نمیشوند، به نفع شبکهای از brokerها حذف کردیم.
(برای آشنایی بیشتر با این کارگزار پیام، می توانید به مقاله آشنایی با معماری ActiveMQ مراجعه کنید.)
درک ما از مِشِ (mesh یا شبکه، brokerهای متعددی را ارائه میدهد که همه به یکدیگر متصل هستند) brokerها این است که شما به یکی از اعضا متصل میشوید و پیامی را منتشر یا مصرف میکنید. شما نمیدانید که صف در کدام node قرار دارد، brokerی که به آن متصل میشوید، درخواست شما را میداند و مسیریابی میکند. برای کمک بیشتر، میتوانید تمام واسطهها را در رشتهی اتصال مشخص کنید و اگر brokerی که به آن متصل هستید از کار بیافتد، کتابخانهی مشتری، دوباره به یک broker دیگر متصل میشود. این ویژگی برای نیازمندی ها و انتظارات ما بسیار خوب به نظر میرسد.
با راه اندازی مشِ brokerها، نرخ درج حدود 5000 پبام در ثانیه را در 15 رشته دریافت کردیم و یک مصرفکننده منفرد توانست 2000 پبام در ثانیه را بخواند. اجازه دادیم مدتی اجرا شود و 150 میلیون پیام دریافت کردیم. در این مرحله، رابط وب را از دست دادیم و سرعت انتشار بسیار کندتر شده بود.
بنابراین، ActiveMQ همراه با بسیاری از ویژگیها و عملکرد مناسب، بسیار نزدیک به انتظارات ما میباشد.
Kestrel
Kestrel، یک broker جالب دیگر است که این بار بیشتر شبیه Kafka است. broker Kestrel که در Scala نوشته شده است، پروتکل memcached را بیان میکند. اساساً کلید به عنوان نام صف و object یا شیء به عنوان پیام نامیده میشود. Kestrel بسیار ساده است: صفها در یک فایل پیکربندی تعریف میشوند، اما میتوانید در هر صف، محدودیتهای ذخیرهسازی، انقضا و رفتار زمانی که به محدودیتها رسیدید را مشخص کنید. با تنظیمی مانند «discardOldWhenFull = true»، نیاز ما برای مسدود نکردن ناشران به راحتی برآورده میشود.
از نظر خوشهبندی، Kestrel کمی محدود است، اما هر کدام میتوانند در دسترس بودن خود را در Zookeeper منتشر کنند تا ناشران و مصرفکنندگان بتوانند از یک سرور از دست رفته، مطلع شوند و خودشان را تنظیم و سازگار کنند. البته، اگر سرورهای Kestrel زیادی با همان صف تعریف شده دارید، مصرفکنندگان باید از همهی brokerها، query کنند تا پیام را بازپس بگیرند و مرتب سازی دقیق ممکن است کمی سخت باشد.
از نظر عملکرد، چند اسکریپت bash ساده با استفاده از nc، برای انتشار پیامها به راحتی به 10k پیام رسیدند که بسیار خوب است. نرخ (rate) در طول زمان ثابت است و احتمالاً با اتصال مجدد برای هر پیام محدود میشود. حضور مصرفکنندگان کمی نرخ انتشار را کاهش میدهد اما مورد شدید و چشمگیری نیست. تنها مشکلی که داشتیم این بود که تعداد زیادی از پیامها منقضی شدند و سرور برای مدتی متوقف شد، اما به این دلیل بود که فراموش کردیم maxExpireSweep را روی مقداری در حدود 100 تنظیم کنیم و همهی پیامها در یک pass (عملیات تهی)، حذف شدند.
بنابراین، بازخورد نسبتاً خوبی از Kestrel گرفته شد، Kestrel ساده است اما به خوبی کار میکند.
جمع بندی
در این مقاله به معرفی 4 مورد از مشهورترین پیام رسان های معروف پرداختیم که هر کدام مزایا، معایب و سختی های خاص خود را دارند. شاید بتوان گفت Kafka به دلیل عملکرد بالا، تضمین در دسترس بودن و مسدود نشدن تحت هر شرایطی، بهترین انتخاب است. شاید هم انتخاب شما RabbitMQ باشد که سبک تر از Kafka بوده و به دلیل سیستم سادهی Pub/Sub (تولید کننده / مصرف کننده پیام) انتخاب بهتری باشد.
اما یکی دیگر از message broker هایی که اخیرا مورد توجه توسعه دهندگان زیادی قرار گرفته است و مزایای زیادی دارد، NATS است. NATS، یک سیستم پیامرسانی سریع و منبع باز است که دارای یک هسته ساده و در عین حال قدرتمند می باشد. برای بررسی بیشتر در مورد این پیام رسان می توانید به مقالهی NATS چیست؟ در سکان پلاس مراجعه کنید.