برای یک API سفارش تازه، کشیدن چند سرویس، صف پیام و خوشه کانتینر کار سختی نیست. سختتر این است که توضیح بدهیم هر جزء دقیقاً کدام محدودیت محصول را حل میکند و تیم چه کسی را برای نگهداری آن دارد. نمودار معماری وقتی ارزشمند است که با نیاز امروز و مسیر تغییر فردا ارتباط داشته باشد.
در این مجموعه، رشد یک API سفارش را از استقرار ساده تا تصمیمهای پیچیدهتر دنبال میکنیم. بخش اول به انتخاب ابزار نمیپردازد؛ ابتدا بار، بودجه، توان تیم و نیاز دسترسپذیری را روشن میکنیم. اگر با درخواست HTTP و نقش دیتابیس آشنا باشید، میتوانید بحث را دنبال کنید. خروجی، یک برگه محدودیت و یک طرح اولیه قابل توضیح است؛ بدون ادعای ظرفیت آزمایشنشده.
از یک درخواست واقعی شروع کنیم
مسیر نمونه ما ثبت سفارش است: کاربر اقلام سبد را میفرستد، سرور قیمت و موجودی معتبر را بررسی میکند و سفارش ثبت میشود. پرداخت از مسیر جداگانهای تأیید میشود و مسئول عملیات وضعیت سفارش را میبیند. در این مقاله وارد جزئیات درگاه پرداخت نمیشویم؛ کافی است بدانیم چند مرحله با موفقیت یا خطا مواجه میشوند.
همین سناریو پرسشهای معماری را روشن میکند. چه چیزی باید همان لحظه قطعی باشد؟ چه کاری میتواند کمی دیرتر انجام شود؟ اگر پیام تأیید ارسال نشود، آیا سفارش باید از بین برود؟ اگر پاسخ به مرورگر نرسید ولی سفارش ثبت شد، تلاش دوباره چه میکند؟ این پرسشها از انتخاب نام سرویسها جلوترند.
بار را با «تعداد کاربر» توصیف نکنیم
داشتن ده هزار حساب به معنی ده هزار درخواست همزمان نیست. بخشی از کاربران ممکن است ماهها وارد نشوند؛ بخشی دیگر در زمان فروش ویژه در چند دقیقه فعال شوند. برای تخمین اولیه باید الگوی استفاده را بشناسیم: تعداد درخواست در بازههای مهم، نسبت خواندن به نوشتن، اندازه پاسخ و کار لازم برای هر عملیات.
در API سفارش، دریافت فهرست و ثبت سفارش هزینه یکسانی ندارند. صفحه فهرست ممکن است به چند اتصال جدول و مرتبسازی نیاز داشته باشد؛ ثبت سفارش ممکن است با تراکنش و رقابت روی موجودی درگیر شود. میانگین کل ترافیک میتواند این تفاوت را پنهان کند. بار را برای مسیرهای اصلی جداگانه بنویسید و بازه اوج را از روز عادی جدا نگه دارید.
| مسیر | چیزی که باید بدانیم | منبع تخمین |
|---|---|---|
| فهرست سفارش | اندازه صفحه، فیلتر و دفعات تازهسازی | روال کاری تیم عملیات |
| ثبت سفارش | نرخ اوج و احتمال رقابت روی موجودی | برنامه فروش و داده استفاده |
| گزارش | حجم داده و قابل تأخیر بودن خروجی | نیاز مالی و عملیاتی |
اگر داده نداریم، فرض را با برچسب فرض ثبت میکنیم. سپس یک آزمایش بار بر اساس همان فرض طراحی میشود. ظرفیت نهایی را از روی تعداد هسته سرور یا یک بنچمارک نامرتبط اعلام نمیکنیم؛ ترکیب کوئریها، شبکه و اندازه داده روی نتیجه اثر دارد.
بودجه فقط صورتحساب سرور نیست
هزینه زیرساخت شامل محاسبات، ذخیرهسازی، ترافیک، پایش و نسخه پشتیبان است؛ اما زمان تیم هم هزینه دارد. راهکاری با سرور ارزانتر ممکن است ساعتهای بیشتری برای رسیدگی به خرابی بخواهد. سرویس مدیریتشده ممکن است هزینه مستقیم بالاتری داشته باشد، ولی بعضی مسئولیتهای عملیاتی را کم کند. این معامله را باید برای وضعیت واقعی تیم سنجید.
برای برآورد اولیه، هزینه ثابت و هزینه وابسته به رشد را جدا کنید. همچنین بنویسید اگر حجم داده یا ترافیک چند برابر شود، کدام جزء هزینه را بالا میبرد. لازم نیست امروز عدد دقیق سال آینده را بدانیم؛ باید بدانیم صورتحساب از چه بخشهایی تشکیل میشود و کدام فرضها حساساند.
توان تیم یک محدودیت فنی واقعی است
اگر فقط یک نفر با دیتابیس و استقرار آشناست، معماری به همان اندازه که به فناوری وابسته است به حضور آن فرد هم وابسته میشود. افزودن ابزار تازه ممکن است این وابستگی را بیشتر کند. پیش از انتخاب چند جزء مستقل، بپرسید چه کسی هشدار شبانه را بررسی میکند، چه کسی بازیابی را تمرین کرده و چه کسی میتواند نسخه جدید را برگرداند.
سادگی در اینجا به معنی حذف دقت نیست. یک برنامه و یک دیتابیس هم به تنظیم امن، پایش و پشتیبان نیاز دارند. تفاوت در تعداد نقاطی است که باید فهمیده و هماهنگ شوند. اگر با یک استقرار ساده میتوان نیاز فعلی را پاسخ داد، هزینه توزیع را باید با منفعت مشخص توجیه کرد.
دسترسپذیری را از نگاه کاربر تعریف کنیم
بالابودن پردازش برنامه الزاماً به معنی قابل استفاده بودن سرویس نیست. شاید صفحه اصلی پاسخ دهد ولی ثبت سفارش بهدلیل قطع ارتباط دیتابیس شکست بخورد. به همین دلیل باید مشخص کنیم کدام رفتار برای کاربر مهم است و چگونه موفقیت آن را میسنجیم.
برای مثال، میتوان نسبت درخواستهای معتبر ثبت سفارش که با نتیجه درست تمام میشوند و زمان پاسخ همان مسیر را جداگانه بررسی کرد. این فقط پیشنهاد تعریف شاخص است؛ مقدار هدف باید با نیاز کسبوکار، هزینه و داده پایه تعیین شود. هدف «هرگز خراب نشود» ابزار تصمیمگیری خوبی نیست، چون درباره محدوده سنجش و هزینه قابل قبول چیزی نمیگوید.
در بخشهای بعد SLI و SLO را دقیقتر باز میکنیم. فعلاً روشن کنید کدام خرابی مانع کار اصلی است و چه میزان تأخیر یا توقف چه اثری دارد. گزارش روزانه ممکن است کمی دیر برسد و کار متوقف نشود؛ ثبت سفارش شرایط متفاوتی دارد.
دو سؤال مستقل درباره داده
اول اینکه پس از خرابی، از دست رفتن چه مقدار تغییر قابل قبول است؛ دوم اینکه برگشت سرویس چقدر میتواند طول بکشد. پاسخ این دو سؤال یکی نیست. شاید نسخه پشتیبان نسبتاً تازه داشته باشیم، ولی بازیابی آن چند ساعت زمان ببرد. یا برنامه زود بالا بیاید، اما دادهای که باید حفظ میشد موجود نباشد.
پشتیبان صرفاً فایلی نیست که در جایی ساخته شده باشد. باید مشخص باشد چه کسی آن را بازمیگرداند، چه دسترسیهایی لازم دارد و سازگاری داده چگونه بررسی میشود. در این مرحله برنامه تمرین بازیابی را ثبت کنید؛ درباره زمان بازیابی وعدهای ندهید که با یک اجرای واقعی سنجیده نشده است.
طرح اولیه میتواند کوچک بماند
برای API نمونه، یک نقطه ورود HTTPS، یک برنامه ماژولار و یک دیتابیس رابطهای میتواند گزینه اولیه قابل بررسی باشد. فایلهای کاربر باید سیاست ذخیرهسازی روشن داشته باشند و لاگها و پشتیبانها بیرون از عمر کوتاه کانتینر نگهداری شوند. این یک پیشنهاد آموزشی است؛ انتخاب نهایی به محدودیتهای ثبتشده وابسته است.
در این طرح، سفارش و موجودی در مرزهای کدی مشخص قرار میگیرند، ولی هنوز الزاماً سرویس شبکهای جدا نیستند. این انتخاب امکان استفاده از تراکنشهای روشنتر را حفظ میکند. اگر بعداً شواهد نشان داد بخشی بار یا چرخه انتشار مستقل دارد، میتوان مرز استقرار را بازبینی کرد.
اضافهکردن صف برای ارسال پیام تأیید هم باید با رفتار خطا همراه باشد. انتقال کار به پسزمینه فقط زمان پاسخ درخواست را تغییر نمیدهد؛ حالا با تأخیر، تلاش دوباره و احتمال اجرای تکراری هم روبهرو هستیم. درباره این بخش در قسمتهای صف، idempotency و outbox بهطور مستقل بحث میکنیم.
شرط رشد را پیش از رسیدن به بحران بنویسیم
بهجای وعده «بعداً مقیاس میدهیم»، نشانههای بازبینی را مشخص کنید. مثلاً اگر مسیر اصلی با داده نماینده و تنظیم مناسب به هدف تأخیر نمیرسد، ابتدا گلوگاه را پیدا میکنیم. اگر کوئری عامل کندی باشد، افزودن نسخه بیشتر برنامه ممکن است فقط فشار دیتابیس را بالا ببرد.
یا اگر تیم نمیتواند تغییر یک بخش را بدون هماهنگی با همه منتشر کند، باید علت را بررسی کرد: آیا مرزبندی کد ضعیف است یا واقعاً استقرار مستقل لازم داریم؟ هر مشکل هماهنگی با میکروسرویس حل نمیشود. گاهی همان تفکیک مسئولیت در برنامه واحد، تغییر را بسیار سادهتر میکند.
برگه محدودیت را تکمیل کنیم
یک صفحه با این موارد بسازید: کار اصلی، مسیرهای حساس، تخمین بار و منبع آن، هدفهای کیفیت، بودجه، توان تیم، نیاز داده و فرضهای باز. کنار هر مورد، مسئول و زمان بازبینی را بنویسید. این سند باید با یادگیری تازه تغییر کند؛ قرار نیست برای همیشه ثابت بماند.
تمرین این بخش این است که ثبت سفارش و گزارش مدیریتی را در همان برگه مقایسه کنید. کدامیک به نتیجه فوری نیاز دارد؟ خرابی هرکدام چه اثری میگذارد؟ کدام فرض را میتوان با یک آزمایش کوچک روشن کرد؟ در بخش بعد، بر پایه همین محدودیتها سراغ معماری ماژولار یکپارچه میرویم و میبینیم پیش از جداکردن سرویسها، مرز ماژولها را چگونه طراحی کنیم.
منابع
- فصل هدفهای سطح خدمت در کتاب Google SRE؛ مرجع تعریف شاخص و هدف کیفیت.
- AWS Well-Architected Framework؛ چارچوب مرور تصمیمها از نظر عملیات، قابلیت اتکا و هزینه.
