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

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

مثل هذا التطبيق يتيح لنا حل المشاكل التالية:
- لا يوجد منطق أعمال في ساحة الاستقبال قد يؤدي إلى إبطاء الاتصال الجديد. يوجد هذا النوع من البث على الخادم في نسخة واحدة ، لذا فإن التأخير فيه سيؤثر على الفور على وقت فتح اللوحات ، وإذا كان هناك خطأ في قانون الأعمال ، فيمكن تعليقه بسهولة.
- لا يتم إجراء تهيئة الحالة في تدفق أعمال اللوحات ولا يؤثر على وقت معالجة أوامر العمل من المستخدمين. قد يستغرق الأمر بعض الوقت ، وتعالج تدفقات الأعمال العديد من المجالس في وقت واحد ، وبالتالي فإن فتح المجالس الجديدة لا يؤثر بشكل مباشر على المجالس الحالية.
- غالبًا ما يكون تحليل أوامر الشبكة أسرع من تنفيذها مباشرة ، لذلك قد يختلف تكوين تجمع مؤشرات ترابط الشبكة عن تكوين تجمع مؤشرات ترابط الأعمال من أجل استخدام موارد النظام بكفاءة.
تلوين التدفق
النظام الفرعي الموصوف أعلاه في التطبيق غير بديهي تمامًا. يجب على المطور مراعاة مخطط النظام ومراعاة العملية العكسية للوحات الإغلاق. عند الإغلاق ، من الضروري إزالة جميع الاشتراكات ، وحذف الإدخالات من السجلات والقيام بذلك في نفس التدفقات التي تمت تهيئتها بها.
لاحظنا أن الأخطاء وصعوبات تعديل التعليمات البرمجية التي نشأت في هذا النظام الفرعي كانت مرتبطة غالبًا بعدم فهم سياق التنفيذ. جعل من الصعب التعامل مع سلاسل الرسائل والمهام الإجابة عن السؤال الذي يتم فيه تنفيذ جزء معين من التعليمات البرمجية.
لحل هذه المشكلة ، استخدمنا طريقة تلوين الخيوط - هذه سياسة تهدف إلى تنظيم استخدام الخيوط في النظام. يتم تعيين الألوان إلى مؤشرات الترابط ، وتعرف الطرق نطاق التنفيذ داخل مؤشرات الترابط. اللون هنا عبارة عن تجريد ، يمكن أن يكون أي كيان ، على سبيل المثال ، تعداد. في Java ، يمكن أن تكون التعليقات التوضيحية بمثابة لغة تمييز اللون:
@Color @IncompatibleColors @AnyColor @Grant @Revoke
تتم إضافة التعليقات التوضيحية إلى الطريقة ، وباستخدامها يمكنك تعيين صلاحية هذه الطريقة. على سبيل المثال ، إذا كان التعليق التوضيحي لطريقة ما يسمح باللون الأصفر والأحمر ، فيمكن للخيوط الأولى استدعاء هذه الطريقة ، أما بالنسبة للثانية ، فستكون هذه المكالمة خاطئة.

يمكن تحديد ألوان غير صالحة:

يمكنك إضافة امتيازات الخيط وإزالتها في الديناميات:

يقول عدم وجود تعليق توضيحي أو تعليق توضيحي كما في المثال أدناه أنه يمكن تنفيذ الطريقة في أي مؤشر ترابط:

قد يكون مطورو Android على دراية بهذا النهج الخاص بالتعليقات التوضيحية MainThread و UiThread و WorkerThread وما إلى ذلك.
يستخدم تلوين الخيوط مبدأ رمز التوثيق الذاتي ، والطريقة نفسها تفسح المجال للتحليل الثابت. باستخدام التحليل الثابت ، يمكنك القول قبل تنفيذ التعليمات البرمجية أنه مكتوب بشكل صحيح أم لا. إذا استبعدنا التعليقات التوضيحية الخاصة بالمنحة وإلغاءها ، وافترضنا أن الدفق عند التهيئة لديه بالفعل مجموعة من الامتيازات غير القابلة للتغيير ، فسيكون هذا تحليلًا لا يتأثر بالتدفق - وهو إصدار بسيط من التحليل الثابت لا يأخذ في الاعتبار ترتيب المكالمات.
في وقت تنفيذ طريقة تلوين التدفق ، لم تكن هناك حلول جاهزة للتحليل الثابت في البنية التحتية لدينا devops ، لذلك ذهبنا بطريقة أبسط وأرخص - قدمنا شروحنا ، والتي ترتبط بشكل فريد مع كل نوع من التدفقات. بدأنا في التحقق من صحتها بمساعدة الجوانب في وقت التشغيل.
@Aspect public class ThreadAnnotationAspect { @Pointcut("if()") public static boolean isActive() { …
بالنسبة للجوانب ، نستخدم مكتبة sidesj والمكون الإضافي maven ، والذي يوفر النسيج عند تجميع المشروع. تم تكوين النسيج في البداية لوقت التحميل عند تحميل الفئات باستخدام ClassLoader. ومع ذلك ، فقد واجهنا حقيقة أن الحائك يتصرف في بعض الأحيان بشكل غير صحيح عند تحميل الفئة نفسها بشكل تنافسي ، ونتيجة لذلك ظل الكود المصدري للفئة بدون تغيير. ونتيجة لذلك ، أدى هذا إلى سلوك إنتاج لا يمكن التنبؤ به للغاية وصعب. ربما في الإصدارات الحالية من المكتبة لا توجد مشكلة من هذا القبيل.
سمح لنا الحل المتعلق بالجوانب بالعثور بسرعة على معظم المشكلات الموجودة في الكود.
من المهم ألا تنسى دائمًا تحديث التعليقات التوضيحية: يمكن حذفها ، وإضافة الكسل ، ويمكن إيقاف تشغيل جوانب النسيج تمامًا - وفي هذه الحالة سيفقد التلوين أهميته وقيمته بسرعة.
جوارديبي
واحد من أنواع التلوين هو الشرح GuardedBy من java.util.concurrent. يحدد الوصول إلى الحقول والأساليب ، مع الإشارة إلى الأقفال الضرورية للوصول الصحيح.
public class PrivateLock { private final Object lock = Object(); @GuardedBy (“lock”) Widget widget; void method() { synchronized (lock) {
IDE الحديثة حتى دعم تحليل هذا الشرح. على سبيل المثال ، تعرض IDEA هذه الرسالة إذا كان هناك خطأ في الكود:
طريقة تلوين الخيوط ليست جديدة ، لكن يبدو أنه في لغات مثل Java ، حيث غالبًا ما ينتقل الوصول متعدد الخيوط إلى كائنات قابلة للتغيير ، واستخدامه ليس فقط كجزء من الوثائق ، ولكن أيضًا في مرحلة الترجمة ، يمكن للتجميع أن يبسط بشكل كبير تطوير الشفرة متعددة الخيوط.
ما زلنا نستخدم التنفيذ على الجوانب. إذا كنت معتادًا على حل أو أداة تحليل أكثر أناقة تتيح لك زيادة ثبات هذا النهج في تغييرات النظام ، يرجى مشاركته في التعليقات.