توسعهدهنده نرمافزار
ریزسرویسها
مطلبی که میخوانید ترجمه قسمت ۲۱۳ از رادیو مهندسی نرمافزار است. رادیو مهندسی نرمافزار هر یکی دو هفته یک بار مصاحبهای درباره یکی از موضوعات حوزه مهندسی نرمافزار با افراد خبره و با تجربه در موضوع مورد بحث ترتیب میدهد.
در این قسمت، یوهانس تونز با جیمز لوییس، معمار نرمافزار و مدیر شعبه انگلیس ThoughtWorks درباره ریزسرویسها (microservice) صحبت میکند. جیمز، عضو هیأت مشاوره تکنولوژی در ThoughtWorks است که هر ۳ ماه یکبار، ملاقات دارند و رادار تکنولوژی ThoughtWorks را تولید میکنند. (رادار تکنولوژی، نام سندی است که حدوداً هر ۶ ماه یکبار منتشر میشود و در آن تغییراتی که در گرایشهای مربوط به تکنیکها، تکنولوژیها، زبانها و ابزارهای توسعه نرمافزار رخ داده به شکل گرافیکی و توضیحات موجز مطرح میشود- مترجم). علاقه جیمز، تولید نرمافزار از طریق سرویسهای کوچک همکار است. جیمز، در دهه ۹۰ اخترفیزیک خوانده است اما از برنامهنویسی به زبان فرترن خسته شد! او بعد از ۱۵ سال کارهای مدیریت پایگاه داده و بعدها طراحی و معماری نرمافزار، اعتقاد دارد که نوشتن نرمافزار، سادهترین بخش مسأله است و بیشتر کار، مربوط به درست فکر کردن افراد است.
در این مصاحبه یوهانز و جیمز درباره محبوبیت اخیر ریزسرویسها، سبکهای معماری، استقرار، اندازه، تصمیمهای فنی و قراردادهای مبتنی بر مشتری (consumer-driven contracts)، صحبت میکنند. آنها همچنین ریزسرویسها را با معماری سرویسگرا (service-oriented) مقایسه میکنند و بحث را با صحبت در مورد چهرههای شاخص در اجتماع ریزسرویس و اینکه بر دوش بزرگان سواریم به پایان میرسانند.
جیمز، به برنامه خوش آمدی!
خیلی ممنون!
آیا چیزی هست که بخواهی به بیوگرافیات اضافه کنی؟
فکر نکنم. فکر کنم خوب پوشش دادید. من حدود ۸ سال و نیم است که این کار را انجام میدهم. من تغییرات زیادی در صنعت نرمافزار را دیدهام و فکر میکنم تغییرات خیلی خیلی سریع است و فکر میکنم آنچه امروز میخواهیم در مورد آن صحبت کنیم، یکی از آن آخرین تغییرها باشد.
بیا در مورد موضوع عمیق شویم. ما میخواهیم در مورد ریزسرویسها (microservice) صحبت کنیم. ممکن است با این شروع کنید که ریزسرویس چیست؟
بله، البته. ریزسرویس به اعتقاد من، یک برنامه کوچک است که میتواند مستقلاً مستقر (deploy) شود، مستقلاً مقیاس بپذیرد، مستقلاً تست شود و یک مسئولیت منفرد دارد. این مسئولیت منفرد، چند وجه دارد. یکی از این نظر که یک علت منفرد برای تغییرکردن داشته باشد و هم یک علت منفرد برای جایگزین شدن داشته باشد؛ این ضروری است. اما وجوه دیگر مسئولیت منفرد این است که فقط یک کار را انجام دهد و به سادگی برای کسانی که آن را توسعه میدهند قابل فهم باشد.
چنین چیز منفردی، چه میتواند باشد؟
سئوال خوبی است. یک مثال از چیز منفرد میتواند یک مسئولیت منفرد در ارتباط با نیازمندیهای عملکردی یا غیرعملکردی یا نیازمندیهای فراعملکردی (cross-functional) باشد. یک مثال میتواند یک پردازشگر صفی باشد؛ چیزی که یک پیغام را از صف میخواند، بر روی آن یک تکه پردازش کوچک مرتبط با منطق کسبوکار (business logic) انجام میدهد و آن را منتقل میکند. میتواند یک کار غیرعملکردی یا تقاطعی باشد و یا میتواند چیزی باشد که مسئولیتش این باشد که در مورد منبعی (resource) خدمت بدهد و نمایشی از یک منبع باشد. مثلاً (نمایشی از) کاربر، مقاله، ریسک بیمه، ... . چیزی که خیلی خیلی مشخص و کوچک باشد و به تنهایی کار منفردی را انجام دهد.
من این احساس را دارم که ریزسرویسها، اخیراً خیلی محبوب شدهاند. شما در مورد آن صحبت کردهاید، دیگران در مورد آن صحبت کردهاند و ... . چرا اینطور شده است؟
سئوال خوبی است که چرا ریزسرویسها، به ناگهان محبوب شدهاند. حدود ۴ سال پیش، یک کارگاه بود که افراد مختلفی از اجتماعهای مختلف صنعت نرمافزار شرکت داشتند، بهعنوان مثال برخی افراد از اجتماع طراحی مبتنی بر حوزه (domain-driven design)، برخی از اجتماع RESTful، برخی از اجتماع تبادلات پیغام (messaging) و ... . من در این کارگاه شرکت کردم. در آنجا برایم مشخص شد که سئوالهایی درباره اندازه برنامهها هستند که زیاد تکرار میشوند. بهعنوان مثال اینکه فلان برنامه ما طی ۲ سال و نیم یا ۵ سال یا ۱۰ سال آنچنان بزرگ شده است که دیگر نمیتوانیم آن را نگهداری کنیم؛ انجام هر تغییر عملکردی در آن سخت شده است یا اینکه ما میخواهیم بتوانیم این برنامه را بر روی فضای ابری (cloud) مستقر کنیم و بهصورت نرمافزار بهعنوان خدمت (software as a service) عرضه کنیم، اما در حال حاضر ممکن نیست.
در نتیجه آن، این ایده مطرح میشد که برنامهها به مؤلفههای کوچک همکار خرد شوند که در پروسسهای خود اجرا شوند و با همدیگر کار کنند و بتوانند جداگانه نگهداری شده و جداگانه مقیاس بپذیرند. بنابراین مجموعهای از اجتماعهایی که بیشتر تجربی بودند شکل گرفت. تعدادی از آنها در لندن بودند و برپایه برخی افراد شکل گرفتند. مثلاً فکر میکنم خیلیها با بروس استنلی کار کرده باشند (ممکن است اسم به اشتباه تشخیص داده شده باشد - مترجم) و یا دن نورث که در مورد معماریهای با مؤلفههای قابلجایگزینی صحبت میکند و روی ایده جایگزینپذیری، تأکید دارد. فرد جورج هم چند سالی است که در مورد ریزسرویسها صحبت میکند. اینطور دیده میشود که بیشتر در لندن، روی آن کار میشود. در همین حین، Netflix را دارید که در ساحل غربی آمریکا، در مورد آنچه در موردش صحبت میکنیم یعنی معماریهای سرویسگرای ریزدانه، شراکت دارد. آنها هم از همین اصطلاح ریزسرویس استفاده میکنند. تعدادی از این اجتماعهای مختلف بوده است که در طی زمان رشد کردهاند. بنابراین ارائه این رهیافت برای تولید نرمافزار، دوام داشته است. اگر به شرکتهایی با ابعاد Netflix نگاه کنید، چنین رهیافتی تقریباً ضرورت داشت. همان طور که آدریان کاکرافت میگوید، علت این که سیستمها را اینگونه میسازند این است که میخواهند بتوانند تا جایی که ممکن است سریع باشند و تغییرات را سریع اعمال کنند.
به سئوال شما برگردیم که چرا اینقدر محبوب است. من فکر میکنم سازمانهای زیادی هستند که در ۵ سال گذشته، به حد مرگ رسیدهاند. آنها فهمیدهاند که برای اینکه بتوانند مقیاسپذیر باشند و بتوانند در تحویل محصول عملیاتی، کاراتر باشند و بتوانند مزایای چیزهایی از قبیل تحویل مستمر (continuous delivery) را داشته باشند به برخی رهیافتها نیاز دارند که به آنها این اجازه را بدهد که در بُعدهای مختلف بهصورت مستقل، بزرگ شوند. بنابراین فکر میکنم زمانش رسیده بود که ایدههایی مانند ریزسرویسها توفیق یابند زیرا شرکتهای زیادی با مشکل یکسانی روبرو شده بودند.
شما چیز جالبی گفتید که افراد در این موارد، برنامههای بزرگ یکپارچهای (monolithic) داشتهاند که آنها را به ریزسرویسهایی خرد کردند. آیا شکل معمول بکارگیری ریزسرویسها همین است؟
سئوال خوبی است و چیزی است که در واقع با آن درگیر هستم. اگر به بطن سئوال توجه کنیم در واقع این است که آیا با ریزسرویسها آغاز میکنید یا اینکه آنها را بعداً میسازید؟ بهصورت تجربی، غالب سازمانها با یک چیز بزرگ شروع کردند و در طول زمان آن چیز بزرگ را شکستند. اکثر سازمانها چیزی که من به آنها پیادهسازی به سبک ریزسرویسها میگویم را بعداً ساختند. مثلاً Netflix [اینطور است]، مثال دیرینه آن، احتمالاً Amazon است. آنها با یک پایگاه داده بزرگ شروع کردند و بعد به سمت معماری سرویسگرا کشیده شدند. Netflix هم موقعیت کاملاً یکسانی داشت و با یک سیستم با ساختار کاملاً بزرگ یکپارچه آغاز کرد و با حرکت به سمت این نوع معماری سرویسگرای ریزدانه بود که خرد شد.
آیا مثالی دارید که بتوانید کمی بیشتر صحبت کنید و روشنتر کنید که این خرد کردن و رسیدن به این معماری ریزسرویسها چطور رخ میدهد؟
قطعاً. من مشاور خوبی هستم. ۳ سال و نیم پیش من مشتریانی از صنعت بیمه داشتم که درگیر کار با بانکها شدم. آنها این شرایط را داشتند که یک برنامه خیلی بزرگ با سبک چند لایهای مرسوم را نوشته بودند که در آن یک پایگاه داده بزرگ با کلی منطق کاری داشتند، محیط داتنت بود و یک سرویس دسترسی به دادهها داشتند که بر روی آن، یک برنامه بزرگ دیگری داشتند که کلی منطق کاری در آن نوشته شده بود. مشکلی که داشتند این بود که در این برنامه بزرگ که بر روی لایه سرویس نوشته شده بود، محصولات مختلفی را توسعه داده بودند. آنها انواع بیمهها را میفروختند. هر کدام از آنها چرخه تغییرات خودش را داشت. برخی از آنها بالغتر و برخی نابالغتر بودند و همچنان که سازمان رشد میکرد باید جداگانه بر روی این محصولات مختلف، سرمایهگذاری میکردند. متأسفانه از آنجایی که یک برنامه خیلی بزرگ داشتند، همه چیز بههم گره خورده بود، اگر میخواستند تغییراتی بر روی محصولات بیمههای درمانی داشته باشند، باید برای [اتمام] چرخه تغییرات مربوط به محصولات بیمههای خودرو صبر میکردند و یا اگر میخواستند مقیاس محصولات بیمههای خودرو را بزرگ کنند -زیرا عموماً بیمههای خودرو مثلاً در مقایسه با بیمههای عمر ارزش خیلی بیشتری دارند- مقیاس دیگر محصولات هم باید در اندازه بیمههای خودرو بزرگ میشدند. آنها انعطاف زیادی نداشتند و همچنین گرفتار وضعیتی شده بودند که برای اینکه تیمهای توسعهدهندهای که روی این برنامه کار میکردند بتوانند بدون ایجاد مزاحمت برای یکدیگر کار کنند، استراتژیهای بسیار پیچیده و عجیب و غریبی برای انشعاب و ادغام (branch & merge) را به کار گرفته بودند زیرا هرگاه تغییری میدادید بر روی نواحی مختلفی تأثیر میگذاشت.
آنها در شرایط سختی قرار داشتند و طی ۳ سال گذشته در جهت خرد کردن این برنامه به محصولات حرکت کردند. این یک مثال از نحوهای است که ممکن است خرد کنید.
یعنی آنها با جدا کردن یکی از بخشها آغاز کردند؟ طوری که آن بخش همچنان با برنامه یکپارچه مراوده داشته باشد؟
در این مورد خیر، این کار را نکردند. بهجای آن محصولات جدید را جداگانه خارج از سایت تولید کردند. در واقع به سمت بازسازی آن (refactor) نرفتند بلکه مثلاً بخش بیمه درمانی را بهصورت یک ریزسرویس مجزا، توسعه دادند. در واقع داخلش یک پیادهسازیِ SQL ،OR و REST در محیط دات نت داشت که از الگوی مجزاسازی مسئولیتهای فرمان از پرس و جو (command and query responsibility segregation) استفاده میکرد.
مختصراً میگویید که این (الگوی مجزاسازی مسئولیتهای فرمان از پرس و جو) چه معنی میدهد؟
مختصراً میگویم باشه :-)
در آن مورد، ایده مربوط به جدا کردن خواندن از نوشتن بود. پیادهسازیهای دسترسی به دادهها و ذخیره دادهها بهطور مجزا از هم از طریق پرسوجوها و فرامین انجام میشود. وقتی فرامینی اجرا میکردید، رخدادهایی تولید میشدند که مدل مربوط به خواندن را پر میکردند و بعد برنامهتان از آن مدل میخواند. در جایی مینوشتید و از جای دیگری میخواندید و مؤلفههایی که این کارها را میکردند بهصورت مجزا توسعه داده شدند و بهصورت سرویسهای مجزا بهعنوان ریزسرویسها اجرا میشدند. من در گروهی بودم که اسم افراد را به خاطر نمیآورم اما یک ارائه خوب، در مورد نحوهای که به این رهیافت رسیدند از یکی از آن معمارها در کنفرانس QCon مربوط به مارچ ۲۰۱۴ لندن وجود دارد. آنها برنامه را با تفکیک به صفحات خرد کردند. آنها زیرسرویسی داشتند که مسئول بخشهایی از یک سایت منفرد بود.
بیایید کمی در مورد این صحبت کنیم که از لحاظ فنی چطوری ریزسرویسها را میسازید. مثلاً اگر بخواهم ریزسرویسی برای اعتبارسنجی کاربر، بنویسم از چه زبانی استفاده میکنم؟ بر طبق چه استانداردهایی مینویسم؟
یکی از راهنماها و اصول درباره آن این است که مورد به مورد، [بسته به خودش] کار را تنظیم کنید. بهجای اینکه یک زبان خاص را انتخاب کنید یا یک ذخیرهگاه داده خاص برای همه بسته محصولاتتان داشته باشید، این انعطاف را دارید که بر اساس اطلاعات و ابزارها تصمیماتی آگاهانه بگیرید. یک انتخاب خاص که [فقط همان] صحیح باشد وجود ندارد.
مثلاً در مورد سرویس کاربران که شما صحبت کردید میتوانید آن را به سادگی در سیشارپ یا جاوا و یا هر نوع زبان مبتنی بر مدل دیگری بسازید. اینجا من خطر دفاع کردن در مقابل این همه افرادی که این روزها عاشق جاوااسکریپت هستند را میپذیرم :-) من شخصاً طرفدار آن نیستم، اگر میخواهید بدانید میتوانید علتش را از مارتین فاولر بپرسید! اما تقریباً با هر زبانی میشود این کار را کرد. مسأله مهم این است که بهجای اینکه بهدنبال استفاده از بستههای سنگین اضافی باشید، بسته را سبک نگاه دارید. این مطلب شامل این میشود که بهجای اینکه [سرویسها را] بر روی محفظههای برنامه (application container) مثلاً Tomcat و دیگر محفظهها مستقر کنید به این فکر کنید که آیا میتوانید از ابزارهای سبکتر مثلاً Embedded Tomcat و Embedded Jetty استفاده کنید. این در مورد جاوا بود. دات نت هم به تنهایی مکان جالبی است زیرا هر آنچه بهطور سنتی در فضای داتنت انجام دادهایم بر روی IIS مستقر میشود. همه برنامههایی که از محیطهای مختلف میآیند اینطور هستند. اما حتی در داتنت هم، این جنبش وجود دارد که آموختههای اجتماعهای لینوکس و جاوا را بهکار بگیرند و از سیستمهای تعبیه شده استفاده کنند. بنابراین در فضای داتنت هم راههای جایگزینی وجود دارد که در کنار برنامههایتان از ابزارهای سبکتر استفاده کنید.
اما وقتی که به لایه ارتباطات نگاه کنیم، همه چیز به فرم HTTP و REST است ...
من خودم اقرار دارم که طرفدار سبک معماری REST هستم. فکر میکنم به این طریق به خیلی از مشکلاتی که طی چندین دهه اخیر در زمینه یکپارچهسازی (integration) شناسایی کردهایم، چیره شدیم.
بهعنوان مثال؟
بهعنوان مثال، نسخهدهیهایی که در ارتباط با مجموعه نتایج خروجی باید داشته باشیم آسانتر قابل انجام خواهد بود. البته باید گفت که در ریزسرویسها چنین چیزی نداریم که سرویسها حتماً باید از نوع RESTful باشند و همواره باید از REST استفاده کنید. آنچه در حالت عادی به افراد میگویم این است که باید برای کار ابزار درستش را انتخاب کنید. اگر ابزار درست کار شما این است که تبادل پیغام را از طریق یک گذرگاه داشته باشید چراکه مقدار زیادی نیازمندیهای فراعملکردی (cross-functional) حول آن دارید، همین کار را باید انجام دهید. من و مارتین اینطور میگوییم که در عوض اینکه بر روی یک گذرگاه سرویس معظم (enterprise service bus) تمرکز کنید که سرویسها خارج از گذرگاه آویخته شده باشند بهجای آن بر روی نقطههای انتهایی هوشمند و لولههای خِنگ (smart endpoints and dumb pipes) تمرکز کنید. این روش بیشتر به این صورت است که این ریزسرویسها، منطق کارها (business logic) را در خود دارند و بهوسیله پروتکل اصلی برنامه با هم صحبت یا تعامل میکنند اما در خود ریزسرویسها اینطور نیست.
فکر میکنم این نقطههای انتهایی هوشمند و لولههای خِنگ، به مدل Unix ارجاع داشته باشد.
ممکن است همین طور باشد. فکر میکنم به این دلیل این نام را انتخاب کردیم که این عنوان در مقابل مدل گذرگاه سرویس معظم اختیار میشود. آن طور که من به خاطر دارم، همواره در ThoughtWorks بیاعتمادی زیادی نسبت به یکپارچهسازی [به این نوع سبک گذرگاه سرویس معظم] رواج داشته است؛ این چیزها معمولاً وعده میدهند که همه مشکلات شما را حل میکنند. به شخصه، پیادهسازی خوبی از گروهی از سرویسها یا معماری ندیدهام که همه چیز حول یک گذرگاه سرویس مرکزی معظم آویخته شده باشند. من ندیدهام که اینجور چیزها موفق شده باشد. به عقیده من، اینجور مدل مرکزیسازی، خوب نیست. اینکه منطقهای خود را در یک جا و بالقوه در یک گذرگاه سرویس معظمی قرار دهید که همه چیزها از مسیریابی و انتقال دادهها و همه اینجور کارها را -که برای مراوده بین برنامهها نیاز است- انجام دهد. این روشها مشکل شما را حل نمیکند و روش درستی نیست. چند سال پیش در کنفرانس QCon، یک ارائه خوب از جیم وبر و مارتین فاولر با این عنوان بود که «آیا گذرگاه من در اینجا بزرگ به نظر میرسد؟» در آنجا جیم در مورد ایده داشتن یک گذرگاه پیچ در پیچ شگفتآور صحبت میکند. به عقیده او، این گذرگاههای معظم فقط کمک میکنند که نمودارهای زیبایی داشته باشیم چراکه وقتی به نمودارهای معماریهای معظمتان نگاه میکنید، متوجه یک سری خطوط متقاطع زشت میشوید و تمایل دارید که یک جعبه در وسط قرار دهید تا به ناگاه همه آن خطوط حذف شود. بهعنوان یک معمار، این را دوست دارید. اما در واقع همه آن خطوط هنوز وجود دارند، فقط داخل آن جعبه قرار گرفتهاند؛ جعبه پیچ در پیچ شده است.
اگر مسیریابی توسط یک گذرگاه سرویس معظم انجام نمیشود، پس چه کسی آن را انجام میدهد؟ آیا لازم است خودم مسیریابی کنم؟
بله، قطعاً لازمست نحوه ارتباط برقرار کردن بین برنامهها را بدانید. و اگر سرویسهای بیشتری بسازید مسائل مربوط به یکپارچه کردن (integration) بیشتری خواهید داشت. اگر در گذشته با دو سه تا سیستم خارجی، مسائل یکپارچهسازی کمتری احتمال داشتند، الان با یک سیستم گسترشیافته، مسائل بیشتری در این ارتباط رخ خواهد داد و باید در مورد نحوه ارتباط برقرار کردن بین برنامهها بیشتر بدانید. با این حال روشهای مختلفی برای این کار وجود دارد. چیزهایی از قبیل تأمین منابع پیشرفته (advanced sourcing) یا برنامههای رخدادگرا. مثلاً اگر از چیزهایی از قبیل HTTP و ارائه نتایج (در پکتهای HTTP) استفاده کنید، به شما اجازه میدهد که ناهمبسته (decouple) شوید و بهوضوح اینطور نیست که همواره ارتباطاتِ از نوع نقطه به نقطه ببینید.
آیا شبیه این نیست که پیچیدگی و نحوه با هم کارکردنها را از یک برنامه یکپارچه به لایه شبکه آوردهایم؟
جواب مختصرش این است که بله، و در واقع وقتی با افراد در این مورد صحبت میکنم یکی از نظراتی که دریافت میکنم همین است. دوباره از مارتین فاولر نقل میکنم که: «آیا با این کار داریم پیچیدگیهای تصادفی -پیچیدگی تصادفی، به همان معنایی که فرد بروکس تعریف کرده- را از داخل معماری و از کدهای پیونددهنده (glue code) که کامپوننتها و ماژولهای داخل برنامه هستند به زیرساختها منتقل میکنیم؟»
احتمالاً الان زمان خوبی برای [این انتقال] است زیرا الان روشهای خیلی بیشتری برای مدیریت این نوع از پیچیدگیها را بدست آوردهایم. اگر در برنامهنویسی زیرساختها نظر کنید، خودکارسازی زیرساختها و حرکت به سمت ابرها (cloud) و ... را داشتهایم که امروزه در همهجا حاضرند. بنابراین برای این کارها و اینکه بفهمیم چه تعداد برنامه داریم و چطور با همدیگر صحبت میکنند ابزارهای بهتری بهدست آوردهایم.
یکی از چیزهایی که از گوش کردن به یکی از صحبتهایتان متوجه شدم این است که ریزسرویسها باید بدون محفظه (container) و بهصورت مستقل قابل استقرار باشند. این به چه معناست؟ خصوصاً در ارتباط با خودکارسازی زیرساختها چه معنی میدهد؟
بله، در گذشته من این را گفتهام. اغلب این چیزها مفصلتر از آن است که بتوانم ظرف یک صحبت ۵ دقیقهای توضیح دهم. من اینطور فکر میکنم که چیزهایی که نیاز دارید مستقلاً مستقر شوند را باید بتوانید مستقلاً مستقر کنید. به دلایل مختلفی [ممکن است این را نیاز داشته باشید]. مثلاً آیا نیاز دارید که آنها را به دلایل میزان دسترسی یا توان عملیاتی یا تأخیر، بهصورت مستقل مقیاسدهی کنید؟ اگر چنین نیازمندی وجود دارد باید بتوانید آنها را مستقلاً مستقر کنید. اگرچه من سازمانهایی را میشناسم که گروههایی از این چیزها را با هم مستقر میکنند بنابراین شاید ۳ یا ۴ چیز باشند که واقعاً نیاز دارید که چرخه تغییرات آنها را مشترک کنید.
مانند سرویس کاربران یا اعتبارسنجی ...
بله، بالقوه اینها میتواند باشد. در مورد مثال بیمه که من صحبت کردم، یک سرویس حقوق دسترسی وجود داشت که بهعنوان بخشی از ریلیز آن محصول عرضه میشد. از جنبه تأثیرات اینها بر روی تیم پشتیبانی و مسائلی از قبیل تولید زیرساختها، باید بتوانیم نرمافزارها را بهتر توسعه دهیم. مثلاً ایدهای تحویل مستمر (continuous delivery) را داریم که جز هامبل و دیوید فارلی در کتابشان توضیح دادهاند و در مورد این است که چطور خط لولهها را کنار هم بسازیم و چگونه کل این مسیر انتقال نرمافزار از کار توسعه تا محیط عملیاتی را انجام دهیم. همچنین در خیلی موارد شاهد یک تغییر نگرش هستیم که بهجای میانگین زمان رسیدن به خرابی (mean time to failure) به میانگین زمان ترمیم یافتن (mean time to recovery) توجه میشود.
منظور شما از میانگین زمان ترمیم یافتن چیست؟
یک مثال خیلی جالب از آن، روشی است که Netflix، از قطعکننده مدار (circuit breaker) در محصولاتش استفاده میکند. مدتی است که الگوی قطعکننده مدار، جایگاه پیدا کرده است. این الگو ابتدا توسط مایکل نایگارد اختراع و ترویج شد. او این الگو را در کتاب خودش، کتاب «منتشرش کن!» بیان کرد. این کتاب خارقالعاده و یکی از بهترین کتابها در زمینه عملیات (operation) است. آنچه قطعکننده مدار در نرمافزار انجام میدهد این است که هنگام صحبت با سیستمهای خارجی سطحی از ایمنی را فراهم میآورد. من در تجربه شغلیام، قطعکننده جریان را در حالتی پیادهسازی کردهام که سیستمهای پاییندستی کُند شوند و یا از دسترس بروند. همه میدانیم که بین کُند شدن و اصلاً نبودن، تفاوت وجود دارد. این تکه از نرمافزار که قطعکننده مدار نام دارد میتواند در این حالتها عمل کرده و مدار را باز کند و سیستمهای پاییندست از آن پس، غیرقابل دسترس خواهند بود.
بنا به نوع پیادهسازی، میتوان انواع ایمنیهای مختلفی داشت مثلاً یک راهنما میتواند حجم حوض نخها (thread pool) باشد (اشاره به نوعی از پیادهسازی این الگو که در آن، هر فراخوانی راه دور در یک نخ (thread) که از حوض نخها گرفته میشود، انجام میشود و اگر تعداد نخهای مشغول از سقف مشخصی بالاتر رود، قطعکننده مدار عمل کرده و تا زمان رفع مشکل، اصطلاحاً مدار را باز کرده و خودش فراخوانیها را با پاسخ پیشفرض یا پاسخ خطا جواب میدهد - مترجم).
اگر درست متوجه شده باشم مثلاً یک پیادهسازی از قطعکننده جریان مانند gmail است که وقتی قطع باشم، بعد از ۵ ثانیه تلاش میکند، بعد به ۱۰ ثانیه افزایش مییابد، بعد به ۲۰ ثانیه افزایش مییابد و ... .
بله، دقیقاً. این یک مثال عالی است. بعد از آن وقتی در محیط عملیاتی کارهایی از قبیل مانیتور کردن و گزارشگیری بر روی این موارد را انجام میدهید، میتوانید ببینید که در هر لحظه از زمان دقیقاً چه اتفاقی برای سیستمتان افتاده است. البته اگر مثلاً ۱۰ سرویس داشته باشید، این مسئله خیلی مهمتر خواهد بود، چون اگر سرعت پاسخدهی کاهش یابد، پیدا کردن منشأ آن دشوار خواهد بود. کارهایی از قبیل بکارگیری الگوی قطعکننده مدار و سپس گزارش دادن بر روی آن به ما این اجازه را میدهد که به محض وقوع مشکلات توجهمان بر روی آنها جلب شود و در سریعترین زمان ممکن آنها را برطرف کنیم. این فکر آغاز شده که [کاهش دادن] میانگین زمان ترمیم یافتن از طریق راهاندازی مجدد سرویسها یا با توازن برقرار کردن بین ظرفها یا ایجاد ظرفهای جدید یا هر روش ممکن دیگری، اهمیت خیلی بیشتری دارد. فکر میکنم احتمالاً محبوبترین پیادهسازی که در مورد آن وجود دارد، Hystrix نام دارد که آن هم از Netflix است. Netflix بیش از ۶۰۰ سرویس مستقل عملیاتی دارد. وقتی این منظره را نگاه میکنید عملکردها پیچیده میشود و نیاز به چیزی دارید که در شناسایی مشکلات کمکتان کند و کار را برای افرادی که روی این سیستمها کار میکنند راحتتر کند.
شاید بهتر باشد کمی بحث را عوض کنیم. بر اساس چیزی که شما گفتید؛ در زمانی که چند صد سرویس داریم، [هرکدام از] این سرویسها چقدر بزرگ هستند؟ ریزسرویسها چقدر بزرگ هستند؟
من از محدوده چند صد خط کد تا چند هزار خط کد را دیدهام.
اما قطعاً یک میلیون نیست!
قطعاً یک میلیون نیست. راهنمایی که در این مورد وجود دارد و خود من هم به آن فکر میکنم این است که آیا [یک ریزسرویس] یک کار و تنها یک کار را انجام میدهد؟ سخت است که تصور کنیم که چیزی در حد چند میلیون خط کد یک کار و فقط یک کار انجام دهد! مگر آنکه کارش فقط این باشد که یک میلیون خط کد باشد که هرکدام خودشان را پرینت کنند، در آن صورت ممکن است :-) بنابراین در ارتباط با اندازه آنها، باید قابل فهم بوده و تنها یک دلیل برای تغییر آنها وجود داشته باشد و بیش از چند هزار خط کد نباشند. با در نظر داشتن این مسأله تعداد آنها اهمیت مییابد. احتمالاً اهمیت بیشتری دارد که به این فکر کنید که چه تعداد از آنها را بهصورت عملیاتی میتوانید پشتیبانی کنید تا اینکه به این فکر کنید که آنها واقعاً چقدر کوچک هستند. زیرا اگر پشتیبانی عملیاتی شما کاملاً نابالغ باشد بهتر است که تعداد کمتری از آنها که اندازههای بزرگتری دارند، داشته باشید؛ منظور بالغ بودن از این لحاظ است که یک دکمه داشته باشید که کاملاً خودکار سیستم را مستقر عملیاتی کند، اینکه تیمهای عملیاتی داشته باشید، دانش و مهارتهای لازم برای کار با مشتری و چیزهایی از این قبیل را داشته باشید. از این نظر، بهتر است که بر روی تعداد سرویسها تمرکز کنید تا بر روی اندازه هرکدام از آنها.
شما خیلی در مورد مسئولیتهای منفرد (Single Responsibility) صحبت کردهاید و به طراحی مبتنی بر حوزه (Domain Driven Design) اشاره کردهاید. آیا ریزسرویسها، نوعی طراحی مبتنی بر حوزه در لایه سرویسها هستند؟
اگر به صحبتهایی که از من در اینترنت هست گوش کنید، میبینید که من همواره کلامم را به این ختم میکنم که ما بر روی دوش بزرگان ایستادهایم. (مَثَلی است که خصوصاً پس از آنکه اسحاق نیوتن در یکی از نامههایش به کار برد مرسوم شد و به این معناست که ایدههای جدید مبتنی بر دانش و ایدههای قبلی که گذشتگان مطرح کردهاند، شکل میگیرد- مترجم). به نظر من ریزسرویسها، گردآوری از چندین مجموعه رویههای برتر از چندین اجتماع مختلف است. از این میان، چیزهای خوبی از اجتماع طراحی مبتنی بر حوزه هم گردآوری شده است مانند طراحی استراتژیک (strategic design)، حیطههای مرزبندی شده (bounded context)، زیرحوزهها (sub-domain) و اینکه چطور یک حوزه بزرگ را به زیرحوزهها تفکیک کنید و ... . همین طور مزیتهایی از رویههای برتر مرتبط با کارهای عملیاتی و خودکارسازی آنها آورده شده است؛ این موارد از اجتماع DevOps و برنامهنویسی زیرساختها، آورده شده است. همین طور مواردی از اجتماع ابری (cloud) و اجتماعهای یکپارچهسازی، گردآوری شده است؛ از اجتماع افرادی که با پیغامها و سرویسهای RESTful کار میکنند، آنهایی که خیلی سخت کار میکنند تا افراد آگاه شوند که میتوانند مسائل یکپارچهسازی را با همین ابزارهای رایگان موجود در وب، انجام دهند و نیازی به هزینه و سرمایهگذاری بر روی آن ندارند. همه این ایدههای مختلف، در یکجا جمع شده است.
از اجتماع طراحی مبتنی بر مدل (domain driven design) هم ایدههایی گرفته شده است. من گفتم که ریزسرویسها بهصورت مجزا تولید میشوند اما اگر بخواهم کمی از گفتهام عقب نشینی کنم باید بگویم بهعقیده من روشی که بتوانید یک معماری مؤثر داشته باشید این است که آن معماری مبتنی بر منطق منطق کسبوکار (business logic) باشد و این باید یک کار بالا به پایین باشد. [به این ترتیب که] شما متوجه میشوید که کسبوکار چیست، که دورنمای کسبوکار چیست، که فرآیندهای مرتبط با کسبوکار چیست و [آنگاه] مقصود محصول نرمافزاری را نشأتگرفته از آنها را مییابید؛ و بهعقیده من فهم اینکه زمینه کسبوکار و مدلهای کسبوکار چیست همان قلب طراحی مبتنی بر مدل است.
یکی از همکارانم در کلراید، نظر خیلی فنی خوبی دارد. ایدهاش این است که معماری کسبوکار باید همریخت (isomorphism) باشد. به این صورت که اگر به طراحیهای موجود در معماریتان نگاه کنید و از طرف دیگر به مفاهیم کسبوکارتان نگاه کنید باید خیلی شبیه به هم باشند. باید بتوانید به کسبوکارتان نگاه کنید و معماریتان را ببینید و به معماریتان نگاه کنید و مفاهیم کسبوکارتان را ببینید. چه یک متخصص فنآوری باشید و چه فردی باشید که در کسبوکاری مشغول است، در هر دو حالت، باید متوجه شوید که چه کاری دارد انجام میشود.
یک نسخه تشدیدشده از این ایده وجود دارد که چالشیتر و بحثبرانگیزتر است و آن در این ارتباط است که اصلاً نباید ایدهای داشته باشیم و همه چیز باید در کسبوکار گفته شود که من نمیخواهم بهسراغش بروم که ممکن است بحثش یک ساعت به درازا بکشد.
به خاطر دارم که شما در مورد چیزی مانند همسایه یک ریزسرویس صحبت کردید. کنجکاوم بدانم آیا این راهی برای گروه کردن آنهاست؟
همسایه؟!
شما چیزی شبیه به این گفتید که اگر میخواهید ریزسرویسها را با هم گروه کنید باید آنها را حول کسبوکارها یا حول نیازمندیهای غیرعملکردی با هم گروه کنید و این را همسایه ریزسرویس نامیدید.
آهان، شاید یک لغت جدید ابداع کرده باشم! بله، همین طور فکر میکنم. ما بهصورت داخلی خیلی در این مورد صحبت کردهایم. این هم اصطلاحی است که برپایه ایدههای همکارانم درست کردهام یعنی همان طور که گفتم ما بر روی دوش بزرگان ایستادهایم. این ایده به این صورت است که وقتی سیستمتان را طراحی میکنید بهجای آنکه شبیه طراحی یک ساختمان بر اساس یک طرح اولیه باشد بیشتر مانند طراحی یک شهر است. بیشتر در ارتباط با ناحیههای یک شهر است. در یک شهر، نواحی مختلف صنعتی، مسکونی، تجاری و ... وجود دارند. [در سیستم نرمافزاری] اتصال بین این نواحی، بر اساس پروتکلهای حوزه برنامه انجام میشود. اینها [حُکمِ] امکانات زیرساخت را دارند یعنی همه [نواحی] از امکانات زیرساخت خطوط آب استفاده میکنند، همگی از امکانات برق و چراغ برق استفاده میکنند و ... . بنابراین رمز کار این است که چطور ناحیهبندی کنیم و نقشه نواحی چطور تهیه شود. من برای این کار روشهای سبک وزن تر را میپسندم. به این ترتیب که ابتدا فرآیندهای کسبوکار را بشناسید و سپس از تکنیکهای سبک استفاده کنید مثلاً اینکه از تخته وایتبرد یا کارتهای ایندکس و ترکیب این ابزارها استفاده کنید. اما بله، فهم اینکه این گروهها کدام هستند بسیار اهمیت دارد و باید قبل از هر کار دیگری انجام شود.
در اینجا، همپوشانی گستردهای با فضای جامعه معماری نرمافزار وجود دارد. آنها برای مدت طولانی، کارهای گسترده خوبی در این زمینه کردهاند. بنابراین میتوانید همه آن تعلیمات را بکار گیرید مثلاً با استفاده از ایدههای طراحی مبتنی بر حوزه، امور را به زیردامنهها و زمینههای مرزبندی شده بخش بندی کنید و بعداً آنها را به برنامههای مجزا تفکیک کنید.
به خاطر میآورم که یکی از همکارانم میگفت که ما به این دلیل در مورد ریزسرویسها صحبت میکنیم زیرا اصطلاح معماری سرویسگرا (service oriented architecture) آسیب دیده است. آیا میتوانید تفاوتش با معماری سرویسگرا را بیان کنید و توضیح دهید که چه چیزی به آن آسیب رسانده که ما امروزه از ریزسرویسها صحبت میکنیم؟
ابتدا میخواهم بین کلیت جامعه معماری سرویسگرا با پیاده سازیهایی که در سازمانهای مختلف دیدهام، تفاوت قائل شوم زیرا فکر میکنم تفاوت آشکاری وجود دارد. اگر به عنوان مثال به مانیفست معماری سرویسگرا (SOA) نگاه کنید، در واقع، کاملاً روشن و ملموس بیان شده است. اما بر طبق تجربه من و تجربههای همکارانم و تجربهای که در کل صنعت داریم اگر ایده SOA را با پیادهسازیهایی که از آن در واقعیت بیرونی شده است مقایسه کنید، تفاوتهای آشکاری وجود دارد. واقعیت بیرونی این است که برنامههای ۱۰ میلیون دلاری داریم که مثلاً دو سال و نیم است اجرا میشود و ارزشی تولید نکرده است. واقعیت این است که بر روی بخشهایی از کد که وظیفه حل مسائل یکپارچهسازی (integration) را دارند میلیونها دلار خرج میشود و این هزینهکردها هیچگاه به بار نمینشیند. فکر میکنم اهمیت دارد که بین آن چیزی که مورد حمایت جامعه [معماری سرویسگرا] است با آنچه در واقعیت وجود دارد تفاوت قائل شویم.
فکر میکنم ایده ریزسرویسها، واقعاً در مورد آن واقعیتهاست؛ به نظر من جای شگفتی ندارد که [ریزسرویس] یک رهیافت پایین به بالا برای ساختن سیستمها است. خیلی از افرادی که در این کار شرکت کردهاند، به شدت در داخل تیم و با توسعهدهندهها مشغول بودهاند و همچنان هستند. به این ترتیب، هر روزه مشکلات واقعی مثلاً هزینه زمان مورد نیاز برای انجام تغییرات و مشکلات مقیاسپذیری را مشاهده میکردهاند.
در چنین شرایطی، ما بهدنبال رهیافتی میگشتهایم که برخی از این مشکلات را حل کند. اینطور فکر نکنید که این تصادفی بوده است که خیلی از افراد مستقل از [خواستگاههای مختلف مثلاً] معماری تکاملی، طراحی مبتنی بر مدل، XP و جنبش agile و ... به شدت درگیر آن شدهاند. زیرا آنچه در ریزسرویسها واقعاً در مورد آن صحبت میکنیم مدلی است که بیشتر یک رهیافت افزایشی (incremental) و نوبهای (iterative) برای ساختن معماریهای سرویسگرا است.
حتی اگر اینها را هم در نظر نگیریم، اگر به صحبتهایی که چند سال قبل میشد برگردید میبینید که در مورد معماری سرویسگرای واقعی گفته میشد که بهعنوان رهیافتی برای ساختن سرویسها و تولید ارزش بهصورت افزایشی است در مقابل اینکه بخواهید دورنمای یکپارچه شده از سرویسها را از پیش طراحی کنید و بخواهید ۸ ماه به طراحی مستندات بگذرانید و پیادهسازیها ۵ سال طول بکشد و واقعاً هیچ نتیجهای ندهد.
به عقیده من، سبک ریزسرویسها برای ساختن برنامهها، مرتبط با همان معماری سرویسگرا است زیرا روش سرویسگرا برای ساختن سیستمها، یک روش کاملاً معقولی است. به عقیده من تفاوت اصلی آنها در جزییات نحوه پیادهسازی است؛ مثلاً اینکه مدل حاکمیت (governance) غیرمتمرکز دارید.
یا مثلاً اینکه [در ریزسرویسها] یک گذرگاه سرویس معظم نداریم!
بله، اینها مربوط به انتخاب فناوریها است که راه حلهای سبکتر نسبت به سنگینتر، ارجحیت داده میشود. اما بهغیر از آن در مورد چیزهایی از قبیل مدیریت نیز اینگونه است که شما هم از جنبه فنی و هم از جنبه انسانی، سازوکارهای سبکتر را میپسندید؛ بهجای اینکه تمایل داشته باشید تعداد زیادی استانداردهای سخت وضع کنید، استانداردها را بهصورت داخلی در طی زمان و بر اساس کاربردها و تجربههای واقعی طراحی میکنید و یا بهجای اینکه یک رهیافت بالا به پایین داشته باشید، بهجای آن در داخل همان سطوح [خودشان] به آنها فکر میکنید و در سطوح پایین تولید انجام میشود.
یکی از چیزهایی دیگری که من شنیدهام این است که برخلاف SOA در معماری ریزسرویس، سرویسها، واسط کاربر گرافیکی (GUI) دارند. آیا این درست است؟
خیر. فکر نمیکنم اینطور باشد. فکر نمیکنم چیزی وجود داشته باشد که التزام کند که باید GUI داشته باشید. البته فکر میکنم در واقع کاملاً مفید است که حتی فقط برای بررسی نحوه کارکرد برنامه هم که شده، GUI داشته باشیم.
برای مانیتور کردن ...
بله، اما در مورد GUI هایی که مشتریها با آنها روبرو میشود، خیر چنین چیزی نداریم که باید GUI داشته باشید.
بیایید کمی در مورد تیم صحبت کنیم. فکر میکنم شما قانون کانوِی (conway's law) را بدانید. ممکن است کمی در مورد آن صحبت کنید و بگویید در معماری ریزسرویسها چه معنی میدهد؟
این مثال دیگری است که یک تجربه و یا چیزی که برای مدت طولانی، مشخص شده است، در کاربرد دیگری، ظاهر میشود و پلی بین این اجزاء برقرار میشود. فکر میکنم در سال ۱۹۶۸ بوده است که ملوین کانوِی، -او اهل ساحل غربی آمریکا است- یک مقاله در مورد تجربیاتش در مورد فکر میکنم تئوری ارتباطات (communication theory) مینویسد. آنچه او در مقالهاش توضیح میدهد این است که ساختار راههای ارتباطی در نرمافزاری که یک سازمان میسازد کاملاً یا به شدت با ساختار راههای ارتباطاتی که آن سازمان در داخل خودش دارد منطبق است به این ترتیب که اگر شما مثلاً یک تیم پایگاه داده داشته باشید و یک تیم میانافزارها (middleware) داشته باشید و یک تیم واسط کاربری (UI) داشته باشید، در نهایت سیستمی هم تولید خواهید کرد که یک پایگاه داده، نوعی میانافزار و یک UI خواهد داشت. این را میتوانم با گفته دن نورث خیلی خوب توضیح دهم. او به من گفت که اگر از ۹ نفر بخواهید که کامپایلر بنویسند، در نهایت به یک کامپایلر میرسید که در ۹ مرحله کامپایل میکند! :-)
جالب است که آن مقاله، ابتدا بهوسیله مجله مرور کسبوکار هاروارد (Harvard business review) رد میشود. سپس بعد از گذشت مدتی سازمانهای دیگری بهعنوان مثال مایکروسافت بهصورت داخلی این تحقیقات را تکرار کردند. در انتها HBR خودش هم تحقیقات را تکرار کرد و فهمیدند که به نظر میرسد این مورد در نرمافزار رخ میدهد. بنابراین قانون کانوِی اعتبار یافت.
جالب این است که ما در مباحثههای خودمان متوجه شدیم که این پیامد فقط بهخاطر نحوه ساختاردهی تیم یا طراحی سیستمتان نیست بلکه پیامدی از نحوه اجبار کردن مرزهای بین این تیمهاست. بنابراین در واقع میتوانید با ساختار تیمتان مرزهایی در نرمافزارتان تحمیل کنید که باعث مرزهایی در معماری سیستمتان میشود.
برای روشن شدن مطلبی که میگویید میپرسم: آیا یک ریزسرویس باید بهصورت تمام وقت دقیقاً توسط یک تیم توسعه داده شود؟
خوشحالم که شفافسازی میکنید. خیر، من دقیقاً این را نمیگویم. من توصیه نمیکنم که باید یک تناظر یک به یک بین ریزسرویسها و تیمها باشد. چنانچه ۳ ریزسرویس داشته باشیم که در یک حیطه مرزبندی شده (bounded context) قرار گیرند، ممکن است این ۳ ریزسرویس به یک تیم تخصیص یابند. من علاقه دارم که محدوده [فعالیتهای] یک تیم مجموعهای از قابلیتهای پایدار کسبوکار باشد که در یک زمینه قرار میگیرند. این [ساختار] در یک سازمان میتواند کاملاً پایدار باشد زیرا در این صورت کسبوکار و معماری، مفاهیمی همریخت خواهند بود. ما باید ببینیم که کسبوکار به چه شکلی کار میکند و به همان شکل به نرمافزار ساختار دهیم تا مانند هم بشوند. اگر بر این اساس تیمها را ساختار دهید، ممکن است تیمهای مربوط به نیازمندیهای فراعملکردی (cross-functional) وجود داشته باشد که مانند مدل کسبوکار باشد.
این یک حوزه خیلی جالب است که فکر میکنم در حال حاضر افراد خیلی برای کاوش آن علاقهمندند. یکی از مثالهای خوب آن در مورد یکی از مشتریهایم است که از من درخواست مشاوره معماری کرده بودند. بخش بزرگی از سیستم آنها در هند استقرار داشت و بخشی از نرمافزارشان در لندن بود. اگر به نحوه تعاملات بین نرمافزار لندن و نرمافزار هندی نگاه میکردید، میدیدید که نسبت به دیگر ارتباطهای بین سیستمهایشان، احتمالاً بیشترین ناهمبستگی (decouple) را داشتند و بیشتر بر اساس رد و بدل کردن پیغام بود. این یک مثال خوب بود از اینکه وقتی تیمها در قارههای مجزایی قرار دارند، این سبک [سیستمهای تا حد ممکن ناهمبسته] تنها روشی است که میتوانیم به طراحی سیستم فکر کنیم.
بنابراین چنانچه میخواهید ماژولها یا سرویسهای خود را ناهمبسته کنید، تیمهایتان را ناهمبسته کنید.
بله. مثلاً در قارههای مجزا :-) یا مانند یک مشتری دیگر که در آمریکا داشتم که یک تیم ۶ نفره بودند که بر روی یک نرمافزار واحد کار میکردند اما هر کدام از آنها در یک جای اداره مینشستند و مکعبی کار میکردند. (هر کدام در یک وجه مکعب قرار داشتند) و برای ۶ ماه تمام کار میکردند و بعد از آن دور هم جمع میشدند و من فکر میکنم که در این مورد کار درستی نبود. به آنها گفتم مطمئن نیستم با ۶ ماه از هم دور کار کردن و بعد دور هم جمع شدن هیچگاه محصولی حاصل شود.
بله، باید بتوانید ببینید که سیستمتان بهصورت یک مجموعه واحد، کار میکند. این من را به سئوال بعدی رهنمون میکند که چطور ریزسرویسها را تست میکنید. ما میدانیم که چطور تست واحد (unit test) بکنیم، میدانیم که چطور در داخل سطح یک سرویس منفرد، تست یکپارچگی (integration) بکنیم اما وقتی یک برنامه متشکل از تعدادی ریزسرویس دارید باید بتوانید مطمئن شوید که آنها در کنار همدیگر نیز کار میکنند. چطور آن را تست میکنید؟ آیا برپا کردن تست خیلی دشوار نخواهد بود؟
بله، احتمالاً این یکی از جنبههای دشوار کار است. وقتی سیستمی میسازیم همواره مصالحههایی (trade-off) میکنیم. وقتی روی اموری از قبیل مقیاسپذیری و قابلیت نگهداری در طی زمان تمرکز میکنیم، یکی از مصالحههایی هم که باید داشته باشیم این است که تست کردن دشوارتر میشود.
اغلب سیستمهایی که من دیدهام در نهایت به این سرانجام رسیدهاند که بر روی ریزسرویسها یک سیستم مبتنی بر رخداد (event driven) برقرار شده است که خودش دوباره لایه پیچیدگی جدیدی میافزاید زیرا بهغیر از آنکه باید نگران تست کردن چیزهای زیادی بهصورت مجزا باشید باید نگران ناهمگام بودن (asynchronicity) هم باشید. با این وجود، مزیتهای زیادی هم خواهید داشت زیرا میتوانید چیزهای کوچک را بهصورت مجزا تست کنید که براساس مجموعهای از مشخصات آیا آنچه قرار است انجام دهند، را انجام میدهند یا خیر. مشکل جایی رخ میدهد که میخواهید چندین سیستم گره خورده به هم را تست کنید. مشکل جاییست که میخواهید فرآیندهای کسبوکار را از خلال چندین ریزسرویس تست کنید.
یک مثال آن، میتواند در یک سیستم بانکی باشد که مشتری داشته باشم که یک حساب بانکی دارد و یک فرآیند کسبوکار دارم که میگوید وقتی یک مشتری ساخته میشود باید یک حساب مشتری برایش ساخته شود و میخواهم بتوانم برای آن تعدادی ایمیل یا کارت بانکی یا چیزهای دیگری مرتبط با کسبوکار را ارسال کنم. با رهیافت ریزسرویسها، در اینجا چندین قابلیت کسبوکار وجود دارد. شما قابلیت مشتری (کلیت مشتری) را دارید. ممکن است قابلیت تراکنش و حساب (account) را داشته باشید و چیزهای دیگر. شما فرآیندهای کسبوکاری دارید که از خلال این مرزها عبور میکنند. چطور میخواهید تستهایتان را حول این چیزها قرار دهید؟ راههای مختلفی برای این کار وجود دارد. یک مثالش این است که نوعی تستِ در سطح محصول داشته باشیم. اینکه یک جریان گذرکننده از این چیزها داشته باشم که در سطح محصول باشد که بتوانم بهصورت خودکار اجرایش کنم و محیطی داشته باشم که بتوانم این چیزها را بر روی آن مستقر کنم و آنها را تست کنم.
همچنین راهها و سازمانهای پیشرفتهتری هم وجود دارند. سازمانهایی هستند که واقعاً روشهای جدیدی برای تولید نرمافزار اختراع میکنند. من فکر میکنم Netflix و شرکتهای مانند آن، از این دسته هستند. آنها در واقع دارند به سمت مدلهای جالب جدیدی حرکت میکنند. در واقع چیزهایی از قبیل تست کردن کارایی (performance) قبل از راه انداختن سیستم، مربوط به گذشته هستند. امروزه تست را بر روی محصول عملیاتی انجام میدهید. در واقع تست بهوسیله تعداد اندکی از کاربرانمان بر روی محصول عملیاتی انجام میشود. البته واضح است که این کار بعد از تست اصلی عملکردی توسط تیم توسعهدهنده انجام میشود.
البته به این امید که با یک سازوکار مانیتور کردن خوب هم پشتیبانی میشود.
بله، با مانیتور کردنهای اضافی و لاگگذاریهای متراکم و چیزهای دیگری که باید قرار دهید، پشتیبانی میشود.
قبلاً گفتم که جنبشی در ارتباط با [تمرکز روی] میانگین زمان ترمیم یافتن وجود دارد و یا ایده مانیتور کردن معنایی (semantic monitoring) محصول را داریم و اینکه طی کردن مراحل از توسعه به استقرار و عملیاتی شدن محصول بهطور خودکار انجام شود. مثلاً [در زمینه مانیتور کردن معنایی] مانیتور کردن مسیری که کاربر هنگام بازدید از یک وبسایت طی میکند و چیزهایی از این قبیل مطرح است که این گونه مباحث در سازمانهای بزرگ در حال پیشرفت است.
حالا شما میتوانید از من در مورد سازوکار نسخه دادن (versioning) سئوال کنید. شاید باید در مورد آن هم صحبت کنیم.
بسیار خوب. پس در مورد نسخهها صحبت کنید :-)
من فکر میکنم کاملاً مرتبط با تست کردن است. زیرا چیزهای زیادی دریافت میکنید که با همدیگر صحبت میکنند. واضح است که مطمئن شدن از اینکه [این اجزاء] میتوانند همچنان با همدیگر صحبت کنند از مثلاً [تست کردن] یک فراخوانی متد سختتر است. همینطور بازسازی (refactor) این چیزها نیز سختتر است و اموری از قبیل سازوکار نسخهدهی، نسبت به آنچه در گذشته بوده، مشکل بزرگتری خواهد بود. چطور میخواهید قراردادی که بر اساس آن، با کلاینتهایتان صحبت میکنید را نسخهدهی کنید؟ برای این منظور، افراد روشهای مختلفی استفاده میکنند. در برخی مواقع افراد از ایده قراردادهای مبتنی بر مصرفکننده (consumer driven contract) استفاده میکنند.
قرارداد مبتنی بر مصرفکننده چیست؟
این ایده، نقطه مقابل یک ایده قدیمی است. اگر یک سیستم پاییندست داشته باشم که قرار است با آن یکپارچه شوم میخواهم مطمئن شوم که اگر تستهایم را در قبال آن سرویس از راه دور اجرا کنم، همچنان مبتنی بر قراردادهایی است که داشتهایم وگرنه نخواهم توانست با آن تعامل کنم. بنابراین دوست دارم که بهطور خودکار هر تغییری که در قرارداد ایجاد شده را بفهمم تا بتوانم در سمت خودم نیز تغییرات را اعمال کنم.
ایده قرارداد برآمده از مصرفکننده این است که به نوعی جهت فلش را عوض کنیم. یعنی بهجای اینکه من بهعنوان کلاینت یک سرویس زیردست، تستها را اجرا کنم، همچنان تست را من مینویسم اما آن را به سرویس میدهم تا خودش مالکش باشد و بعنوان بخشی از ساختن (build) خودش آن را اجرا کند.
یعنی به آنها این امکان را میدهید که تستهای شما را در قبال سیستم خودشان و در خط لولهای که خودشان دارند اجرا کنند.
دقیقاً همین است. به آنها یک سری بیان مشخصاتی که قابل اجرا باشد را میدهید و (به آنها میگویید) که بهعنوان کلاینت انتظار دارید چطور با شما رفتار شود. از طرف دیگر افرادی که کدها را توسعه و نگهداری میکنند، این مشخصات را گرفته و همان طور که شما گفتید بهعنوان بخشی از فرآیند ساختن (build) خودشان اجرا میکنند. بنابراین قرارداد این میشود که این قراردادها در سیستمهای بالادست شکسته نشود. این ایده، خیلی عالی است و افرادی این ایدهها را گرفته و کمی جلوتر هم رفتهاند؛ فکر میکنم یک پیادهسازی Ruby به نام Pact از آن وجود دارد و یک پیادهسازی دیگری هم با نام Pacto وجود دارد. به خاطر نمیآورم این نام مخفف چیست اما یکی از این پیادهسازیها در استرالیا و دیگری در برزیل انجام شده است.
روش دیگری که درباره مدیریت نسخهها وجود دارد و بر اساس ایده مضحکی شکل گرفته این است که با شکل دادن تردد (traffic shaping) این کار را انجام دهیم. این کار لزوماً در مورد API ها عمومی انجام نمیشود بلکه در داخل ساختاری که برای ریزسرویسها دارید هم کاربرد دارد که من مثالش را در همان Netflix یا شرکتهای دیگری که با آنها کار کردهام، دیدهام.
وقتی با ریزسرویسها تعامل دارید چنانچه بخواهید تغییراتی دهید که درونتیمی باشد واقعاً نیازی نیست که چندان نگران نسخهها باشید زیرا این تغییراتی که تنها بر روی قراردادهای درونتیمی تأثیر دارند خیلی سریع انجام میشود. کافیست به همکارم بگویم که من میخواهم این تغییر را بدهم. خوبه؟ و او میگوید بله، و به همین سرعت تغییر انجام میشود اما وقتی به قراردادهای با تیمهای دیگر میکشد، باید سطحی از پایداری را فراهم کنید. در اینجا شرکتهایی مانند Netflix همانطور که گفتم بهجای استفاده از سازوکارهای خاص نسخهدهی از نوعی شکلدادن تردد استفاده میکنند و یا اینکه این روش را در ترکیب با یک سازوکار خاص نسخهدهی استفاده میکنند. در این مدل، شما نسخهای از سرویس را دارید که بهصورت عملیاتی در حال اجرا است و بعد شما نسخه بعدی را هم عملیاتی میکنید، یعنی دو نسخه متفاوت از یک سرویس را خواهید داشت که هر دو عملیاتی شدهاند اما در واقع نسخه جدید، زنده نیست و هیچ ترددی به آن نمیشود. بعد از آنکه تستها را انجام دادید و تستهای سطح محصول را انجام دادید و مطمئن شدید که صاحب محصول (product owner) از آنچه رخ داده، خرسند است و ...
و شاید از برخی مشتریان قدیمی هم بخواهید که از آن استفاده کنند...
دقیقاً. بعد از انجام این کارها شروع میکنید که تردد به آن را شکل دهید و افراد را به سمت آن سوق دهید و یا اینطور خواهد بود که تیمهای بالادستی خودشان انتخاب میکنند که چه زمانی جابجا کنند. و البته با توجه به اینکه ما به سمت عصری پیش میرویم که در آن پردازش ابری در همه جا حاضر است، این ایده که بتوانید نسخههای مختلف چیزی را همزمان اجرا کنید، سادهتر فهمیده و اجرا میشود چون دیگر با محدودیتهای مراکز دادهها روبرو نمیشویم، فقط ممکن است بوسیله محدودیتهای مراکز داده Amazon و ... به مشکل بخوریم :-)
بسیار خوب، در پایان آیا سئوالی هست که بخواهید از شما بپرسم یا چیزی هست که بخواهید در حالت کلی در مورد ریزسرویسها بگویید؟
بله، نظر من این است که ما بر دوش بزرگان ایستادهایم. [ریزسرویسها] پدیده کاملاً جدیدی نیست. قطعاً اینطور نیست و فکر نمیکنم هیچ کس دیگری چنین ادعایی داشته باشد که همه این ایدهها جدید است بلکه بر روی تعدادی زیادی از ایدههای موجود ساخته شده است و آنها را طوری کنار هم قرار داده است که قدرت آنها چند برابر شود. قرار دادن این چیزها کنار یکدیگر بهعنوان یک راه جامع که شامل ایدههای توسعه تَرکهای (lean) باشد، شامل ایدههای دنیای سرویسگرا باشد، شامل ایدههایی از دنیای DevOps باشد، ... همه این چیزها کنار همدیگر، نسخه قدرتمندی میسازد که میتوانید سیستمهای امروزی را برپایه آن تولید کنید.
افراد زیادی [در این کار] مشارکت داشتهاند، پیش از این در مورد دن نورث صحبت کردم که در ارتباط با ایده معماریهای مؤلفههای جایگزینپذیر خیلی مشارکت داشته است و یا فرد جورج و یا ادریان کاکرافت که در Netflix صحبتهای گستردهای در این زمینه کرده است یا مؤسسههای بزرگی در استرالیا هستند، یکی از همکارانم به اسم اوان بوچر آنجا همکاری میکند. در مقایسه، در انگلستان، میتوانید کارهای خیلی جذابی [در ارتباط با این موضوع] داشته باشید و از کاری به کار دیگر بروید. فکر میکنم اکنون که پردازش ابری در همهجا حاضر است، دوران شگفتانگیزی برای توسعه نرمافزار باشد و فکر میکنم این چیزی است که معادلات دنیای امروز را عوض میکند و ما خیلی مختصر در مورد آن صحبت کردیم.
خوب است. شنوندگان ما کجا میتوانند چیزهای بیشتری در این زمینه پیدا کنند؟ شنیدهام که در نوشتن یک کتاب مشارکت دارید.
بله. اولینش همان مقاله ریزسرویسها است که با مارتین فاولر نوشتهام که در سایت مارتین فاولر قرار دارد. در بلاگم -که البته خیلی بهندرت بر روی آن پست میگذارم- مباحث و آشناییهایی با موضوع وجود دارد. از این بابت عذر میخواهم که خیلی از مطالب بلاگم در مورد اخترفیزیک است :-) همانطوری که گفتید کتابی هم هست که در حال حاضر در نوشتن آن مشارکت دارم. در همین حال، یکی از همکارانم یعنی سم نیومن که [در زمینه ریزسرویسها] مشارکت فراوانی داشته است، کتابی با عنوان ساختن ریزسرویسها نوشته است. بهغیر از اینها فکر میکنم بلاگ Netflix، یک منبع خارقالعاده است. احتمالاً چیزهای بیشتری هم هست :-)
کجا میتوان بیشتر از شما شنید. آیا در توییتر هستید؟
بله، در توییتر با عنوان boicy@ هستم. همچنین میتوانید به من ایمیل بزنید. آدرسش در زیر مقاله سایت فاولر هست. ما خیلی دوست داریم که با افراد تعامل داشته باشیم. اگر علاقهمند شدید لطفاً در ارتباط باشید. من تمام تلاشم را میکنم که به همه نامههایی که دریافت میکنم پاسخ دهم.
مطلبی دیگر از این انتشارات
کانبَن
مطلبی دیگر از این انتشارات
تاریخچه JUnit و آینده تست
مطلبی دیگر از این انتشارات
توسعه مبتنی بر تست (TDD)