از سال ۲۰۱۹، قانون برچسبگذاری اجباری در روسیه به اجرا درآمده است. این قانون برای همه گروههای محصول اعمال نمیشود و مهلتهای برچسبگذاری اجباری برای هر گروه محصول متفاوت است. دخانیات، کفش و دارو اولین محصولاتی هستند که مشمول برچسبگذاری اجباری میشوند و سایر محصولات مانند عطر، پارچه و شیر نیز بعداً اضافه خواهند شد. این نوآوری قانونی باعث توسعه راهحلهای جدید فناوری اطلاعات شده است که به همه طرفهای درگیر در این فرآیند - هم دولت و هم همه سازمانهایی که محصولات دارای برچسبگذاری اجباری را میفروشند - امکان میدهد کل چرخه عمر محصول را از تولید تا خرید توسط مصرفکننده نهایی ردیابی کنند.
در X5، سیستمی که کالاهای برچسبگذاری شده را ردیابی کرده و دادهها را با دولت و تأمینکنندگان تبادل میکند، «مارکوس» نام دارد. ما توضیح خواهیم داد که چگونه و توسط چه کسی آن را توسعه داده است، فناوری آن چیست و چرا ما چیزی برای افتخار داریم.

بار زیاد واقعی
«مارکوس» مشکلات متنوعی را حل میکند که اصلیترین آنها ادغام بین سیستمهای اطلاعاتی X5 و سیستم اطلاعات دولتی برای محصولات علامتگذاری شده (GIS MP) برای ردیابی جابجایی محصولات علامتگذاری شده است. این پلتفرم همچنین تمام کدهای علامتگذاری دریافتی و کل تاریخچه جابجایی آنها در بین مراکز را ذخیره میکند و به حذف محصولات علامتگذاری شده اشتباه کمک میکند. به عنوان مثال، محصولات دخانی که در گروههای اول کالاهای علامتگذاری شده قرار داشتند، فقط یک کامیون سیگار حاوی تقریباً ۶۰۰۰۰۰ بسته است که هر کدام کد منحصر به فرد خود را دارند. وظیفه سیستم ما ردیابی و تأیید قانونی بودن جابجایی هر بسته از این نوع بین انبارها و فروشگاهها و در نهایت، تأیید مجاز بودن آنها برای فروش به مشتری نهایی است. ما تقریباً ۱۲۵۰۰۰ تراکنش نقدی در ساعت ثبت میکنیم و همچنین باید نحوه رسیدن هر بسته از این نوع به فروشگاه را ثبت کنیم. بنابراین، با در نظر گرفتن تمام جابجاییها بین مراکز، انتظار داریم دهها میلیارد رکورد در سال ثبت شود.
تیم م
اگرچه مارکوس به عنوان یک پروژه در X5 در نظر گرفته میشود، اما با رویکردی مبتنی بر محصول در حال اجرا است. این تیم از اسکرام استفاده میکند. این پروژه تابستان گذشته آغاز شد، اما اولین نتایج تنها در ماه اکتبر به دست آمد - یک تیم کاملاً تشکیل شد، معماری سیستم توسعه داده شد و تجهیزات خریداری شد. این تیم در حال حاضر شامل 16 نفر است که شش نفر از آنها بر توسعه بکاند و فرانتاند و سه نفر بر تجزیه و تحلیل سیستمها تمرکز دارند. شش نفر دیگر مسئول تست دستی، بارگذاری و خودکار و همچنین نگهداری محصول هستند. ما همچنین یک متخصص SRE داریم.
تیم ما محدود به توسعهدهندگان نیست؛ تقریباً همه میدانند چگونه برنامهنویسی کنند و تستهای خودکار، اسکریپتهای بارگذاری و اسکریپتهای اتوماسیون مینویسند. ما به این موضوع توجه ویژهای داریم، زیرا حتی پشتیبانی محصول نیز به سطح بالایی از اتوماسیون نیاز دارد. ما همیشه سعی میکنیم به همکارانمان که قبلاً برنامهنویسی نکردهاند، مشاوره و کمک ارائه دهیم و وظایف کوچکی را به آنها محول کنیم تا روی آنها کار کنند.
به دلیل همهگیری ویروس کرونا، ما کل تیم خود را به کار از راه دور منتقل کردیم. داشتن تمام ابزارهای مدیریت توسعه و گردش کار داخلی در Jira و GitLab این انتقال را بسیار آسان کرد. ماهها کار از راه دور نشان داده است که بهرهوری تیم کاهش نیافته است و بسیاری از افراد در کار احساس راحتی بیشتری میکنند. تنها چیزی که کم داریم ارتباط حضوری است.
جلسه تیمی قبل از دورکاری

جلسات در حین کار از راه دور

پشته فناوری راهکار
GitLab مخزن استاندارد و ابزار CI/CD برای X5 است. ما از آن برای ذخیرهسازی کد، آزمایش مداوم و استقرار در سرورهای تست و تولید استفاده میکنیم. ما همچنین از بررسی کد استفاده میکنیم که برای تأیید هرگونه تغییر ایجاد شده توسط یک توسعهدهنده، حداقل به دو همکار نیاز دارد. آنالیزورهای کد استاتیک SonarQube و JaCoCo به ما کمک میکنند تا کد خود را تمیز نگه داریم و سطح مورد نیاز پوشش تست واحد را تضمین کنیم. همه تغییرات کد باید از این بررسیها عبور کنند. همه موارد تست دستی متعاقباً خودکار میشوند.
برای پیادهسازی موفقیتآمیز فرآیندهای کسبوکار مارکوس، ما مجبور بودیم تعدادی از مشکلات فنی را حل کنیم که به ترتیب در مورد هر یک از آنها بحث خواهیم کرد.
مشکل ۱. نیاز به مقیاسپذیری افقی سیستم
برای حل این مشکل، ما یک رویکرد معماری میکروسرویسها را انتخاب کردیم. درک مسئولیتهای سرویسها بسیار مهم بود. ما سعی کردیم آنها را بر اساس عملیات تجاری، با در نظر گرفتن جزئیات فرآیندها، دستهبندی کنیم. به عنوان مثال، رسید انبار یک عملیات نادر اما با حجم بسیار بالا است. این عملیات نیاز به دریافت سریع اطلاعات از نهاد ناظر ایالتی در مورد واحدهای دریافتی دارد که میتواند تا ۶۰۰۰۰۰ واحد در یک محموله واحد باشد، تأیید مجاز بودن پذیرش این اقلام در انبار و انتقال تمام اطلاعات لازم به سیستم اتوماسیون انبار. از سوی دیگر، حمل و نقل از انبارها بسیار فشردهتر است اما حجم دادههای کمتری را شامل میشود.
ما تمام سرویسها را با استفاده از اصل بیحالتی پیادهسازی میکنیم و حتی سعی میکنیم عملیات داخلی را با استفاده از چیزی که خودمان آن را خود-موضوعات کافکا مینامیم، به مراحلی تقسیم کنیم. این زمانی است که یک میکروسرویس پیامی را به خودش ارسال میکند که به ما امکان میدهد بار را روی عملیاتهای با منابع بیشتر متعادل کنیم و نگهداری محصول را سادهتر کنیم، اما بعداً بیشتر در مورد آن صحبت خواهیم کرد.
ما تصمیم گرفتیم ماژولهای تعامل با سیستمهای خارجی را در سرویسهای جداگانهای تفکیک کنیم. این به ما اجازه داد تا مشکل تغییر مکرر APIهای سیستمهای خارجی را بدون هیچ تأثیری بر سرویسهای دارای عملکرد تجاری، حل کنیم.

همه میکروسرویسها در یک کلاستر OpenShift مستقر میشوند که هم مشکل مقیاسپذیری را برای هر میکروسرویس حل میکند و هم نیاز به ابزارهای کشف سرویس شخص ثالث را از بین میبرد.
چالش ۲. نیاز به حفظ بار زیاد و تبادل دادههای بسیار فشرده بین سرویسهای پلتفرم: تنها در مرحله راهاندازی پروژه، تقریباً ۶۰۰ عملیات در ثانیه انجام میشود. ما انتظار داریم با اتصال خردهفروشان به پلتفرم ما، این رقم به ۵۰۰۰ عملیات در ثانیه افزایش یابد.
این مشکل با استقرار یک کلاستر کافکا و حذف تقریباً کامل تعاملات همزمان بین میکروسرویسهای پلتفرم حل شد. این امر مستلزم تجزیه و تحلیل بسیار دقیقی از الزامات سیستم است، زیرا همه عملیات نمیتوانند ناهمزمان باشند. علاوه بر این، ما صرفاً رویدادها را از طریق یک کارگزار منتقل نمیکنیم؛ ما همچنین تمام اطلاعات تجاری مورد نیاز را در پیام منتقل میکنیم. بنابراین، اندازه پیام میتواند به چند صد کیلوبایت برسد. محدودیت اندازه پیام کافکا ما را ملزم میکند که اندازه پیامها را به طور دقیق پیشبینی کنیم و در صورت لزوم آنها را تقسیم کنیم، اما این تقسیمبندی منطقی است و به عملیات تجاری گره خورده است.
برای مثال، ما کالاهایی که با ماشین میرسند را در جعبهها جدا میکنیم. میکروسرویسهای جداگانهای برای عملیات همزمان اختصاص داده میشوند و تست بار کامل انجام میشود. استفاده از کافکا چالش دیگری را ایجاد کرد: آزمایش عملکرد سرویس ما با ادغام کافکا، تمام تستهای واحد ما را ناهمزمان میکند. ما این مشکل را با نوشتن متدهای کاربردی خودمان با استفاده از Embedded Kafka Broker حل کردیم. این کار نیاز به نوشتن تستهای واحد برای متدهای منفرد را از بین نمیبرد، اما ما ترجیح میدهیم موارد پیچیده را با استفاده از کافکا تست کنیم.
ما توجه زیادی به ردیابی لاگها داشتهایم تا اطمینان حاصل کنیم که TraceIdها هنگام وقوع استثنا در حین اجرای سرویس یا هنگام کار با دستهای از کافکا از بین نمیروند. در حالی که مورد اول مشکل خاصی ایجاد نمیکند، در مورد دوم، ما مجبوریم تمام TraceIdهایی را که یک دسته با آنها دریافت میکند، ثبت کنیم و یکی را برای ادامه ردیابی انتخاب کنیم. سپس، هنگام جستجو بر اساس TraceId اصلی، کاربر میتواند به راحتی TraceId ای را که ردیابی با آن ادامه یافته است، شناسایی کند.
مشکل ۳. نیاز به ذخیره حجم زیادی از دادهها: X5 سالانه بیش از ۱ میلیارد برچسب فقط برای دخانیات دریافت میکند. این برچسبها نیاز به دسترسی مداوم و سریع دارند. در مجموع، سیستم باید تقریباً ۱۰ میلیارد رکورد از تاریخچه دادههای محصول برچسبگذاری شده را پردازش کند.
برای حل مشکل سوم، ما پایگاه داده MongoDB NoSQL را انتخاب کردیم. ما یک شارد (shard) از ۵ گره داریم و هر گره دارای یک مجموعه Replica از ۳ سرور است. این به ما امکان میدهد سیستم را به صورت افقی با اضافه کردن سرورهای جدید در خوشه قرار داده و تحمل خطای آن را تضمین کنیم. در اینجا، با مشکل دیگری مواجه شدیم: تضمین تراکنشپذیری در یک خوشه Mongo در حین استفاده از میکروسرویسهای مقیاسپذیر افقی. به عنوان مثال، یکی از وظایف سیستم ما تشخیص تلاشها برای کپی کردن محصولات با کدهای علامتگذاری یکسان است. این امر منجر به همپوشانی با اسکنهای اشتباه یا عملیات صندوقدار اشتباه میشود. ما کشف کردیم که چنین کپیهایی میتوانند هم در یک دسته کافکا که در حال پردازش است و هم در دو دسته موازی رخ دهند. بنابراین، بررسی کپیها با پرس و جو از پایگاه داده هیچ نتیجهای نداشت. برای هر میکروسرویس، ما بر اساس منطق تجاری آن سرویس، به طور جداگانه به این مشکل پرداختیم. به عنوان مثال، برای رسیدها، یک چک در دسته اضافه کردیم و پردازش جداگانهای برای کپیها هنگام درج انجام دادیم.
برای اطمینان از اینکه تعاملات کاربران با تاریخچه تراکنشها بر مهمترین چیز - یعنی عملکرد فرآیندهای تجاری ما - تأثیر نگذارد، ما تمام دادههای تاریخی را در یک سرویس جداگانه با یک پایگاه داده جداگانه، که اطلاعات را از طریق کافکا نیز دریافت میکند، ایزوله کردهایم. این امر به کاربران اجازه میدهد تا بدون تأثیر بر سرویسهایی که دادههای تراکنشهای فعلی را پردازش میکنند، با یک سرویس ایزوله کار کنند.
وظیفه ۴. پردازش مجدد صف و نظارت:
سیستمهای توزیعشده ناگزیر با مشکلات و خطاهایی در رابطه با دسترسی به پایگاه داده، صفها و منابع داده خارجی مواجه میشوند. در مورد مارکوس، منبع این خطاها ادغام با سیستمهای خارجی است. برای تلاش مجدد درخواستها برای پاسخهای خطا با یک زمان انقضای مشخص، در حالی که پردازش درخواستهای موفق در صف اصلی ادامه مییابد، به راهحلی نیاز بود. برای دستیابی به این هدف، مفهوم «تلاش مجدد مبتنی بر موضوع» انتخاب شد. برای هر موضوع اصلی، یک یا چند موضوع تلاش مجدد ایجاد میشود که پیامهای خطا به آنها هدایت میشوند و تأخیر در پردازش پیامها از موضوع اصلی را از بین میبرند. طرح تعامل به شرح زیر است:

برای پیادهسازی این طرح، لازم بود این راهکار را با Spring ادغام کنیم و از تکرار کد جلوگیری کنیم. ما به طور اتفاقی به یک راهکار مشابه آنلاین، مبتنی بر BeanPostProccessor Spring، برخوردیم، اما به نظر میرسید که بیش از حد دست و پا گیر است. تیم ما یک راهکار سادهتر توسعه داد که به ما امکان میدهد در چرخه ایجاد مصرفکننده Spring ادغام شویم و علاوه بر آن، Retry Consumers را نیز اضافه کنیم. ما یک نمونه اولیه از راهکار خود را به تیم Spring ارائه دادیم؛ میتوانید آن را اینجا مشاهده کنید. تعداد Retry Consumers و تعداد تلاشها برای هر Consumer از طریق پارامترها، بسته به نیازهای فرآیند کسبوکار، پیکربندی میشوند. برای اینکه همه چیز به درستی کار کند، تنها کاری که باقی میماند اضافه کردن حاشیهنویسی org.springframework.kafka.annotation.KafkaListener است که برای همه توسعهدهندگان Spring آشناست.
اگر پیامی پس از تمام تلاشهای مجدد پردازش نشود، با استفاده از DeadLetterPublishingRecoverer شرکت Spring به یک DLT (موضوع نامهی مرده) ارسال میشود. بنا به درخواست تیم پشتیبانی ما، این قابلیت را گسترش داده و سرویس جداگانهای ایجاد کردهایم که امکان مشاهدهی پیامهای ارسالی به DLT، همراه با ردیابی پشته، traceId و سایر اطلاعات مفید آنها را فراهم میکند. ما همچنین نظارت و هشدارها را برای همه موضوعات DLT اضافه کردهایم، بنابراین اکنون، اساساً، ظاهر شدن یک پیام در یک موضوع DLT باعث ایجاد بررسی و ایجاد یک اشکال میشود. این بسیار راحت است - از روی نام موضوع، ما بلافاصله متوجه میشویم که مشکل در کدام مرحله از فرآیند رخ داده است و به طور قابل توجهی جستجوی علت اصلی آن را سرعت میبخشد.

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

عملیات پلتفرم
این پلتفرم در حال حاضر در مرحله تولید است؛ ما روزانه در حال تحویل و ارسال کالا هستیم و مراکز توزیع و فروشگاههای جدید را به هم متصل میکنیم. به عنوان بخشی از این طرح آزمایشی، این سیستم با دستههای محصولات «تنباکو» و «کفش» کار میکند.
کل تیم ما در طرحهای آزمایشی شرکت میکند، مسائل نوظهور را تجزیه و تحلیل میکند و پیشنهادهایی برای بهبود محصول ما، از بهبود گزارشها گرفته تا تغییر فرآیندها، ارائه میدهد.
برای جلوگیری از تکرار اشتباهات، تمام مواردی که در طول نسخه آزمایشی شناسایی شدهاند، در تستهای خودکار منعکس میشوند. تعداد زیادی از تستهای خودکار و واحد، امکان تست رگرسیون و استقرار اصلاحات فوری (hotfix) را تنها در عرض چند ساعت فراهم میکنند.
ما همچنان به توسعه و بهبود پلتفرم خود ادامه میدهیم و دائماً با چالشهای جدیدی روبرو میشویم. اگر علاقهمند باشید، در مقالات بعدی اطلاعات بیشتری در مورد راهحلهای خود به اشتراک خواهیم گذاشت.
منبع: www.habr.com
