مقدمه: مشکلات داده های توزیع شده در میکروسرویس ها
معمولاً وقتی نرم افزار را به روشی یکپارچه توسعه می دهیم. ما از پایگاه داده های رابطه ای که شامل قابلیت ACID برای تراکنش ها هستند استفاده می کنیم. Atomicity، ACID Consistency، مختصر چهار کلمه Isolation و Durability، به ما اطمینان می دهد که به زودی در این مورد بحث خواهیم کرد.
اول، Atomicity: که به عنوان شرط همه یا هیچ نیز شناخته می شود، تضمین می کند که در یک تراکنش، اگر چندین عملیات انجام شود، یا همه آنها به طور کامل انجام می شود یا هیچ یک از آنها انجام نمی شود. برای مثال در یک پایگاه داده باید اطلاعات را در یک جدول به روز کرد و در جدول دیگر اطلاعات را حذف کرد و نتیجه انجام این دو عمل در جدول سوم نوشته می شود. در این شرایط یا سه عملیات به طور کامل انجام می شود و یا به هر دلیلی هیچ عملیاتی انجام نمی شود، تمام فرآیندها حذف می شوند و اگر فکر کنیم در مرحله حذف مشکل پیدا کردیم، اطلاعات به روز شده برمی گردد. به وضعیت خود باز خواهند گشت
دوم، سازگاری. سازگاری که سازگاری نیز نامیده می شود، تضمین می کند که اگر یک تراکنش کامل انجام شود، پایگاه داده قطعا از یک حالت صحیح به حالت صحیح دیگر خواهد رفت. مثلاً حساب 1 دارای 100 و حساب 2 دارای 200 است و ما در بانک مجموعاً 300 داریم. حال اگر 50 درام از حساب شماره 1 به حساب شماره 2 منتقل شود، پس از انجام تراکنش، قطعاً در حساب شماره 1 50 و در شماره 2 250 خواهد بود و کل مبلغ همچنان 300 باقی می ماند اما کل مبلغ. نمیشه مثلا از انتقال 320 درام میشه.
سوم، جداسازی: هنگامی که چندین تراکنش به طور همزمان در پایگاه داده در حال اجرا هستند، تضمین می کند که این تراکنش ها از وجود یکدیگر مطلع نیستند و اجرای همزمان چندین تراکنش بر اجرای تراکنش های دیگر تأثیر منفی نمی گذارد. هنگامی که تراکنش ها نیاز به تأثیرگذاری بر یکدیگر دارند، این اثر در نهایت نتیجه را بررسی می کند تا ترتیب به جای اینکه به طور همزمان انجام شود، به صورت متوالی انجام شود.
چهارم، دوام: معروف به قانون دوام، به ما این اطمینان را می دهد که پس از اتمام تراکنش، نتیجه اجرای آن تحت هیچ شرایطی به طور تصادفی از پایگاه داده حذف و لغو نخواهد شد.
به لطف این ویژگی زیبا، در یک برنامه یکپارچه می توانیم فقط یک تراکنش را شروع کنیم و تعداد زیادی عملیات را انجام دهیم و تراکنش را به اتمام برسانیم و از تمام اطلاعات راضی باشیم.
یکی دیگر از ویژگی های جذاب پایگاه داده های رابطه ای زبان SQL است. شاید استفاده از این زبان از ارزش ها و مزایای بزرگ آن کاسته باشد، اما با کمی دقت خواهیم دید که با کمک این زبان عصای جادویی در دست داریم. با چند دستور، داده های چندین جدول مختلف خوانده و ترکیب می شوند، نتیجه جمع آوری و مشاهده می شود و در پشت صحنه، طراح پرس و جو تفسیر، بهینه سازی و زمان بندی دستورات ما را برای اجرا بر عهده می گیرد. لازم نیست نگران هیچ یک از این مراحل باشید، فقط چوب را تکان دهید و کلمه خاص را بگویید.
متاسفانه، اگر از روش میکروسرویس برای توسعه نرم افزار استفاده کنیم. ما بیشتر این ویژگی ها را از دست خواهیم داد. هنگام توسعه میکروسرویس ها؛ هر سرویس باید تمام داده های خود را به طور مستقل در یک پایگاه داده جداگانه داشته باشد. اگر یک سرویس به داده های سرویس دیگر نیاز دارد. دسترسی به داده های آن سرویس تنها از طریق API که از آن پشتیبانی می کند امکان پذیر است. این روش توسعه تضمین می کند که هر سرویس می تواند خدمات مستقلی را ارائه دهد. علاوه بر این آزادی، هر سرویس را می توان با سخت افزار و سیستم خاص خود پیکربندی کرد، بنابراین می توان موتورهای DB مختلف را برای هر سرویس با توجه به نیازهای خاص آن استفاده کرد. یک سرویس می تواند از NoSQL اختصاصی استفاده کند، در حالی که سرویس دیگر می تواند از پایگاه داده رابطه ای برای ذخیره داده های خود استفاده کند. مثلا، سرویس هایی که به جستجوی متنی نیاز دارند می توانند از Elasticsearch استفاده کنند. در سایر خدمات ممکن است به حفظ روابط بین شعب نیاز داشته باشیم. بنابراین، استفاده از یک پایگاه داده گرافیکی مانند Neo4J کار توسعه را ساده می کند. ترکیب موتورهای مختلف DB در سیستم Polyglot Persistence نامیده می شود.
هنگامی که از این روش های ذخیره سازی داده ها استفاده می کنیم، مزایای زیادی مانند توزیع، عدم اطمینان، کارایی بالا و غیره به دست خواهیم آورد. در کنار این مزایا، برای توزیع گسترده این داده ها با چالش های زیادی روبرو خواهیم بود.
اولین و شاید بزرگترین چالش در این راه، مدیریت تراکنش هایی است که مراحل و داده های آن در متنوع ترین سرویس ها ذخیره می شود. برای مثال فرض کنید ما یک سامانه ثبت نام آنلاین سفروش داریم. در خدمات مدیریت مشتری، داده ها و اعتبار مشتری حفظ می شود. در سرویس سفارش، لازم است قبل از ثبت سفارش، اطلاعات و اعتبار مشتری اعتبارسنجی شود. در یک برنامه یکپارچه، ما به سادگی یک جدول از پایگاه داده درخواست می کنیم، اما در یک برنامه میکروسرویس این امکان وجود ندارد.
توسعه بر اساس رویدادها:
در بسیاری از برنامه ها، راه حل مشکل استفاده از رویدادها است. در این روش هر بار که یکی از داده های مهم سرویس تغییر می کند، رویدادی در سیستم ایجاد و ارسال می شود و سایر سرویس های موجود در سیستم می توانند این رویداد را مدیریت کرده و در صورت لزوم داده های داخلی خود را با توجه به این رویداد به روز کنند. این تغییر داده به تنهایی می تواند چندین رویداد دیگر را در سیستم ایجاد کند.
رویدادها همچنین می توانند برای پیاده سازی تراکنش های برنامه مورد استفاده قرار گیرند. هر تراکنش شامل چندین مرحله است. در هر مرحله داده ها در میکروسرویس به روز می شوند و پس از اتمام این مرحله رویدادی ایجاد می شود که مرحله بعدی را راه اندازی می کند و در نهایت با تکمیل تمام مراحل تراکنش به پایان می رسد.
ایجاد و ارسال رویدادها در عملیات داخلی:
یکی از راه های رسیدن به اتمی در این مورد، ایجاد یک فرآیند چند مرحله ای و مدیریت مراحل با استفاده از تراکنش های محلی است. تکنیکی که برای اجرای این روش باید استفاده کنیم، ایجاد جدولی برای رویدادها است. این جدول به عنوان یک صف پیام در سیستمی که رویدادها در آن ذخیره می شوند عمل می کند. هنگامی که یک موجودیت در سیستم تغییر می کند، داده های رویداد مربوط به این تغییر نیز در این جدول ذخیره می شود. این ثبت داده ها با تغییر داده های اصلی در تراکنش انجام می شود و به این ترتیب می توان از گم نشدن داده های رویداد اطمینان حاصل کرد و در صورت بروز مشکل، داده ها در پایگاه داده ثبت می شوند. مشکل سرویس حل شده است، ارسال رویدادها از سر گرفته می شود.
در این فرآیند ابتدا سفارش ثبت می شود و در عین حال رویداد با وضعیت تحویل نشده در جدول ثبت می شود. پس از آن داده ها در جدول Event پیدا شده و Event به سیستم ارسال می شود و در صورت انجام صحیح عملیات وضعیت Event به Sent تغییر می کند و تراکنش با موفقیت انجام می شود.
بزرگترین مزیت استفاده از این روش این است که اطمینان حاصل شود که رویداد 2PC برای هر تراکنش داخلی بدون دخالت روال منتشر می شود. یکی دیگر از مشکلات این روش، مشکل پیاده سازی در هنگام کار با مقداری NoSql و این احتمال است که توسعه دهنده فراموش کرده است رویداد را ذخیره یا مدیریت کند.
بررسی گزارش تراکنش:
راه بعدی برای دستیابی به اتمی بدون استفاده از 2PC، تولید و ارسال رویدادها با استفاده از Transaction Logs است. در این روش زمانی که برنامه اصلی داده ها را تغییر می دهد، تغییراتی غیر از داده های اصلی نیز در Transaction Log ثبت می شود. حال در یک برنامه دیگر به این Transaction Log میرسیم و تغییرات را از این فایل دریافت میکنیم و رویدادهای مناسب را ایجاد میکنیم و در سیستم منتشر میکنیم.
نمونه ای از این رویکرد را می توان در پایگاه داده لینکدین یافت. با استفاده از این سیستم میتوانید گزارشهای موتورهای مختلف DB مانند Oracle یا MySql را پردازش کنید و رویدادهای سیستم را گزارش کنید و تغییرات دادهها را شناسایی کنید.
به عنوان مثال دیگری از این روش می توان به جریان ها در AWS DynamoDB اشاره کرد. به لطف این مکانیسم، تمام تغییرات داده ها از جمله ضبط، حذف و به روز رسانی به مدت 24 ساعت به ترتیب زمانی نگهداری می شوند. اکنون برنامه مدیریت تراکنش می تواند ذخیره کند. داده های این جدول رویدادهای مرتبط با آنها را بخوانید و منتشر کنید.
باز هم در پایان فصل باید مزایا و معایب استفاده از روش توسعه را در نظر بگیریم. یکی از مزایای این روش تضمین ایجاد و توزیع رویداد بدون دخالت 2PC است. با این روش، رویه تولید و توزیع رویداد از حلقه اصلی برنامه حذف می شود و این کار می تواند به ساده سازی روند تولید و بهبود عملکرد کمک کند. بزرگترین عیب این روش تفاوت در گزارش تراکنش ها در موتورهای مختلف پایگاه داده است. حتی گاهی اوقات ساختار و شرایط گزارش تراکنش بین دو نسخه مختلف از یک پایگاه داده متفاوت است. دشواری استخراج داده های سطح بالا، مانند اطلاعات رویداد، از داده های سطح پایین ذخیره شده در ثبات ها، یکی دیگر از معایب این روش پیاده سازی است.
استفاده از منبع رویداد:
این روش از روش اتمیزه کردن کاملاً متفاوتی با آنچه تاکنون دیدهایم استفاده میکند. در این روش به جای اینکه وضعیت یک شی را ذخیره کنیم، اتفاقاتی را که برای جسم رخ داده است را ثبت می کنیم. حال برای دسترسی به وضعیت فعلی یک شی، می توانیم اتفاقاتی را که برای آن اتفاق افتاده از ابتدا اجرا کنیم تا شی دوباره در آخرین حالت قرار گیرد. در این روش هیچ داده ای تغییر نمی کند و هیچ داده ای حذف نمی شود، فقط رویدادها بر روی آیتم ها نمایش داده می شوند و به ترتیب به پایگاه داده اضافه می شوند. ذخیره رویدادها به طور پیش فرض یک عملیات اتمی است.
به این ترتیب، تمام اطلاعات مربوط به فعالیت در پایگاه داده ثبت می شود. داده های رویداد را می توان در جداول و پایگاه های داده استاندارد نیز ذخیره کرد. اما پایگاه داده های جداگانه ای نیز برای ذخیره اطلاعات رویدادها وجود دارد، مانند فروشگاه رویداد. همچنین فروشگاه رویداد وجود دارد که یک API جداگانه برای ضبط و مدیریت داده های رویداد ارائه می دهد.
استفاده از این روش مزایای زیادی دارد و بسیاری از مشکلات را حل می کند، به عنوان مثال می توانید به راحتی در هر زمانی از گذشته تا حال وضعیت موجودیت را دریافت کنید. شما به راحتی می توانید روند انجام شده را مشاهده کنید تا زمانی که واحد در وضعیت فعلی خود قرار گیرد. برای اطلاع از دلیل و شرایط تغییر وضعیت موجودیت ها و ... نیازی به نگهداری داده های اضافی نیست.
اما در کنار این مزایا، یکی از معایب این روش پیچیدگی اجرای این روش یا بهتر بگوییم نگرش جدیدی است که باید نسبت به مقوله داده و حفظ وضعیت موجودیت ها داشته باشیم. مشکل دوم در این مدل این است که اگر رویدادهای موجودیت زیادی وجود داشته باشد، انجام فرآیندهای لازم برای رسیدن به وضعیت نهایی موجودیت، زمان زیادی می برد. سومین و آخرین عیب این روش که در این مقاله به بررسی آن خواهیم پرداخت، مشکل بازیابی اطلاعات است. جستجوی منظم با این روش آسان نیست و برای استخراج پاسخ یک پرس و جو ساده از بسیاری از رویدادها به پردازش زیادی نیاز داریم.
اگرچه این مدل یک مدل مستقل است که به تنهایی می تواند این کار را انجام دهد، اما اغلب برای پوشش مشکلات و ایرادات این مدل ساخته شده و به سیستم CQRS اضافه می شود.
نتیجه.
در معماری میکروسرویس، هر میکروسرویس پایگاه داده و داده های خاص خود را دارد و می تواند خدمات خود را مستقل از سایر خدمات ارائه دهد. میکروسرویسهای مختلف همچنین میتوانند از موتورهای DB مختلف استفاده کنند که قابلیتهای خاصی برای آن سرویس خاص دارند که میتوانند کار را بهبود یا سادهتر کنند. اما این روش پیاده سازی چالش هایی را در اشتراک و توزیع داده ها ایجاد می کند که توسعه دهندگان باید برای آن چاره ای بیندیشند. یکی از راه حل های این چالش ها استفاده از رویدادهای سیستمی است که در این مقاله به بررسی آن ها پرداخته ایم.
- ۰۱/۰۹/۱۹