تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

چه چیزی می تواند شرکت بزرگی مانند لامودا را با فرآیندی ساده و ده ها سرویس به هم پیوسته مجبور کند تا رویکرد خود را به طور قابل توجهی تغییر دهد؟ انگیزه می تواند کاملاً متفاوت باشد: از قانونگذاری گرفته تا تمایل به آزمایش ذاتی در همه برنامه نویسان.

اما این بدان معنا نیست که نمی توانید روی مزایای اضافی حساب کنید. سرگئی زایکا به شما خواهد گفت که اگر API مبتنی بر رویدادها را در کافکا پیاده سازی کنید دقیقاً چه چیزی را می توانید برنده شوید (چند). همچنین قطعاً در مورد عکس های بزرگ و اکتشافات جالب صحبت خواهد شد - آزمایش بدون آنها نمی تواند انجام دهد.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

سلب مسئولیت: این مقاله بر اساس مطالب جلسه ای است که سرگی در نوامبر 2018 در HighLoad++ برگزار کرد. تجربه زنده لامودا از کار با کافکا، شنوندگان را کمتر از سایر گزارش‌های برنامه جذب کرد. ما فکر می کنیم این یک مثال عالی از این واقعیت است که شما همیشه می توانید و باید افراد همفکر خود را پیدا کنید، و سازمان دهندگان HighLoad++ به تلاش خود برای ایجاد فضایی مناسب برای این امر ادامه خواهند داد.

در مورد روند

Lamoda یک پلت فرم تجارت الکترونیکی بزرگ است که دارای مرکز تماس، خدمات تحویل (و بسیاری از شرکت های وابسته)، یک استودیوی عکس، یک انبار بزرگ است و همه اینها بر روی نرم افزار خود اجرا می شود. ده ها روش پرداخت وجود دارد، شرکای b2b که ممکن است از برخی یا همه این خدمات استفاده کنند و بخواهند اطلاعات به روز محصولات خود را بدانند. علاوه بر این، لامودا در سه کشور به جز فدراسیون روسیه فعالیت می کند و همه چیز در آنجا کمی متفاوت است. در مجموع، احتمالاً بیش از صد راه برای پیکربندی یک سفارش جدید وجود دارد که باید به روش خاص خود پردازش شود. همه اینها با کمک ده ها سرویس کار می کند که گاهی اوقات به روش های غیر آشکار ارتباط برقرار می کنند. همچنین یک سیستم مرکزی وجود دارد که مسئولیت اصلی آن وضعیت سفارش است. ما او را باب صدا می کنیم، من با او کار می کنم.

ابزار بازپرداخت با API مبتنی بر رویدادها

کلمه رویداد محور کاملاً هک شده است؛ کمی بیشتر با جزئیات بیشتر منظور از این را تعریف خواهیم کرد. من با زمینه ای شروع می کنم که در آن تصمیم گرفتیم رویکرد API مبتنی بر رویداد را در کافکا امتحان کنیم.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

در هر فروشگاهی، علاوه بر سفارش هایی که مشتریان بابت آن هزینه پرداخت می کنند، مواقعی نیز وجود دارد که فروشگاه ملزم به بازگشت وجه است، زیرا محصول مناسب مشتری نیست. این یک فرآیند نسبتا کوتاه است: در صورت لزوم، اطلاعات را شفاف می کنیم و پول را منتقل می کنیم.

اما بازگشت به دلیل تغییرات در قوانین پیچیده تر شد و ما مجبور شدیم یک میکروسرویس جداگانه برای آن پیاده سازی کنیم.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

انگیزه ما:

  1. قانون FZ-54 - به طور خلاصه، قانون مستلزم گزارش دادن به اداره مالیات در مورد هر تراکنش پولی، اعم از اظهارنامه یا رسید، در یک SLA نسبتاً کوتاه چند دقیقه است. ما به عنوان یک شرکت تجارت الکترونیک، عملیات بسیار زیادی را انجام می دهیم. از نظر فنی، این به معنای مسئولیت جدید (و در نتیجه یک سرویس جدید) و بهبود در تمام سیستم های درگیر است.
  2. تقسیم BOB یک پروژه داخلی شرکت برای رهایی BOB از تعداد زیادی از مسئولیت های غیر اصلی و کاهش پیچیدگی کلی آن است.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

این نمودار سیستم های اصلی لامودا را نشان می دهد. در حال حاضر اکثر آنها بیشتر هستند یک صورت فلکی از 5-10 میکروسرویس در اطراف یکپارچه در حال کوچک شدن. آنها به آرامی در حال رشد هستند، اما ما سعی می کنیم آنها را کوچکتر کنیم، زیرا استقرار قطعه انتخاب شده در وسط ترسناک است - ما نمی توانیم اجازه دهیم که سقوط کند. ما مجبور هستیم همه مبادلات (فلش ها) را رزرو کنیم و این واقعیت را در نظر بگیریم که ممکن است هر یک از آنها در دسترس نباشد.

BOB همچنین مبادلات بسیار زیادی دارد: سیستم های پرداخت، سیستم های تحویل، سیستم های اطلاع رسانی و غیره.

از نظر فنی BOB عبارت است از:

  • ~ 150 هزار خط کد + 100 هزار خط آزمایش.
  • php7.2 + Zend 1 & Symfony Components 3;
  • بیش از 100 API و 50 ادغام خروجی؛
  • 4 کشور با منطق تجاری خودشان.

استقرار BOB پرهزینه و دردناک است، مقدار کد و مشکلاتی که حل می کند به حدی است که هیچ کس نمی تواند همه آن را در ذهن خود قرار دهد. به طور کلی دلایل زیادی برای ساده کردن آن وجود دارد.

فرآیند بازگشت

در ابتدا، دو سیستم درگیر فرآیند هستند: BOB و Payment. اکنون دو مورد دیگر ظاهر می شود:

  • خدمات مالی، که مشکلات مربوط به مالیه سازی و ارتباط با خدمات خارجی را برطرف می کند.
  • ابزار بازپرداخت، که به سادگی حاوی مبادلات جدید است تا BOB را متورم نکند.

حالا روند به این صورت است:

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

  1. BOB درخواستی برای بازپرداخت دریافت می کند.
  2. BOB در مورد این ابزار بازپرداخت صحبت می کند.
  3. ابزار بازپرداخت به Payment می گوید: «پول را برگردان».
  4. پرداخت پول را برمی گرداند.
  5. ابزار بازپرداخت و BOB وضعیت ها را با یکدیگر همگام می کنند، زیرا در حال حاضر هر دو به آن نیاز دارند. ما هنوز آماده تغییر کامل به ابزار بازپرداخت نیستیم، زیرا BOB دارای یک رابط کاربری، گزارش هایی برای حسابداری و به طور کلی داده های زیادی است که به این راحتی نمی توان آنها را انتقال داد. شما باید روی دو صندلی بنشینید.
  6. درخواست برای پرداخت مالی از بین می رود.

در نتیجه، ما نوعی اتوبوس رویداد در کافکا ساختیم - اتوبوس رویداد، که همه چیز از آن شروع شد. هورای، حالا ما یک نقطه شکست (طعنه) داریم.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

مزایا و معایب کاملاً واضح است. ما یک اتوبوس ساختیم، به این معنی که اکنون همه خدمات به آن بستگی دارد. این طراحی را ساده می کند، اما یک نقطه از شکست را وارد سیستم می کند. کافکا سقوط خواهد کرد، روند متوقف خواهد شد.

API رویداد محور چیست

پاسخ خوبی به این سوال در گزارش مارتین فاولر (GOTO 2017) آمده است. "معانی بسیاری از معماری رویداد محور".

خلاصه کاری که انجام دادیم:

  1. تمام مبادلات ناهمزمان از طریق ذخیره سازی رویدادها. به‌جای اطلاع‌رسانی به هر مصرف‌کننده علاقه‌مند در مورد تغییر وضعیت از طریق شبکه، رویدادی را درباره تغییر وضعیت به یک حافظه متمرکز می‌نویسیم و مصرف‌کنندگان علاقه‌مند به موضوع، هر چیزی را که از آنجا ظاهر می‌شود می‌خوانند.
  2. رویداد در این مورد یک اعلان است (اطلاعیه ها) که چیزی در جایی تغییر کرده است. به عنوان مثال، وضعیت سفارش تغییر کرده است. مصرف کننده ای که علاقه مند به برخی از داده های همراه با تغییر وضعیت است که در اعلان گنجانده نشده است، می تواند خودش از وضعیت آن مطلع شود.
  3. حداکثر گزینه منبع یابی رویداد تمام عیار است، انتقال دولتی، که در آن رویداد حاوی تمام اطلاعات لازم برای پردازش است: از کجا آمده است و به چه وضعیتی رسیده است، دقیقاً چگونه داده ها تغییر کرده اند و غیره. تنها سوال امکان پذیر بودن و میزان اطلاعاتی است که می توانید برای ذخیره آن هزینه کنید.

به عنوان بخشی از راه اندازی ابزار بازپرداخت، از گزینه سوم استفاده کردیم. این پردازش رویداد ساده‌سازی شد زیرا نیازی به استخراج اطلاعات دقیق نبود، به علاوه سناریویی را که در آن هر رویداد جدید موجی از درخواست‌های شفاف‌سازی از مصرف‌کنندگان ایجاد می‌کرد، حذف کرد.

سرویس ابزار بازپرداخت بارگذاری نشده استبنابراین کافکا بیش از آنکه یک ضرورت باشد، طعم قلم را می پسندد. من فکر نمی کنم که اگر خدمات بازپرداخت به یک پروژه پر بار تبدیل شود، تجارت خوشحال می شود.

تبادل Async همانطور که هست

برای تبادلات ناهمزمان، بخش PHP معمولا از RabbitMQ استفاده می کند. ما داده های درخواست را جمع آوری کردیم، آن را در یک صف قرار دادیم و مصرف کننده همان سرویس آن را خواند و ارسال کرد (یا ارسال نکرد). برای خود API، Lamoda به طور فعال از Swagger استفاده می کند. ما یک API طراحی می کنیم، آن را در Swagger توصیف می کنیم و کد مشتری و سرور را تولید می کنیم. ما همچنین از JSON RPC 2.0 کمی بهبود یافته استفاده می کنیم.

در برخی مکان‌ها از اتوبوس‌های ESB استفاده می‌شود، برخی در ActiveMQ زندگی می‌کنند، اما به طور کلی، RabbitMQ - استاندارد.

تبادل Async TO BE

هنگام طراحی مبادله از طریق رویدادهای اتوبوس، می توان یک قیاس را ردیابی کرد. ما به طور مشابه تبادل داده های آینده را از طریق توضیحات ساختار رویداد توصیف می کنیم. فرمت yaml، ما باید خودمان تولید کد را انجام می‌دادیم، ژنراتور DTO را مطابق مشخصات ایجاد می‌کند و به کلاینت‌ها و سرورها کار با آنها را آموزش می‌دهد. نسل به دو زبان می رود - golang و php. این به حفظ ثبات کتابخانه ها کمک می کند. ژنراتور به زبان گلنگ نوشته شده است و به همین دلیل نام آن را گوگی گذاشته است.

منبع یابی رویداد در کافکا یک چیز معمولی است. راه حلی از نسخه سازمانی اصلی Kafka Confluent وجود دارد ناکدی، راه حلی از برادران دامنه ما Zalando. ما انگیزه شروع با وانیلی کافکا - این به معنای آزاد گذاشتن راه حل است تا زمانی که در نهایت تصمیم بگیریم که آیا از آن در همه جا استفاده خواهیم کرد یا خیر، و همچنین برای خودمان فضایی برای مانور و پیشرفت باقی می گذاریم: ما برای خود حمایت می خواهیم. JSON RPC 2.0، ژنراتورهای دو زبانه و ببینیم چه چیز دیگری.

طعنه آمیز است که حتی در چنین مورد خوشحال کننده ای، وقتی یک تجارت تقریباً مشابه وجود دارد، Zalando، که راه حل تقریباً مشابهی ساخته است، ما نمی توانیم به طور مؤثر از آن استفاده کنیم.

الگوی معماری در زمان راه اندازی به شرح زیر است: ما مستقیماً از کافکا می خوانیم، اما فقط از طریق اتوبوس رویدادها می نویسیم. در کافکا چیزهای زیادی برای خواندن وجود دارد: کارگزاران، متعادل کننده ها، و کم و بیش برای مقیاس بندی افقی آماده است، می خواستم این را حفظ کنم. ما می‌خواستیم ضبط را از طریق یک Gateway با نام Events-bus کامل کنیم، و دلیل آن این است.

رویدادها - اتوبوس

یا اتوبوس رویداد. این به سادگی یک دروازه http بدون حالت است که چندین نقش مهم را ایفا می کند:

  • تولید اعتبار سنجی - بررسی می کنیم که رویدادها با مشخصات ما مطابقت دارند.
  • سیستم اصلی رویداد، یعنی این سیستم اصلی و تنها در شرکت است که به این سوال پاسخ می دهد که کدام رویدادها با کدام ساختارها معتبر تلقی می شوند. اعتبار سنجی صرفاً شامل انواع داده ها و شمارش ها برای مشخص کردن دقیق محتوا است.
  • تابع هش برای تقسیم - ساختار پیام کافکا کلید-مقدار است و با استفاده از هش کلید محاسبه می شود که آن را در کجا قرار دهید.

چرا

ما در یک شرکت بزرگ با فرآیندی ساده کار می کنیم. چرا هر چیزی را تغییر دهیم؟ این یک آزمایش است، و ما انتظار داریم که از چندین مزیت بهره مند شویم.

مبادلات 1:n+1 (یک به چندین)

کافکا اتصال مصرف کنندگان جدید را به API بسیار آسان می کند.

فرض کنید یک دایرکتوری دارید که باید آن را همزمان در چندین سیستم (و در برخی از سیستم های جدید) به روز نگه دارید. قبلا، ما یک بسته نرم افزاری اختراع کردیم که set-API را پیاده سازی می کرد و سیستم اصلی از آدرس های مصرف کننده مطلع می شد. اکنون سیستم اصلی به‌روزرسانی‌ها را برای موضوع ارسال می‌کند و همه علاقه‌مندان آن را می‌خوانند. یک سیستم جدید ظاهر شد - ما آن را برای موضوع ثبت نام کردیم. بله، بسته نرم افزاری، اما ساده تر.

در مورد ابزار بازپرداخت، که یک قطعه BOB است، برای ما راحت است که آنها را از طریق کافکا هماهنگ کنیم. پرداخت می گوید که پول برگشت داده شده است: BOB، RT از این موضوع مطلع شدند، وضعیت خود را تغییر دادند، خدمات مالی از این موضوع مطلع شد و چک صادر کرد.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

ما برنامه‌هایی برای ایجاد یک سرویس اطلاع‌رسانی یکپارچه داریم که به مشتری در مورد اخبار مربوط به سفارش/بازگشت‌هایش اطلاع می‌دهد. اکنون این مسئولیت بین سیستم ها پخش شده است. برای ما کافی است که به سرویس Notifications یاد دهیم که اطلاعات مربوطه را از کافکا بگیرد و به آن پاسخ دهد (و این اعلان ها را در سیستم های دیگر غیرفعال کنید). نیازی به تبادل مستقیم جدید نخواهد بود.

داده محور

اطلاعات بین سیستم‌ها شفاف می‌شود - مهم نیست که چه "شرکت خونین" دارید و مهم نیست که حجم شما چقدر زیاد است. Lamoda دارای یک بخش تجزیه و تحلیل داده است که داده ها را از سیستم ها جمع آوری می کند و آنها را به شکل قابل استفاده مجدد، هم برای تجارت و هم برای سیستم های هوشمند، قرار می دهد. کافکا به شما اجازه می دهد تا به سرعت داده های زیادی را به آنها بدهید و جریان اطلاعات را به روز نگه دارید.

گزارش تکرار

مانند RabbitMQ، پیام ها پس از خواندن ناپدید نمی شوند. زمانی که یک رویداد حاوی اطلاعات کافی برای پردازش باشد، تاریخچه ای از تغییرات اخیر در شی و در صورت تمایل، امکان اعمال این تغییرات را داریم.

مدت زمان ذخیره‌سازی گزارش تکرار به شدت نوشتن روی این موضوع بستگی دارد؛ کافکا به شما اجازه می‌دهد تا به طور انعطاف‌پذیر محدودیت‌هایی را برای زمان ذخیره‌سازی و حجم داده تعیین کنید. برای موضوعات فشرده، مهم است که همه مصرف کنندگان وقت داشته باشند تا اطلاعات را قبل از ناپدید شدن بخوانند، حتی در صورت عدم عملکرد کوتاه مدت. معمولاً امکان ذخیره داده ها برای واحد روز، که برای پشتیبانی کاملاً کافی است.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

در ادامه، بازگویی کمی از مستندات، برای کسانی که با کافکا آشنایی ندارند (تصویر هم از مستندات است)

AMQP دارای صف است: ما پیام هایی را برای مصرف کننده به یک صف می نویسیم. به طور معمول، یک صف توسط یک سیستم با همان منطق تجاری پردازش می شود. اگر نیاز به اطلاع رسانی به چندین سیستم دارید، می توانید به برنامه آموزش دهید که در چندین صف بنویسد یا تبادل را با مکانیزم fanout پیکربندی کند که خود آنها را شبیه سازی می کند.

کافکا نیز انتزاعی مشابه دارد موضوع، که در آن پیام می نویسید، اما پس از خواندن ناپدید نمی شوند. به‌طور پیش‌فرض، وقتی به کافکا متصل می‌شوید، همه پیام‌ها را دریافت می‌کنید و می‌توانید از جایی که متوقف کرده‌اید ذخیره کنید. یعنی شما به صورت متوالی مطالعه می کنید، ممکن است پیام را به عنوان خوانده شده علامت گذاری نکنید، اما شناسه را ذخیره کنید که سپس می توانید به خواندن ادامه دهید. شناسه ای که روی آن تنظیم کردید، افست نامیده می شود و مکانیسم آن commit offset است.

بر این اساس، منطق متفاوتی را می توان پیاده سازی کرد. به عنوان مثال، ما BOB را در 4 نمونه برای کشورهای مختلف داریم - Lamoda در روسیه، قزاقستان، اوکراین، بلاروس است. از آنجایی که آنها به طور جداگانه مستقر شده اند، پیکربندی های کمی متفاوت و منطق تجاری خود را دارند. ما در پیام نشان می دهیم که به کدام کشور اشاره دارد. هر مصرف‌کننده BOB در هر کشور با یک groupId متفاوت می‌خواند، و اگر پیام برای آنها صدق نمی‌کند، از آن صرفنظر می‌کنند. بلافاصله افست +1 را انجام می دهد. اگر همان مبحث توسط سرویس پرداخت ما خوانده شود، این کار را با یک گروه جداگانه انجام می دهد و بنابراین افست ها قطع نمی شوند.

الزامات رویداد:

  • کامل بودن داده ها مایلم رویداد دارای داده های کافی باشد تا بتوان آن را پردازش کرد.

  • یکپارچگی تأیید سازگاری رویداد را به Events-bus واگذار می کنیم و می تواند آن را پردازش کند.
  • ترتیب مهم است. در صورت بازگشت مجبوریم با تاریخ کار کنیم. با اعلان‌ها، سفارش مهم نیست، اگر اعلان‌های همگن باشند، صرف نظر از اینکه کدام سفارش اول رسیده است، ایمیل یکسان خواهد بود. در مورد بازپرداخت، یک روند واضح وجود دارد؛ اگر سفارش را تغییر دهیم، استثناهایی ایجاد می‌شود، بازپرداخت ایجاد یا پردازش نمی‌شود - ما در وضعیت متفاوتی قرار می‌گیریم.
  • ثبات. ما یک فروشگاه داریم و اکنون به جای API رویدادها را ایجاد می کنیم. ما به راهی برای انتقال سریع و ارزان اطلاعات در مورد رویدادهای جدید و تغییرات موجود در خدمات خود نیاز داریم. این امر از طریق یک مشخصات مشترک در یک مخزن git جداگانه و ژنراتورهای کد به دست می آید. بنابراین کلاینت ها و سرورها در سرویس های مختلف با هم هماهنگ می شوند.

کافکا در لامودا

ما سه نصب کافکا داریم:

  1. سیاهههای مربوط
  2. تحقیق و توسعه
  3. رویدادها - اتوبوس.

امروز ما فقط در مورد آخرین نکته صحبت می کنیم. در رویدادهای اتوبوس، ما نصب بسیار بزرگ نداریم - 3 کارگزار (سرور) و فقط 27 موضوع. به عنوان یک قاعده، یک موضوع یک فرآیند است. اما این یک نکته ظریف است و اکنون به آن خواهیم پرداخت.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

در بالا نمودار rps است. فرآیند بازپرداخت با یک خط فیروزه ای (بله، روی محور X) مشخص شده است، و خط صورتی فرآیند به روز رسانی محتوا است.

کاتالوگ Lamoda حاوی میلیون ها محصول است و داده ها همیشه به روز می شوند. برخی از مجموعه ها از مد افتاده اند، مجموعه های جدیدی جایگزین آنها می شوند و مدل های جدید دائماً در کاتالوگ ظاهر می شوند. ما سعی می کنیم پیش بینی کنیم که فردا چه چیزی برای مشتریان ما جالب خواهد بود، بنابراین دائماً چیزهای جدید می خریم، از آنها عکس می گیریم و ویترین را به روز می کنیم.

پیک های صورتی به روز رسانی محصول هستند، یعنی تغییرات در محصولات. دیده می شود که بچه ها عکس گرفتند، عکس گرفتند و دوباره! - بسته ای از رویدادها را بارگذاری کرد.

موارد استفاده Lamoda Events

ما از معماری ساخته شده برای عملیات زیر استفاده می کنیم:

  • ردیابی وضعیت بازگشت: تماس برای اقدام و ردیابی وضعیت از همه سیستم های درگیر. پرداخت، وضعیت، مالیات، اعلان ها. در اینجا ما این رویکرد را آزمایش کردیم، ابزارهایی ساختیم، تمام اشکالات را جمع آوری کردیم، اسناد نوشتیم و به همکارانمان گفتیم که چگونه از آن استفاده کنند.
  • به روز رسانی کارت های محصول: پیکربندی، متا داده، ویژگی ها. یک سیستم می خواند (که نمایش می دهد) و چندین سیستم می نویسند.
  • ایمیل، فشار و اس ام اس: سفارش جمع آوری شده، سفارش رسیده، برگشت پذیرفته شده است و غیره، تعدادشان زیاد است.
  • انبار، تجدید انبار - به روز رسانی کمی اقلام، فقط اعداد: ورود به انبار، بازگشت. لازم است تمام سیستم های مرتبط با رزرو کالا با جدیدترین داده ها کار کنند. در حال حاضر، سیستم به روز رسانی سهام بسیار پیچیده است؛ کافکا آن را ساده می کند.
  • تحلیل دادهها (بخش تحقیق و توسعه)، ابزارهای ML، تجزیه و تحلیل، آمار. ما می خواهیم اطلاعات شفاف باشد - کافکا برای این کار مناسب است.

اکنون قسمت جالب تر در مورد برآمدگی های بزرگ و اکتشافات جالبی که در شش ماه گذشته رخ داده است.

مشکلات طراحی

فرض کنید می‌خواهیم کار جدیدی انجام دهیم - مثلاً کل فرآیند تحویل را به کافکا منتقل کنیم. اکنون بخشی از فرآیند در Order Processing در BOB پیاده سازی شده است. یک مدل وضعیت پشت انتقال سفارش به سرویس تحویل، انتقال به انبار میانی و غیره وجود دارد. یکپارچگی کامل، حتی دو عدد، به اضافه یک دسته API اختصاص داده شده به تحویل وجود دارد. آنها خیلی بیشتر در مورد تحویل می دانند.

به نظر می رسد این مناطق مشابه هستند، اما پردازش سفارش در BOB و سیستم حمل و نقل وضعیت متفاوتی دارند. به عنوان مثال، برخی از خدمات پیک وضعیت های میانی را ارسال نمی کنند، بلکه فقط وضعیت های نهایی را ارسال می کنند: "تحویل شده" یا "از دست رفته". برعکس، برخی دیگر با جزئیات زیادی در مورد جابجایی کالا گزارش می دهند. هر کسی قوانین اعتبار سنجی خود را دارد: برای برخی، ایمیل معتبر است، به این معنی که پردازش خواهد شد. برای دیگران معتبر نیست، اما به دلیل وجود شماره تلفن برای تماس، سفارش همچنان پردازش می شود و شخصی می گوید که چنین سفارشی اصلا پردازش نمی شود.

جریان داده ها

در مورد کافکا، مسئله سازماندهی جریان داده مطرح می شود. این کار شامل انتخاب یک استراتژی بر اساس چندین نکته است؛ بیایید همه آنها را مرور کنیم.

در یک موضوع یا در موضوعات مختلف؟

ما مشخصات رویداد داریم. در BOB می نویسیم که فلان سفارش باید تحویل داده شود و نشان می دهیم: شماره سفارش، ترکیب آن، برخی SKU ها و بارکدها و غیره. هنگامی که کالا به انبار می رسد، تحویل می تواند وضعیت ها، مهرهای زمانی و هر آنچه را که لازم است دریافت کند. اما پس از آن می خواهیم به روز رسانی های این داده ها را در BOB دریافت کنیم. ما یک فرآیند معکوس برای دریافت داده از تحویل داریم. آیا این همان رویداد است؟ یا این یک تبادل جداگانه است که شایسته موضوع خاص خود است؟

به احتمال زیاد، آنها بسیار شبیه خواهند بود و وسوسه ایجاد یک موضوع بی اساس نیست، زیرا یک موضوع جداگانه به معنای مصرف کنندگان جداگانه، تنظیمات جداگانه، یک نسل جداگانه از همه اینها است. اما یک واقعیت نیست.

رشته جدید یا رویداد جدید؟

اما اگر از همان رویدادها استفاده کنید، مشکل دیگری ایجاد می شود. برای مثال، همه سیستم‌های تحویل نمی‌توانند نوع DTO را که BOB می‌تواند تولید کند، تولید کنند. شناسه را برایشان می فرستیم اما چون به آن نیازی ندارند ذخیره نمی کنند و از نقطه نظر شروع فرآیند event-bus این فیلد الزامی است.

اگر قانونی را برای اتوبوس رویداد معرفی کنیم که این فیلد مورد نیاز است، مجبور می شویم قوانین اعتبار سنجی اضافی را در BOB یا در کنترل کننده رویداد شروع تنظیم کنیم. اعتبار سنجی شروع به گسترش در سراسر سرویس می کند - این خیلی راحت نیست.

مشکل دیگر وسوسه رشد تدریجی است. به ما می گویند که باید چیزی به رویداد اضافه شود و شاید اگر فکرش را می کردیم باید یک رویداد جداگانه می بود. اما در طرح ما، یک رویداد جداگانه یک موضوع جداگانه است. یک موضوع جداگانه کل فرآیندی است که در بالا توضیح دادم. توسعه دهنده وسوسه می شود که به سادگی فیلد دیگری را به طرح JSON اضافه کرده و آن را بازسازی کند.

در مورد بازپرداخت، ما در نیم سال به رویداد رویدادها رسیدیم. ما یک رویداد متا داشتیم به نام به‌روزرسانی بازپرداخت، که یک فیلد نوع داشت که این به‌روزرسانی واقعاً چیست. به همین دلیل، ما سوئیچ‌های «شگفت‌انگیز» با اعتبارسنجی‌هایی داشتیم که به ما می‌گفتند چگونه این رویداد را با این نوع تأیید کنیم.

نسخه سازی رویداد

برای اعتبارسنجی پیام‌ها در کافکا می‌توانید استفاده کنید آورو، اما لازم بود بلافاصله روی آن گذاشته و از Confluent استفاده کنید. در مورد ما، ما باید مراقب نسخه سازی باشیم. همیشه خواندن مجدد پیام ها از گزارش تکرار امکان پذیر نخواهد بود زیرا مدل "چپ" شده است. اساساً، به نظر می رسد که نسخه هایی ساخته می شود تا مدل با عقب سازگار باشد: به عنوان مثال، یک فیلد را به طور موقت اختیاری کنید. اگر تفاوت‌ها خیلی زیاد باشد، شروع به نوشتن در یک موضوع جدید می‌کنیم و وقتی مشتریان را مطالعه کردند، موضوع قبلی را به پایان می‌رسانیم.

ترتیب خواندن تضمینی پارتیشن ها

موضوعات داخل کافکا به پارتیشن تقسیم می شوند. در حالی که ما در حال طراحی نهادها و مبادلات هستیم، این خیلی مهم نیست، اما هنگام تصمیم گیری در مورد نحوه مصرف و مقیاس آن مهم است.

در حالت معمول، یک موضوع در کافکا می نویسید. به طور پیش فرض یک پارتیشن استفاده می شود و تمام پیام های این تاپیک به آن می روند. و در نتیجه مصرف کننده این پیام ها را به صورت متوالی می خواند. فرض کنید اکنون باید سیستم را گسترش دهیم تا پیام ها توسط دو مصرف کننده مختلف خوانده شوند. برای مثال، اگر پیامک ارسال می کنید، می توانید به کافکا بگویید که یک پارتیشن اضافی ایجاد کند و کافکا شروع به تقسیم پیام ها به دو قسمت می کند - نیمی اینجا، نیمی اینجا.

کافکا چگونه آنها را تقسیم می کند؟ هر پیام دارای یک بدنه (که ما JSON را در آن ذخیره می کنیم) و یک کلید دارد. می توانید یک تابع هش را به این کلید متصل کنید، که مشخص می کند پیام به کدام پارتیشن می رود.

در مورد بازپرداخت ما، این مهم است، اگر دو پارتیشن بگیریم، این احتمال وجود دارد که یک مصرف کننده موازی رویداد دوم را قبل از اول پردازش کند و مشکل ایجاد شود. تابع هش تضمین می‌کند که پیام‌های دارای کلید یکسان به همان پارتیشن ختم می‌شوند.

رویدادها در مقابل دستورات

این مشکل دیگری است که ما با آن مواجه شدیم. رویداد یک رویداد خاص است: ما می گوییم که در جایی اتفاقی افتاده است (چیزی_اتفاقی) مثلاً یک مورد لغو شده یا بازپرداخت صورت گرفته است. اگر شخصی به این رویدادها گوش دهد، با توجه به "مورد لغو شده"، موجودیت بازپرداخت ایجاد می شود و "بازپرداخت رخ داده است" در جایی از تنظیمات نوشته می شود.

اما معمولاً، وقتی رویدادها را طراحی می کنید، نمی خواهید آنها را بیهوده بنویسید - به این واقعیت تکیه می کنید که کسی آنها را می خواند. وسوسه زیادی وجود دارد که نه چیزی_اتفاق_افتاده (مورد_لغو_شده، بازپرداخت_بازپرداخت شده)، بلکه چیزی_باید_انجام_شده_نوشته شود. به عنوان مثال، کالا آماده بازگشت است.

از یک طرف، نحوه استفاده از رویداد را نشان می دهد. از سوی دیگر، این نام بسیار کمتر شبیه یک نام رویداد معمولی است. علاوه بر این، از اینجا تا دستور do_something دور نیست. اما شما هیچ تضمینی ندارید که کسی این رویداد را بخواند. و اگر آن را بخوانید، آنگاه آن را با موفقیت بخوانید. و اگر آن را با موفقیت بخوانید، پس کاری انجام داده اید، و آن چیزی موفق بوده است. لحظه ای که یک رویداد تبدیل به do_something می شود، بازخورد ضروری می شود و این یک مشکل است.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

در تبادل ناهمزمان در RabbitMQ، وقتی پیام را می‌خوانید، به http بروید، یک پاسخ دارید - حداقل اینکه پیام دریافت شده است. وقتی برای کافکا می نویسید، پیامی وجود دارد که برای کافکا نوشته اید، اما از نحوه پردازش آن چیزی نمی دانید.

بنابراین، در مورد ما باید یک رویداد پاسخگویی را معرفی می‌کردیم و نظارت را راه‌اندازی می‌کردیم که اگر این همه رویداد ارسال می‌شد، پس از فلان زمان، به همان تعداد رویداد پاسخ‌گویی برسد. اگر این اتفاق نیفتد، به نظر می رسد مشکلی رخ داده است. به عنوان مثال، اگر رویداد "item_ready_to_refund" را ارسال کنیم، انتظار داریم که بازپرداخت ایجاد شود، پول به مشتری برگردانده شود و رویداد "money_refunded" برای ما ارسال شود. اما این قطعی نیست، بنابراین نظارت لازم است.

Nuances

یک مشکل نسبتاً واضح وجود دارد: اگر از یک موضوع به طور متوالی مطالعه کنید و پیام بدی داشته باشید، مصرف کننده سقوط می کند و شما جلوتر نخواهید رفت. تو نیاز داری تمام مصرف کنندگان را متوقف کنید، برای ادامه خواندن، افست را بیشتر انجام دهید.

ما در مورد آن می دانستیم، روی آن حساب می کردیم، اما این اتفاق افتاد. و این اتفاق افتاد زیرا رویداد از نظر رویدادها - اتوبوس معتبر بود، رویداد از نظر اعتباردهنده برنامه معتبر بود، اما از نظر PostgreSQL معتبر نبود، زیرا در سیستم ما MySQL با INT بدون علامت، سیستم PostgreSQL را فقط با INT داشت. اندازه او کمی کوچکتر است و شناسه مناسب نیست. سیمفونی با استثنا درگذشت. البته ما استثنا را گرفتیم زیرا به آن تکیه کردیم و قرار بود این افست را انجام دهیم، اما قبل از آن می‌خواستیم شمارنده مشکل را افزایش دهیم، زیرا پیام ناموفق پردازش شد. شمارنده های این پروژه نیز در پایگاه داده هستند و سیمفونی قبلاً ارتباط با پایگاه داده را بسته است و استثنای دوم کل فرآیند را بدون فرصتی برای انجام افست از بین برد.

این سرویس برای مدتی دراز کشید - خوشبختانه، با کافکا این چندان بد نیست، زیرا پیام ها باقی می مانند. وقتی کار بازسازی شد، می توانید خواندن آنها را تمام کنید. راحت است.

کافکا این توانایی را دارد که از طریق ابزارسازی یک افست دلخواه تنظیم کند. اما برای انجام این کار، شما باید تمام مصرف کنندگان را متوقف کنید - در مورد ما، نسخه جداگانه ای را آماده کنید که در آن هیچ مصرف کننده، جابجایی مجدد وجود نداشته باشد. سپس در کافکا می‌توانید افست را از طریق ابزارسازی جابجا کنید، و پیام ارسال می‌شود.

تفاوت ظریف دیگر - Replication log در مقابل rdkafka.so - مربوط به مشخصات پروژه ما است. ما از پی‌اچ‌پی استفاده می‌کنیم و در پی‌اچ‌پی معمولاً همه کتابخانه‌ها از طریق مخزن rdkafka.so با کافکا ارتباط برقرار می‌کنند و سپس نوعی wrapper وجود دارد. شاید اینها مشکلات شخصی ما باشد، اما معلوم شد که خواندن دوباره بخشی از آنچه قبلا خوانده بودیم چندان آسان نیست. در کل مشکلات نرم افزاری وجود داشت.

با بازگشت به مشخصات کار با پارتیشن ها، دقیقاً در مستندات نوشته شده است مصرف کنندگان >= پارتیشن های موضوعی. اما خیلی دیرتر از آنچه که دوست داشتم متوجه این موضوع شدم. اگر می خواهید مقیاس کنید و دو مصرف کننده داشته باشید، حداقل به دو پارتیشن نیاز دارید. یعنی اگر یک پارتیشن داشتید که 20 هزار پیام در آن جمع شده بود و یک پارتیشن تازه ساختید، تعداد پیام ها به زودی برابر نمی شود. بنابراین، برای داشتن دو مصرف کننده موازی، باید با پارتیشن ها سر و کار داشته باشید.

نظارت

فکر می‌کنم نحوه نظارت ما حتی واضح‌تر خواهد بود که چه مشکلاتی در رویکرد موجود وجود دارد.

مثلاً محاسبه می کنیم که اخیراً چند محصول در پایگاه داده تغییر وضعیت داده اند و بر این اساس باید بر اساس این تغییرات اتفاقاتی رخ می داد و این عدد را به سیستم مانیتورینگ خود ارسال می کنیم. سپس از کافکا عدد دوم را می گیریم که واقعاً چند رویداد ثبت شده است. بدیهی است که تفاوت بین این دو عدد همیشه باید صفر باشد.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

علاوه بر این، باید نظارت کنید که تولیدکننده چگونه کار می کند، آیا رویدادها - اتوبوس پیام ها را دریافت کرده اند یا خیر، و مصرف کننده چگونه کار می کند. به عنوان مثال، در نمودارهای زیر، ابزار بازپرداخت به خوبی کار می کند، اما BOB به وضوح دارای مشکلاتی است (اوج آبی).

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

قبلاً به تاخیر گروه مصرف کننده اشاره کردم. به طور کلی، این تعداد پیام های خوانده نشده است. به طور کلی، مصرف کنندگان ما به سرعت کار می کنند، بنابراین تاخیر معمولا 0 است، اما گاهی اوقات ممکن است یک اوج کوتاه مدت وجود داشته باشد. کافکا می تواند این کار را خارج از جعبه انجام دهد، اما شما باید یک فاصله زمانی مشخص را تعیین کنید.

یک پروژه وجود دارد سوراخکه اطلاعات بیشتری در مورد کافکا به شما می دهد. این به سادگی از API گروه مصرف کننده برای ارائه وضعیت عملکرد این گروه استفاده می کند. علاوه بر OK و Failed، یک هشدار وجود دارد و می توانید متوجه شوید که مصرف کنندگان شما نمی توانند با سرعت تولید کنار بیایند - آنها وقت ندارند آنچه نوشته شده را تصحیح کنند. این سیستم کاملا هوشمند و استفاده آسان است.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

این همان چیزی است که پاسخ API به نظر می رسد. در اینجا گروه bob-live-fifa، پارتیشن refund.update.v1، وضعیت OK، تاخیر 0 - آخرین افست نهایی فلان و فلان است.

تجربه در توسعه سرویس ابزار بازپرداخت با یک API ناهمزمان در کافکا

نظارت updated_at SLA (گیر کرده) قبلا اشاره کردم. به عنوان مثال، محصول به وضعیت آماده برای بازگشت تغییر کرده است. ما Cron را نصب می کنیم که می گوید اگر در عرض 5 دقیقه این شی برای بازپرداخت نرفته باشد (ما خیلی سریع پول را از طریق سیستم های پرداخت برمی گردانیم) قطعاً مشکلی پیش آمده است و این قطعاً موردی برای پشتیبانی است. بنابراین، ما به سادگی Cron را می گیریم، که چنین چیزهایی را می خواند، و اگر آنها بزرگتر از 0 باشند، یک هشدار ارسال می کند.

به طور خلاصه، استفاده از رویدادها زمانی راحت است:

  • اطلاعات توسط چندین سیستم مورد نیاز است.
  • نتیجه پردازش مهم نیست.
  • رویدادهای کمی یا رویدادهای کوچک وجود دارد.

به نظر می رسد مقاله موضوع بسیار خاصی دارد - API ناهمزمان در کافکا، اما در ارتباط با آن می خواهم موارد زیادی را به طور همزمان توصیه کنم.
اول، بعدی HighLoad++ باید تا نوامبر صبر کنیم؛ در آوریل نسخه سنت پترزبورگ وجود خواهد داشت و در ژوئن در مورد بارهای زیاد در نووسیبیرسک صحبت خواهیم کرد.
ثانیاً، نویسنده گزارش، سرگئی زایکا، عضو کمیته برنامه کنفرانس جدید ما در زمینه مدیریت دانش است. KnowledgeConf. این کنفرانس یک روزه است و در 26 آوریل برگزار می شود، اما برنامه آن بسیار فشرده است.
و در ماه مه خواهد بود PHP روسیه и RIT++ (با شامل DevOpsConf) - همچنین می توانید موضوع خود را در آنجا پیشنهاد دهید، در مورد تجربه خود صحبت کنید و از مخروط های پر شده خود شکایت کنید.

منبع: www.habr.com

اضافه کردن نظر