انتقل تطوير المنتج: تاريخ مشروع واحد



مرحبا بالجميع! اسمي مكسيم رايندين ، أنا قائد فريق فريقين في Gett - Billing and Infrastructure. أريد أن أتحدث عن تطوير ويب المنتج ، والذي نستخدمه في Gett بشكل أساسي. سوف أخبرك كيف تحولنا إلى هذه اللغة في الفترة 2015-2017 ، ولماذا اخترناها على الإطلاق ، وما هي المشاكل التي واجهناها أثناء الانتقال وما هي الحلول التي وجدناها. وسأخبرك عن الوضع الحالي في المقالة التالية.

بالنسبة لأولئك الذين لا يعرفون: Gett هي خدمة تاكسي دولية تأسست في إسرائيل في عام 2011. يتم تمثيل Gett الآن في 4 دول: إسرائيل وبريطانيا العظمى وروسيا والولايات المتحدة الأمريكية. المنتجات الرئيسية لشركتنا هي تطبيقات الأجهزة المحمولة للعملاء والسائقين ، وبوابة إلكترونية للعملاء من الشركات ، حيث يمكنك طلب سيارة ، ومجموعة من لوحات الإدارة الداخلية التي يقوم موظفونا من خلالها بوضع خطط للتعرفة ، وتوصيل برامج تشغيل جديدة ، ومراقبة حالات الاحتيال وغير ذلك الكثير. في نهاية عام 2016 ، تم افتتاح مكتب عالمي للبحث والتطوير في موسكو ، والذي يعمل لصالح الشركة بأكملها.

كيف وصلنا إلى الذهاب


في عام 2011 ، كان المنتج الرئيسي للشركة هو تطبيق متجانس على Ruby on Rails ، لأنه في ذلك الوقت كان هذا الإطار شائعًا للغاية. كانت هناك أمثلة ناجحة للشركات التي تم تطويرها وإطلاقها بسرعة على Ruby on Rails ، لذلك ارتبطت بالنجاح في العمل. كانت الشركة في طور التطوير ، وجاءت برامج تشغيل ومستخدمين جدد ، ونمت الأحمال. وبدأت المشاكل الأولى في الظهور.

لكي يعرض تطبيق العميل موقع السيارة ، وبحيث تبدو حركتها منحنىًا ناعمًا ، يجب على السائقين إرسال إحداثياتهم في كثير من الأحيان. لذلك ، كانت نقطة النهاية المسؤولة عن تلقي الإحداثيات من السائقين هي دائمًا الأكثر تحميلًا. وقام إطار خادم الويب في روبي أون ريلز بعمل ضعيف في هذا. كان من الممكن التوسع على نطاق واسع فقط ، بإضافة خوادم تطبيقات جديدة ، وهي مكلفة وغير فعالة. نتيجة لذلك ، أخرجنا المجموعة الوظيفية للإحداثيات في خدمة منفصلة ، والتي كانت مكتوبة في الأصل في JS. لفترة من الوقت ، وهذا حل المشكلة. ومع ذلك ، مع زيادة الحمل ، عندما وصلنا إلى 80 ألف دورة في الدقيقة ، توقفت الخدمة على Node.js عن إنقاذنا.

ثم أعلنا hackathon. أتيحت لجميع الموظفين في الشركة فرصة في يوم واحد لكتابة نموذج أولي ، والذي كان لجمع إحداثيات السائقين. فيما يلي معايير إصدارين من تلك الخدمة: التشغيل على prod وإعادة كتابته على Go.


في جميع النواحي تقريبًا ، أظهرت الخدمة في Go أفضل النتائج. تستخدم الخدمة على Node.js مجموعة ، إنها تقنية لاستخدام جميع مراكز الجهاز. وهذا هو ، كانت التجربة زائد أو ناقص صادق. على الرغم من أن Node.js له عيب في وقت التشغيل المفرد ، إلا أنه ليس له تأثير على النتائج.

تدريجيا ، نمت مطالب منتجاتنا. قمنا بتطوير المزيد والمزيد من الوظائف ، وبمجرد أن واجهنا مثل هذه المشكلة: عندما تضيف جزءًا من الشفرة في مكان ما ، قد يحدث شيء ما في مكان آخر حيث يرتبط المشروع بقوة. قررنا التغلب على هذه الآفة عن طريق التحول إلى بنية موجهة نحو الخدمة. لكن الأداء تدهور نتيجة لذلك: عندما تتم مواجهة طلب الشبكة بواسطة مترجم Ruby on Rails عند تنفيذ الشفرة ، يتم حظره ويكون العامل خاملاً. وأصبحت عمليات إدخال / إخراج الشبكة أكثر وأكثر.

نتيجة لذلك ، قررنا اعتماد Go كأحد لغات التطوير الرئيسية.

ميزات تطوير منتجاتنا


أولا ، لدينا متطلبات المنتج مختلفة جدا. نظرًا لأن سياراتنا تعمل في ثلاث دول بقوانين مختلفة تمامًا ، فمن الضروري تطبيق مجموعات مختلفة تمامًا من الوظائف. على سبيل المثال ، في إسرائيل ، يُطلب من الناحية التشريعية النظر في تكلفة الرحلة من قِبل عداد التاكسي - وهذا هو الجهاز الذي يمرر الشهادة الإلزامية كل بضع سنوات. عندما يبدأ السائق الرحلة ، يضغط على زر "go" ، وعندما ينتهي ، يضغط على زر "stop" ويدخل السعر الذي يظهره عداد التاكسي في التطبيق.

لا توجد مثل هذه القوانين الصارمة في روسيا. هنا يمكننا تكوين سياسة التسعير بأنفسنا. على سبيل المثال ، قم بربطها مع مدة الرحلة أو المسافة. في بعض الأحيان ، عندما نريد تنفيذ نفس الوظيفة ، نقوم أولاً بتطبيقها في بلد واحد ، ثم نتكيف ونطرحها في بلدان أخرى.

يضع مديرو منتجاتنا المتطلبات في شكل قصص عن المنتجات ، ونحن نحاول الالتزام بهذه الطريقة فقط. هذا يترك بصماته تلقائيًا على الاختبار: نحن نستخدم منهجية التطوير التي تعتمد على السلوك بحيث يمكن عرض متطلبات المنتج الواردة في مواقف الاختبار. من السهل على الأشخاص البعيدين عن البرمجة قراءة نتائج الاختبار وفهم ما هو.

أردنا أيضًا التخلص من ازدواجية بعض الأعمال. بعد كل شيء ، إذا كانت لدينا خدمة تنفذ نوعًا من الوظائف ، ونحتاج إلى كتابة خدمة ثانية ، وإعادة حل جميع المشكلات التي حلناها في البداية ، وإعادة الاندماج مع أدوات المراقبة والترحيل ، فإن ذلك لن يكون فعالاً.

نحن نحل المشاكل


الإطار


بنيت روبي أون ريلز على MVC الهندسة المعمارية. في وقت الانتقال ، لم نكن نرغب حقًا في التخلي عنه من أجل جعل الحياة أسهل لهؤلاء المطورين الذين يمكنهم البرنامج فقط في هذا الإطار. لا يضيف تغيير الأدوات المزيد من الراحة دون ذلك ، وإذا قمت أيضًا بتغيير بنية التطبيق ، فإن ذلك يعني دفع الشخص الذي لا يعرف كيفية السباحة من القارب. لم نكن نريد إيذاء المطورين بهذه الطريقة ، لذلك أخذنا أحد أطر عمل MVC القليلة في ذلك الوقت والتي تسمى Beego .

لقد حاولنا استخدام Beego ، كما هو الحال في Ruby on Rails ، للقيام بالعرض من جانب الخادم. ومع ذلك ، فإن الصفحة المقدمة على الخادم لا ترضينا حقًا. اضطررت إلى التخلص من مكون واحد ، واليوم تنتج Beego JSON فقط من الواجهة الخلفية ، ويتم تنفيذ جميع عمليات العرض بواسطة React على المقدمة.

Beego يسمح لك لبناء مشروع تلقائيا. كان من الصعب للغاية على بعض المطورين التبديل من لغة البرمجة إلى الحاجة إلى الترجمة. كانت هناك قصص مضحكة عندما قام شخص ما بتنفيذ نوع من الميزات ، وفقط عن طريق مراجعة الكود أو حتى اكتشف عن طريق الخطأ أنه ، يجب أن تقوم بعمل Go-build. والمهمة مغلقة بالفعل.

في Beego ، يتم إنشاء جهاز توجيه من تعليق يكتب فيه المطور المسار إلى إجراءات التحكم. لدينا موقف غامض من هذه الفكرة ، لأنه إذا تمت إعادة كتابة خطأ مطبعي ، على سبيل المثال ، فسيكون من الصعب على أولئك الذين ليسوا متطورين في هذا النهج أن يجدوا خطأً. في بعض الأحيان ، لم يتمكن الأشخاص من معرفة الأسباب حتى بعد عدة ساعات من التصحيح المثير.

قاعدة بيانات


نحن نستخدم PostgreSQL كقاعدة بيانات. هناك مثل هذه الممارسة - للسيطرة على مخطط قاعدة البيانات من رمز التطبيق. هذا مناسب لعدة أسباب: الجميع يعرف عنهم. فهي سهلة النشر ، وقاعدة البيانات دائما متزامنة مع الكود. وأردنا أيضًا الاحتفاظ بهذه الكعك.

عندما يكون لديك العديد من المشاريع والفرق ، في بعض الأحيان لتنفيذ الوظيفة ، عليك الزحف إلى مشاريع الآخرين. ومن المغري للغاية إضافة عمود إلى الجدول ، حيث قد يظهر 10 ملايين سجل. والشخص الذي لا ينغمس في هذا المشروع قد لا يكون على دراية بحجم الجدول. لمنع هذا ، أصدرنا تحذيرًا بشأن عمليات الترحيل الخطيرة التي يمكن أن تمنع قاعدة البيانات للتسجيل ، ومنح المطورين الوسائل لإزالة هذا التحذير.

الهجرة


قررنا الترحيل باستخدام Swan ، وهو عبارة عن أوزة مصححة ، قمنا فيها بإجراء بعض التحسينات. يريد هذان النوعان ، مثل العديد من أدوات الترحيل ، القيام بكل شيء في معاملة واحدة ، بحيث يمكنك التراجع بسهولة في حالة حدوث مشكلات. يحدث أحيانًا أنك تحتاج إلى إنشاء فهرس ، ويتم قفل الجدول. يحتوي PostgreSQL على معلمة concurrently تتجنب ذلك. المشكلة هي أنه إذا كنت في PostgreSQL تبدأ في إنشاء فهرس على هذا concurrently ، وحتى في معاملة ، سوف يظهر لك خطأ. في البداية ، أردنا إضافة علامة حتى لا نفتح معاملة. وفي النهاية ، فعلوا هذا:

 COMMIT; CREATE INDEX CONCURRENTLY huge_index ON huge_table (column_one, column_two); BEGIN; 

الآن ، عندما يضيف شخص ما فهرسًا باستخدام المعلمة concurrently ، يحصل على هذا التلميح. لاحظ أن commit begin لا begin الخلط بينهما. يقوم هذا الرمز بإغلاق المعاملة التي فتحتها أداة الترحيل ، ثم لف المؤشر باستخدام المعلمة concurrently ، ثم يفتح معاملة أخرى حتى تغلق الأداة شيئًا ما.

تجريب


نحن نحاول الالتزام بالتطور الذي يحركه السلوك. في Go ، يمكن القيام بذلك باستخدام أداة الجنكة . إنه جيد لأنه يحتوي على الكلمات الأساسية المعتادة لكل من BDD و "description" و "when" وغيرها ، كما أنه يتيح لك ببساطة إبراز نص مكتوب من قبل مدير المنتج على مواقف الاختبار المخزنة في الكود المصدري. لكن واجهنا مشكلة: يعتقد الأشخاص الذين قدموا من عالم Ruby on Rails أنه يوجد في أي لغة برمجة شيء مشابه لفتاة المصنع - وهو مصنع لتهيئة الظروف الأولية. ومع ذلك ، لم يكن هناك شيء مثل هذا في الذهاب. نتيجة لذلك ، قررنا أننا لن نعيد اختراع العجلة: قبل كل اختبار مباشرة ، وفي الخطافات قبل وبعد الاختبار ، نملأ قاعدة البيانات بالبيانات اللازمة ، ثم نقوم بتنظيفها حتى لا تحدث أي آثار جانبية.

مراقبة


إذا كان لديك خدمة إنتاج يصل إليها الأشخاص ، فأنت بحاجة إلى تتبع عملها: هل هناك خمسمائة خطأ أو يتم معالجة الطلبات بسرعة. في عالم روبي أون ريلز ، غالبًا ما يستخدم NewRelic لهذا الغرض ، وقد امتلكه الكثير من المطورين لدينا بشكل جيد. لقد فهموا كيفية عمل الأداة ، وأين تبحث فيما إذا كانت هناك أية مشكلات. يتيح لك NewRelic تحليل وقت معالجة الطلبات عبر HTTP ، وتحديد المكالمات الخارجية البطيئة والطلبات إلى قاعدة البيانات ، ومراقبة تدفق البيانات ، ويوفر تحليل الأخطاء الذكي والتنبيهات.

NewRelic لديه وظيفة Apdex التجميعية ، والتي تعتمد على الرسم البياني لتوزيع مدة الإجابات وبعض القيم التي تعتقد أنها طبيعية والتي يتم تعيينها في البداية. تعتمد هذه الميزة أيضًا على مستوى الأخطاء في التطبيق. NewRelic يحسب Apdex ويصدر تحذيرًا إذا انخفضت قيمته إلى ما دون مستوى معين.
NewRelic هي أيضا جيدة في وجود وكيل Go الرسمي مؤخرا. هذا ما تبدو عليه نظرة عامة على المراقبة:



على اليسار يوجد مخطط لمعالجة الاستعلام ، كل قسم مقسم إلى مقاطع. تشمل القطاعات قائمة انتظار الطلبات ومعالجة البرامج الوسيطة وطول مدة الإقامة في مترجم Ruby on Rails والوصول إلى المستودعات.

يتم عرض مخطط Apdex في الجزء العلوي الأيمن. أسفل اليمين - وتيرة معالجة الطلبات.

المؤامرة هي أنه في روبي أون ريلز لتوصيل NewRelic تحتاج إلى إضافة سطر واحد من التعليمات البرمجية وإضافة بيانات الاعتماد الخاصة بك إلى التكوين. وكل شيء يعمل بطريقة سحرية. هذا ممكن بسبب حقيقة أنه في روبي أون ريلز يوجد ترقيع للقردة ، وهو غير موجود في Go ، لذلك هناك الكثير للقيام به يدويًا.

بادئ ذي بدء ، أردنا قياس مدة معالجة الطلب. وقد تم ذلك باستخدام السنانير التي قدمتها Beego.

 beego.InsertFilter("*", beego.BeforeRouter, StartTransaction, false) beego.InsertFilter("*", beego.AfterExec, NameTransaction, false) beego.InsertFilter("*", beego.FinishRouter, EndTransaction, false) 

كانت النقطة غير التافهة الوحيدة هي أننا شاركنا في فتح الصفقة وتسميتها. لماذا فعلنا هذا؟ كنت أرغب في قياس مدة معالجة الطلب مع مراعاة الوقت الذي يقضيه في التوجيه. في الوقت نفسه ، نحتاج إلى تقارير مجمعة حسب نقاط النهاية التي جاءت إليها الطلبات. ولكن في وقت فتح المعاملة ، لم نحدد بعد نمط عنوان URL الذي ستحدث به المطابقة. لذلك ، عندما يصل طلب ما ، نفتح معاملة ، ثم على الخطاف ، بعد تنفيذ وحدة التحكم ، ونطلق عليها اسمًا ، وبعد المعالجة ، نغلقها. لذلك ، تبدو تقاريرنا اليوم كما يلي:


استخدمنا ORM يسمى GORM لأننا أردنا الحفاظ على التجريد وليس إجبار المطورين على كتابة SQL خالص. هذا النهج له مزايا وعيوب. في عالم روبي أون ريلز ، يوجد سجل نشط من ORM يفسد الناس حقًا. ينسى المطورون أنه يمكنك كتابة SQL فقط ، والعمل فقط مع مكالمات ORM.

 db.Callback().Create().Before("gorm:begin_transaction"). Register("newrelicStart", startSegment) db.Callback().Create().After("gorm:commit_or_rollback_transaction"). Register("newrelicStop", endSegment) 

لقياس مدة تنفيذ الاستعلام في قاعدة البيانات عند استخدام GORM ، تحتاج إلى اتخاذ كائن db . رد الاتصال يقول أننا نريد تسجيل رد اتصال. يجب أن يتم استدعاؤه عند إنشاء كيان جديد - دعوة إلى الإنشاء. ثم نوضح بالضبط متى يتم تشغيل Callback. Before يكون مسؤولاً عن ذلك باستخدام الوسيطة gorm : begin_transaction هو نقطة ما في وقت فتح المعاملة. بعد ذلك ، مع اسم newrelicStart startSegment وظيفة startSegment ، والتي ببساطة تدعو إلى وكيل Go وتفتح شريحة جديدة للوصول إلى قاعدة البيانات.

سيقوم ORM باستدعاء هذه الوظيفة قبل فتح المعاملة ، وبالتالي فتح القطاع. يجب أن نفعل الشيء نفسه لإغلاق الجزء: فقط قم بتعليق Callback.

بالإضافة إلى PostgreSQL ، نستخدم Redis ، وهي أيضًا ليست سلسة. من أجل هذا الرصد ، كتبنا غلافًا على عميل قياسي ، وفعلنا الشيء نفسه لاستدعاء الخدمات الخارجية. إليك ما حدث:



هذا هو ما يبدو عليه الرصد لتطبيق مكتوب في Go. على اليسار يوجد تقرير عن مدة معالجة الاستعلام ، يتكون من مقاطع: تنفيذ التعليمات البرمجية نفسها في Go ، والوصول إلى قواعد بيانات PostgreSQL و Replica. لا يتم عرض المكالمات إلى الخدمات الخارجية على هذا الرسم البياني ، نظرًا لوجود عدد قليل جدًا منها ولا يمكن رؤيتها ببساطة عند حساب المتوسط. لدينا أيضًا معلومات حول Apdex وتواتر معالجة الطلبات. بشكل عام ، تبين أن المراقبة كانت مفيدة للغاية ومفيدة للاستخدام.

أما بالنسبة لتدفقات البيانات ، فبفضل غلافنا على عميل HTTP ، يمكننا تتبع الطلبات إلى الخدمات الخارجية. يشار إلى مخطط طلب خدمة الترويج هنا: إنه يشير إلى أربعة من خدماتنا الأخرى ومستودعان.



استنتاج


اليوم لدينا أكثر من 75٪ من خدمات الإنتاج المكتوبة في Go ، ونحن لا نجري تطويرًا نشطًا في Ruby ، ​​ولكننا ندعمها فقط. وفي هذا الصدد ، أود أن أشير إلى:

  • لم يتم تأكيد المخاوف من انخفاض سرعة التطوير. تصب المبرمجون في التكنولوجيا الجديدة كل في وضعه الخاص ، ولكن ، في المتوسط ​​، بعد أسبوعين من العمل النشط ، أصبح التطوير على Go متوقعًا وسريعًا مثل Ruby on Rails.
  • أداء تطبيقات Go تحت الحمل مفاجأة سارة مقارنة بالتجربة السابقة. لقد حفظنا بشكل كبير استخدام البنية التحتية في AWS ، مما قلل بشكل كبير من عدد الحالات المستخدمة.
  • لقد شجع التغيير التكنولوجي المبرمجين بشكل كبير ، وهذا جزء مهم من المشروع الناجح.
  • اليوم تركنا بالفعل Beego و Gorm ، المزيد حول هذا الموضوع في المقال التالي.

بإيجاز ، أريد أن أقول إنه إذا لم تكتب على "Go" ، فأنت تعاني من مشاكل أعباء العمل المرتفعة وتُشعر بالملل من حركة المرور ، اذهب إلى هذه اللغة. فقط لا تنسى أن تتفاوض مع الشركة.

Source: https://habr.com/ru/post/ar463097/


All Articles