وظیفه و هدف اصلی Apache Kafka، ارائه یک بستر برای مدیریت و کنترل جریانهای اطلاعاتی با کارآیی بسیار بالا، در سیستمها و زیرسیستمهای مختلف است. یعنی شما میتوانید با ایجاد کردن یک Pipeline برای جریان اطلاعات خود، وابستگی مستقیم سیستمها و زیرسیستمها را از بین ببرید؛ آن هم بصورتی که بروز مشکلی در هر قسمت، کمترین میزان تاثیر را در سایر قسمتها داشته باشد.
فرض کنید شما تعداد زیادی سیستم و زیرسیستم مختلف را داشته باشید که هر کدام از آنها نیازمند ارتباط با برخی از قسمتهای دیگر است. در این صورت شما دو راه دارید: اول اینکه در هر قسمت سرویسهایی را برای ارتباط با سایر قسمتها پیاده سازی کنید یا اینکه هر قسمت بصورت مستقیم با سایر قسمتها در ارتباط باشد.
مشخصا کنترل و مدیریت جریان اطلاعاتی در این پیاده سازی کار بسیار دشواری است. تغییر هر قسمت، تاثیر مستقیمی بر روی سایر قسمتها دارد و در صورتی که هریک از قسمتها با مشکلی روبرو شوند، سایر قسمتهای مرتبط نیز با مشکل روبرو میشوند. این مشکل زمانی بسیار نمایان میشود که در معماریهایی مانند میکروسرویس، بدلیل بالا رفتن تعداد زیرسیستمها و ارتباطات آنها، مدیریت این ارتباطات کار بسیار دشوار، پرهزینه و پیچیدهای میشود.
روش Apache Kafka برای رفع مشکل فوق به این صورت است که Kafka با بر عهده گرفتن مدیریت ارتباطات و جریان دادهای قسمتهای مختلف، به شما کمک میکند تا تیم پیاده سازی، تنها تمرکزشان را بر روی Business ی که میخواهند پیاده سازی کنند، قرار دهند. با این روش میتوانیم به راحتی سیستمهایی را پیاده سازی کنیم که از نظر ارتباطی در حالت معمول، پیچیده یا بسیار پیچیدهاند.
مشاهده کنید :
همانطور که میبینید دیگر نیازی نیست تا قسمتهای مختلف بصورت مستقیم با یکدیگر در ارتباط باشند؛ تمامی ارتباطات از طریق Kafka انجام میشود. تغییر یک قسمت، تاثیر زیادی بر روی سایر قسمتها ندارد یعنی دسترس خارج شدن یا بروز هر گونه مشکلی در یک قسمت، بر روی کل سیستم تاثیر زیادی ندارد. پیامهای مربوط به یک قسمت تا زمانی که پردازش نشدهاند از بین نمیروند؛ پس سیستمها میتوانند در حالت Offline نیز به کار خود ادامه دهند. شما میتوانید در این روش تمامی قسمتهای سیستم را بصورت یک Cluster پیاده سازی کنید. در اینصورت احتمال از دسترس خارج شدن هر قسمت به کمترین میزان میرسد. حتی درصورتی که یک قسمت بصورت موقت از دسترس خارج شود، پیامهای مرتبط با آن قسمت تا زمانی که دوباره به جریان پردازش بازگردد، از بین نمیروند بلکه پس از اضافه شدن قسمت از دسترس خارج شده، بلافاصله تمامی پیامهای مرتبط با آن قسمت برایش ارسال میشوند. برای بالا رفتن میزان کارآیی و تحمل خطا، به راحتی میتوانید خود Kafka را نیز بصورت یک Cluster پیاده سازی کنید و با بالا رفتن تعداد درخواست، در صورت نیاز میتوانید عملیات مقیاس پذیری افقی را به راحتترین روش ممکن انجام دهید.
توسعه پذیری یا همان Scalability که بعضا به آن مقیاس پذیری هم گفته می شود یک تعریف بسیار ساده دارد: سیستمی که بتواند خواسته های در حال افزایش را پاسخ دهد. برای مثال شما یک نرم افزار دارید که بر روی یک سرور قرار دارد
(مثلا یک وب سایت) و روزانه ۱۰۰بازدید کننده از وب سایت شما دیدن می کنند اما بعد از مدتی این ۱۰۰بازدید کننده در روز به ۱۰هزار بازدید کننده میرسید، در صورتیکه سرور شما بتواند با هزینه ی به نسبت معقولی این ۱۰هزار بازدید کننده را مدیریت کند، شما یک سیستم Scalable یا توسعه پذیر خواهید داشت. مثلا فرض کنید، برای ۱۰۰بازدید در روز، شما مبلغ ۱۰هزار تومان در ماه پرداخت میکردید. حالا که بازدید شما ۱۰هزار نفر شده (یعنی ۱۰۰برابر)، هزینه ی شما نباید ۱۰۰برابر شود. مثلا هزینه شما به جای ۱۰هزار تومان در ماه، بایستی ۲۰هزار تومان در ماه شود، تا بتوانیم بگوییم یک سیستم توسعه پذیر داریم. یکی از اهداف سیستم های توزیع شده نیز، توسعه پذیری آن هاست. ( به قول آقای تننباوم یکی از چهار هدف سیستم توزیع شده، توسعه پذیر بودن آن است. یکی دیگر از این اهداف سیستم توزیع شده، شفافیت یا همان Transparency است.) همانطور که گفتیم اگر در یک سیستم توزیع شده، درخواست هایی(Requests) که به سیستم می آیند ۱۰برابر شود، سیستم توزیع شده باید بتواند مثلا با ۲برابر کردن منابع خود، این تعداد درخواست ها را بدون مشکل پاسخ گو باشد.
۱.توسعه عمودی یا به اصطلاح Scale Up
۲. توسعه افقی یا به اصطلاح Scale Out
در توسعه عمودی که به آن Scale Up کردن نیز گفته می شود، شما به هر کدام از گره ها (Nodes ) که در واقع همان کامپیوتر های موجود در سیستم توزیع شده هستند، منابعی مانند Ram ، CPU یا کارت گرافیک و… اضافه میکنید. با این کار شما سیستم های قوی تری دارید که میتوانند به درخواست های بیشتری در زمان سریعتر پاسخ دهند.
در این روش، به جای اضافه کردن منابع بیشتر مانند Ram به کامیپوترهای جاری در سیستم توزیع شده، تعدا گره ها (Nodes) را افزایش می دهیم. برای مثال تعدادی بیشتری کامپیوتر به سیستم توزیع شده اضافه می کنیم.
اگر چه روش اول، روشی ساده و بعضاً کارا است، ولی پیشنهاد می شود در یک سیستم توزیع شده از روش دوم استفاده کنید.
کافکا به صورت خوشه ای بر روی یک یا چند سرور کار می کند.
کافکا جریان داده ها و رکورد ها را در ساختارهایی به نام تاپیک( topics) ذخیره می کند.
هر رکوردی دارای یک کلید، یک مقدار و یک برچسب زمانی می باشد تا بصورت مجزا از سایر رکورد ها مشخص باشد.
حال باید چندین اصطلاح را تعریف کنیم :
ارسال کننده پیام یا رابط تولید کننده . Application، سیستم یا زیرسیستمی است که عملیات Publish پیام را برای Topic خاص از Kafka Server انجام میدهد.
دریافت کننده پیام یا رابط مصرف کننده . Application، سیستم یا زیرسیستمی که بر روی یک یا چند Topic خاص، Subscribe کردهاست به عبارتی دیگر به یک یا چند تاپیک متصل شده و به پردازش رکوردها می پردازد. (همچنین هر Consumer میتواند روی یک یا چند Partition از یک Topic خاص نیز Subscribe کند).
گروهی از Consumerها میباشند که با یک group.id، مشخص شدهاند. عموما این گروه شامل یک Replicate از یک Application است؛ مانند گروه ارسال کننده ایمیل (یک زیر سیستم ارسال کننده ایمیل که چندین بار در سرورهای مختلف اجرا شده است) Kafka.این ضمانت را به ما میدهد که هر پیام ذخیره شده در یکTopic ، برای تمامی ConsumerGroup های مرتبط ارسال شود؛ اما در هر Consumer Group، تنها یک دریافت کننده داشته باشد. یعنی هر پیام در هر Consumer Group، تنها توسط یک Consumer دریافت شود.
قسمتی که تمامی پیامها را از Producer دریافت میکند، سپس آنها را در Log مربوط به Topic مشخص شده ذخیره میکند و پس از آن، پیام ذخیره شده را برای تمامی Consumer های مرتبط ارسال میکند.
مجموعه ای از Brokerها میباشد که بصورت یک Cluster اجرا شدهاند. این کار باعث بالا رفتن کارآیی و تحمل خطا میشود.
یک دسته بندی برای ذخیره کردن پیامهای Publish شده میباشد. Topicها همانند مفهوم Tableها در SQL Server میباشند. همانطور که میدانید هر Table از قبل تعریف شدهاست. یک کاربر با ارسال یک درخواست ثبت، دادهها را در آن ذخیره میکند و سپس گروهی از کاربران از دادههای ثبت شده استفاده میکنند. در مفهموم Topic نیز ابتدا ما Topic مورد نظر را با خصوصیاتی که باید داشته باشد تعریف میکنیم ( البته میتوان بصورت Dynamic نیز آن را تعریف کرد؛ اما این روش توصیه نمیشود). سپس Producer پیام مربوطه را به همراه نام Topic برای Broker ارسال میکند. Broker پیام را در Partition مربوطه از Topic ذخیره میکند و سپس پیام برای تمامی Consumer های مربوطه ارسال میشود.
یکی از تفاوتهای بسیار مهم Kafka با سایر Message brokerها مانند RabitMQ که باعث بالارفتن کارآیی آن نیز شدهاست، قابلیت Partition در Topicها میباشد. در واقع هر Topic از یک یا چندین Partition برای ذخیره دادهها استفاده میکند. تعریف درست تعداد Partitionها در یک Topic ، تاثیر مستقیمی بر درجه همزمانی و کارآیی در آن Topic و کل سیستم دارد. در Kafka تمامی پیامها به همان ترتیبی که وارد شدهاند، در Partitionهای یک Topic ذخیره میشوند و به همان ترتیب نیز برای Consumerها ارسال میشوند.
بطور مثال فرض کنید تعداد Partition های یک Topic با نام DepartmentMessage ، یک میباشد (از این Topic برای ذخیره پیامهای واحدهای مختلف یک سازمان استفاده میشود). در این صورت تمامی پیامهای دریافتی تنها در یک Partition ذخیره میشوند.
هر خانه از یک Partition، توسط یک شناسه از نوع int و با نام offset در دسترس است. تمامی پیامهای جدید ارسالی توسط Producer با offset ای بزرگتر از offset موجود در این Partition ذخیره میشوند؛ یعنی در انتهای آن قرار میگیرند. در مثال فوق در صورت دریافت پیام جدید، offset آن با عدد 10 مقداردهی میشود. همچنین عملیات خواندن نیز از کوچکترین offset ای که هنوز مقدار آن توسط Consumer ها خوانده نشدهاست، انجام میشود. همانطور که مشخص است، بدلیل اینکه تعداد Partitionهای این مثال عدد یک میباشد، تمامی درخواستهای Producerها در یک Partition قرار میگیرند و تمامی Consumerها نیز از طریق یک Partition به پیامها دسترسی دارند؛ یعنی در صورت بالا بردن تعداد Producerها یا Consumerها، کارآیی بالا نمیرود. البته با اینکه کنترل مقدار اولیه offse برای شروع یک Consumer به دست خود Consumer و Zookeeper است، اما در اکثر موارد تمامی Consumer های یک Topic باید از یک نقطه، شروع به خواندن دادهها کنند. در این حالت تا زمانیکه پیام با offset 1، توسط Consumerای خوانده نشود، هیچ Consumerای نمیتواند پیام شماره 2 را بخواند. استفاده کردن از یک Partition بیشتر زمانی کاربرد دارد که بخواهید تمامی پیامهایتان، واقعا در یک صف قرار بگیرند.
حال فرض کنید در سازمان شما سه واحد اداری، مالی و آموزش وجود دارد. در این صورت بدلیل اینکه تمامی پیامها در یک Partition ذخیره میشوند، تا زمانی که یک واحد تمامی پیامهای مرتبط با خود را از ابتدای Partition نخواندهاست، دیگر واحدها نمیتوانند به پیامهای مرتبط با خود دسترسی داشته باشند. پس در این صورت ما میتوانیم تعداد Partitionهای این Topic را عدد 3 درنظر بگیریم؛ بصورتی که پیامهای مرتبط با هر واحد در یک Partition جدا قرار بگیرد.
بدین شکل :
در این روش هر Producer زمانیکه پیامی را برای این Topic ارسال میکند، یک Key نیز برای آن مشخص میکند و این Key نشان دهنده این است که پیام جدید باید در کدام Partition ذخیره شود. یعنی بصورت همزمان میتوانید در هر سه Partition، پیامهایتان را ذخیره کنید؛ بصورتی که بطور مثال تمامی پیامهای مربوط به واحد اداری، در Partition0 و تمامی پیامهای مربوط به واحد مالی، در Partition 1 و واحد آموزش در Partition 2 ذخیره شوند و همچنین عملیات خواندن از این Topic نیز میتواند بصورت همزمان در واحدهای مختلف انجام شود.
باید در تعریف تعداد Partition های یک Topic این نکته را در نظر بگیرید که این تعداد کاملا به نیازمندی شما و کارآیی که شما مد نظر دارید، بستگی دارد. تعداد این Partitionها حتی میتواند به تعداد User های یک سیستم نیز تعریف شود. علاوه بر آن باید بدانید که هر Partition در هر زمان تنها توسط یک Primary Broker میتواند در دسترس سایر قسمتها قرار بگیرد و تمامی عملیات خواندن و نوشتن در Partition توسط Kafka Server انجام میشود و در صورتیکه به هر دلیلی این سرور از دسترس خارج شود، مدیریت این Partition به سرورهای دیگر داده میشود.
نحوه کار کافکا بصورت شماتیک بدین شکل است:
قبل از نمایش معماری کلی باید یکسری معانی دیگری نیز تعریف کنیم :
یک Kafka Server که مسئول خواندن و نوشتن در یک Partition است. در یک Cluster هر Partition در یک زمان تنها یک Primary Broker دارد. این Primary Broker همزمان میتواند برای Partitionهای دیگر نقش Replicas Broker را بازی کند. انتخاب یک Primary Broker برای یک Partition توسط ZooKeeper انجام میشود.
Kafka Server هایی هستند که شامل یک کپی از Partition میباشند. عملیات خواندن و نوشتن در Partition توسط Primary انجام میشود. در صورتیکه Primary از دسترس خارج شود، ZooKeeper یکی از Replicas Broker ها را بعنوان Primary در نظر میگیرد. همچنین این نکته را باید در نظر بگیرید که هر Replicate همزمان میتواند Primary پارتیشنهای دیگر باشد.
این خصوصیت احتمال از دست دادن دادههای یک Topic را به حداقل میرساند؛ به این صورت که هر پیام از یک Topic، در چندین سرور مختلف که تعداد آنها توسط این خصوصیت مشخص میشود، نگهداری میشود.
Kafka هیچ Stateای را نگه نمیدارد (اصطلاحا stateless میباشد). برای ذخیره کردن و مدیریت تمامی State ها از جمله اینکه درحال حاضر Primary Broker برای یک Partition چه سروری است، یا اینکه پیامهای یک Partition تا کدام offset توسط Consumer ها خوانده شدهاند یا اینکه کدام Consumer در حال حاضر در یک Consumer Group مسئول یک Partition میباشد، توسط Apache Zookeeper انجام میشود.
کافکا پیامهایی را که از بسیاری از "تولید کنندگان" دریافت می کند را ذخیره کرده و داده ها بدین صورت بر روی پارتیشن های گوناگونی در "تاپیکهای" مختلف توزیع شده و پارتیشن بندی می شوند. در هر پارتیشن پیامها با هم شاخص گذاری شده و با یک برچسب زمانی ذخیره می شوند. سایر فرایندها مثل "مصرف کنندگان" می توانند پیامها را از پارتیشنها دریافت کرده و اطلاعات مورد نظر خود را درخواست کنند. کافکا بر روی خوشه هایی از یک یا چند سرور اجرا می شود و پارتیشنها می توانند بر روی چندین نود مختلف توزیع شوند.
آپاچی کافکا هنگامی که همراه با آپاچی استورم، اچ بیس و اسپارک استفاده شود به طور موثری می تواند دادههای بلادرنگ و جریان را پردازش کند. اگر کافکا به عنوان یک خوشه روی چندین سرور پیکر بندی و اجرا شود در اینصورت به کمک 4 رابط کاربری خود براحتی عملیات انتشار و دریافت و پردازش اطلاعات را به سرعت و با کارایی بالا انجام می دهد.
توانایی کافکا برای ارایه جریانهای عظیم داده و پیام ،همراه با تحمل خطای بالا ، آن را جایگزین برخی از سیستمهای پیامرسانی مرسوم مانند JMS ، AMQP و غیره کرده است.
1- تمامی پیامهای دریافتی در یک Partition از یک Topic، به همان ترتیبی که دریافت میشوند ذخیره میشوند.
2- در یک Partition فعال ،consumer ها تمامی پیامها را به همان ترتیبی که ذخیره شدهاند، دریافت میکنند.
3- در یک Topic با Replication Factor ای با مقدار N، درجه تحمل خطا N - 1 میباشد.