المواد ، التي نُنشر ترجمتها اليوم ، مكرسة لقصة تحسين الإصدار الجديد من عميل سطح المكتب
Slack ، والذي كان من أهم ميزاته تسريع عملية التحميل.

قبل التاريخ
في بداية العمل على الإصدار الجديد من عميل سطح المكتب Slack ، تم إنشاء نموذج أولي ، والذي كان يسمى "أحذية سريعة". الهدف من هذا النموذج الأولي ، كما تعتقد ، هو تسريع عملية التنزيل قدر الإمكان. باستخدام ملف HTML من ذاكرة التخزين المؤقت CDN ، وبيانات تخزين Redux المخزنة مسبقًا ، وموظف الخدمة ، تمكنا من تحميل الإصدار الخفيف من العميل في أقل من ثانية (في ذلك الوقت ، كان وقت التنزيل المعتاد للمستخدمين مع 1-2 من مساحات العمل حوالي 5 ثوانٍ ). عامل الخدمة كان مركز هذا التسارع. بالإضافة إلى ذلك ، فقد مهد الطريق للفرص التي طلبنا من مستخدمي Slack تنفيذها كثيرًا. نحن نتحدث عن الوضع غير المتصل للعميل. سمح لنا النموذج الأولي برؤية عينٍ ما يمكن أن يكون عميل remade قادرًا عليه. استنادًا إلى التقنيات المذكورة أعلاه ، بدأنا في معالجة عميل Slack ، بحيث نتخيل النتيجة تقريبًا ونركز على تسريع التحميل وتنفيذ وضع تشغيل البرنامج دون اتصال بالإنترنت. دعونا نتحدث عن كيفية عمل جوهر العميل المحدث.
ما هو عامل الخدمة؟
Service Worker (
Service Worker ) هو في الواقع كائن وكيل قوي لطلبات الشبكة ، والذي يتيح للمطور ، باستخدام كمية صغيرة من كود JavaScript ، التحكم في كيفية معالجة المتصفح لطلبات HTTP الفردية. يدعم عمال الخدمة واجهة برمجة تطبيقات تخزين متقدمة ومرنة ، تم تصميمها بحيث يتم استخدام كائنات الطلب كمفاتيح ، ويتم استخدام كائنات الاستجابة كقيم. يتم تنفيذ عمال الخدمة ، مثل عمال الويب ، في عملياتهم الخاصة ، خارج سلسلة عمليات تنفيذ شفرة JavaScript الرئيسية لأي نافذة متصفح.
Service Worker هو تابع
لذاكرة التخزين المؤقت للتطبيق ، والتي تم إهمالها الآن. كانت عبارة عن مجموعة من واجهات برمجة التطبيقات التي
AppCache
واجهة
AppCache
، والتي تم استخدامها لإنشاء مواقع تقوم بتطبيق ميزات غير
AppCache
. عند العمل مع
AppCache
، تم استخدام ملف بيان ثابت يصف الملفات التي يرغب المطور في
AppCache
مؤقتًا للاستخدام دون اتصال. بشكل عام ،
AppCache
ميزات
AppCache
محدودة بهذا. كانت هذه الآلية بسيطة ، ولكنها غير مرنة ، والتي لم تمنح المطور تحكمًا خاصًا في ذاكرة التخزين المؤقت. في W3C ، تم أخذ ذلك في الاعتبار عند تطوير
مواصفات Worker Service . نتيجة لذلك ، يسمح موظفو الخدمة للمطور بإدارة العديد من التفاصيل المتعلقة بكل جلسة تفاعل على الشبكة يتم تنفيذها بواسطة تطبيق ويب أو موقع ويب.
عندما بدأنا العمل مع هذه التقنية لأول مرة ، كان Chrome هو المتصفح الوحيد الذي يدعمها ، لكننا عرفنا أنه لم يكن هناك متسع من الوقت لانتظار الدعم الواسع للعاملين في الخدمة. الآن هذه التقنية موجودة في
كل مكان ، وهي مدعومة من قبل جميع المتصفحات الرئيسية.
كيف نستخدم عمال الخدمة
عندما يقوم المستخدم بتشغيل عميل Slack جديد لأول مرة ، نقوم بتنزيل مجموعة كاملة من الموارد (HTML و JavaScript و CSS والخطوط والأصوات) ونضعها في ذاكرة التخزين المؤقت لموظفي الخدمة. بالإضافة إلى ذلك ، نقوم بإنشاء نسخة من متجر Redux الموجود في الذاكرة وكتابة هذه النسخة إلى قاعدة البيانات IndexedDB. عند بدء تشغيل البرنامج في المرة القادمة ، نتحقق من وجود ذاكرة التخزين المؤقت المقابلة. إذا كانت كذلك ، فإننا نستخدمها عند تنزيل التطبيق. إذا كان المستخدم متصلاً بالإنترنت ، فسنقوم بتنزيل أحدث البيانات بعد بدء تشغيل التطبيق. إذا لم يكن كذلك ، يبقى العميل قيد التشغيل.
للتمييز بين الخيارين المذكورين أعلاه لتحميل العميل - قدمنا لهم الأسماء: تنزيل ساخن (دافئ) وبارد (بارد). غالبًا ما يحدث التمهيد البارد للعميل عندما يقوم المستخدم بتشغيل البرنامج لأول مرة. في هذه الحالة ، لا توجد موارد مخزنة مؤقتًا أو بيانات Redux مخزنة. مع التشغيل السريع ، لدينا كل ما تحتاجه لتشغيل عميل Slack على كمبيوتر المستخدم. يرجى ملاحظة أنه تتم معالجة معظم الموارد الثنائية (الصور وملفات PDF ومقاطع الفيديو وما إلى ذلك) باستخدام ذاكرة التخزين المؤقت للمستعرض (يتم التحكم في هذه الموارد بواسطة رؤوس التخزين المؤقت العادية). يجب ألا يقوم عامل الخدمة بمعالجتها بطريقة خاصة حتى نتمكن من العمل معهم في وضع عدم الاتصال.
الاختيار بين التحميل الساخنة والباردةدورة حياة عامل الخدمة
يمكن للعاملين في الخدمة التعامل مع ثلاثة أحداث دورة حياة. هذه هي
التثبيت ،
وجلب وتفعيل . أدناه سنتحدث عن كيفية استجابتنا لكل حدث من هذه الأحداث ، ولكن أولاً نحتاج إلى التحدث عن تنزيل وتسجيل موظف الخدمة نفسه. تعتمد دورة حياته على كيفية تعامل المتصفح مع تحديثات ملفات عامل الخدمة. إليك ما يمكنك قراءته حوله على
MDN : "يتم التثبيت إذا كان الملف الذي تم تنزيله جديدًا. يمكن أن يكون هذا إما ملفًا مختلفًا عن الملف الموجود (يتم تحديد الفرق في الملفات عن طريق مقارنتها بالبايت) ، أو ملف عامل خدمة واجهه المستعرض لأول مرة في الصفحة التي تتم معالجتها. "
في كل مرة نقوم فيها بتحديث ملف JavaScript أو CSS أو HTML المطابق ، يتم الانتقال من خلال مكون Webpack المخصص ، الذي ينشئ بيانًا مع وصفًا للملفات المطابقة ذات علامات التجزئة الفريدة (فيما يلي مثال مختصر لملف البيان). هذا البيان مضمن في رمز عامل الخدمة ، مما يؤدي إلى تحديث عامل الخدمة في التمهيد التالي. علاوة على ذلك ، يتم ذلك حتى عندما لا يتغير تنفيذ عامل الخدمة.
الحدث .Event
عندما يتم تحديث عامل الخدمة ، نحصل على حدث
install
. استجابةً لذلك ، نذهب إلى الملفات ، التي ترد أوصافها في البيان المضمن في عامل الخدمة ، ونحمّل كل منها ونضعها في كتلة ذاكرة التخزين المؤقت المقابلة. يتم تنظيم تخزين الملفات باستخدام
Cache API الجديد ، وهو جزء من مواصفات Worker Service. API يخزن كائنات
Response
باستخدام كائنات
Request
كمفاتيح. نتيجة لذلك ، اتضح أن التخزين بسيط للغاية. سارت الامور بشكل جيد مع كيفية تلقي الأحداث عامل الخدمة طلبات وردود العودة.
يتم تعيين مفاتيح كتل التخزين المؤقت بناءً على وقت نشر الحل. يتم تضمين الطابع الزمني في كود HTML ، ونتيجة لذلك ، يمكن إرساله ، كجزء من اسم الملف ، في طلب تنزيل كل مورد. يعد التخزين المؤقت للموارد منفصلة عن كل عملية نشر أمرًا مهمًا لتجنب مشاركة الموارد غير المتوافقة. بفضل هذا ، يمكننا أن نتأكد من أن ملف HTML الذي تم تنزيله في البداية سوف ينزل فقط الموارد المتوافقة ، وهذا صحيح سواء عند تنزيلها عبر الشبكة وعندما يتم تنزيلها من ذاكرة التخزين المؤقت.
vEvent جلب
بعد تسجيل عامل الخدمة ، سيبدأ في معالجة جميع طلبات الشبكة التي تنتمي إلى نفس المصدر. لا يمكن للمطور تقديمه بحيث تتم معالجة بعض الطلبات من قبل عامل خدمة ، بينما لا يقوم البعض الآخر بذلك. لكن المطور لديه السيطرة الكاملة على ما يجب القيام به بالضبط مع الطلبات التي يتلقاها عامل الخدمة.
عند معالجة الطلب ، نقوم أولاً بفحصه. إذا كان ما هو مطلوب موجودًا في البيان وكان في ذاكرة التخزين المؤقت ، فإننا نرجع الرد على الطلب من خلال أخذ البيانات من ذاكرة التخزين المؤقت. إذا كانت ذاكرة التخزين المؤقت لا تحتوي على ما تحتاج إليه ، فسنعيد طلب شبكة حقيقي يصل إلى مورد الشبكة الحقيقي كما لو لم يشارك عامل الخدمة في هذه العملية على الإطلاق. في ما يلي نسخة مبسطة من معالج أحداث
fetch
بنا:
self.addEventListener('fetch', (e) => { if (assetManifest.includes(e.request.url) { e.respondWith( caches .open(cacheKey) .then(cache => cache.match(e.request)) .then(response => { if (response) return response; return fetch(e.request); }); ); } else { e.respondWith(fetch(e.request)); } });
في الواقع ، تحتوي هذه الشفرة على المزيد من المنطق الخاص بـ Slack ، لكن جوهر معالجنا بسيط كما في هذا المثال.
عند تحليل تفاعلات الشبكة ، يمكن التعرف على الاستجابات التي يتم إرجاعها من عامل الخدمة بواسطة علامة ServiceWorker في العمود الذي يشير إلى مقدار البياناتvEvent تفعيل
يتم رفع حدث
activate
بعد التثبيت الناجح لعامل خدمة جديد أو محدث. نستخدمها لتحليل الموارد المخزنة مؤقتًا وإبطال كتل ذاكرة التخزين المؤقت التي مضى عليها أكثر من 7 أيام. هذه ممارسة جيدة للحفاظ على النظام بالترتيب ، وبالإضافة إلى ذلك ، فهي تتيح لك التأكد من عدم استخدام الموارد القديمة عند تحميل العميل.
رمز العميل متخلف عن الإصدار الأخير
ربما لاحظت أن تطبيقنا يتضمن أن أي شخص يبدأ عميل Slack بعد البداية الأولى للعميل لن يحصل على أحدث الموارد ، ولكن تم تخزينها مؤقتًا أثناء تحميل موظف الخدمة. في تطبيق العميل الأصلي ، حاولنا تحديث عامل الخدمة بعد كل تنزيل. ومع ذلك ، يجوز لمستخدم Slack المعتاد ، على سبيل المثال ، تنزيل برنامج مرة واحدة فقط في اليوم ، في الصباح. يمكن أن يؤدي هذا إلى حقيقة أنه سيعمل باستمرار مع عميل يتخلف رمزه طوال اليوم عن الإصدار الأخير (نصدر إصدارات جديدة عدة مرات في اليوم).
على عكس موقع الويب المعتاد ، الذي يغادر بسرعة ، عند مغادرة العميل ، يفتح عميل Slack على كمبيوتر المستخدمين لساعات وهو في حالة مفتوحة. كنتيجة لذلك ، فإن الكود لدينا لديه عمر افتراضي طويل ، مما يتطلب منا استخدام طرق خاصة للحفاظ على أهميتها.
في الوقت نفسه ، نسعى جاهدين لضمان عمل المستخدمين مع أحدث إصدارات التعليمات البرمجية ، حتى يتلقوا أحدث الميزات وإصلاحات الأخطاء وتحسينات الأداء. بعد وقت قصير من إصدار عميل جديد ، قمنا بتطبيق آلية تسمح لنا بتضييق الفجوة بين ما يعمل المستخدمون مع ما أصدرناه. إذا تم نشر إصدار جديد من النظام بعد التحديث الأخير ، فسنحمّل موارد جديدة سيتم استخدامها في المرة التالية التي يقوم فيها العميل بالتمهيد. إذا لم يتم العثور على شيء جديد ، فلن يتم تحميل أي شيء. بعد إجراء هذا التغيير للعميل ، تم تخفيض متوسط متوسط عمر الموارد التي تم تحميل العميل بها إلى النصف.
يتم تنزيل الإصدارات الجديدة من الكود بانتظام ، ولكن عند تنزيل البرنامج ، يتم استخدام أحدث إصدار فقطميزة جديدة أعلام مزامنة
بمساعدة إشارات الميزات الجديدة (Feature Flags) ، نقوم بوضع علامة في قاعدة الشفرة التي لم يتم إكمالها بعد. هذا يسمح لنا بتضمين ميزات جديدة في الكود قبل إصداراتها العامة. هذا النهج يقلل من مخاطر الأخطاء في الإنتاج بسبب حقيقة أنه يمكن اختبار الميزات الجديدة بحرية مع باقي التطبيق ، وذلك قبل وقت طويل من اكتمال العمل عليها.
عادةً ما يتم إصدار الميزات الجديدة في Slack عند إجراء تغييرات على واجهات برمجة التطبيقات المقابلة. قبل أن نبدأ في استخدام العاملين في الخدمة ، كان لدينا ضمان بأن تتم مزامنة الميزات والتغييرات الجديدة في واجهة برمجة التطبيقات. ولكن بعد أن بدأنا في استخدام ذاكرة التخزين المؤقت ، والتي قد لا تحتوي على أحدث إصدار من الشفرة ، اتضح أن العميل قد يكون في موقف لا تتم فيه مزامنة الرمز مع إمكانيات الواجهة الخلفية. من أجل مواجهة هذه المشكلة ، لا نقوم بتخزين الموارد فحسب ، ولكن أيضًا بعض استجابات واجهة برمجة التطبيقات.
حقيقة أن عمال الخدمة يعالجون كل طلبات الشبكة أبسطت الحل. مع كل تحديث لموظف الخدمة ، نقوم ، من بين أشياء أخرى ، بتنفيذ طلبات واجهة برمجة التطبيقات ، وتخزين الاستجابات مؤقتًا في نفس كتلة ذاكرة التخزين المؤقت مثل الموارد المقابلة. يعمل هذا على ربط القدرات والوظائف التجريبية بالموارد المناسبة - التي قد تكون قديمة ، ولكن مضمونة أن تكون متسقة مع بعضها البعض.
هذا ، في الواقع ، ليس سوى قمة جبل الجليد من الفرص المتاحة للمطور بفضل العاملين في الخدمة. يمكن حل المشكلة التي لا يمكن حلها باستخدام آلية
AppCache
، أو تلك التي تتطلب حل كل من العميل والخادم ، بسهولة وبشكل طبيعي باستخدام العاملين في الخدمة و Cache API.
النتائج
قام عامل الخدمة بتسريع عملية تحميل عميل Slack من خلال تنظيم التخزين المحلي للموارد الجاهزة للاستخدام في المرة التالية التي يقوم فيها العميل بالتمهيد. الشبكة - المصدر الرئيسي للتأخير والغموض الذي قد يواجهه مستخدمونا ، ليس لها أي تأثير على الموقف. نحن ، إذا جاز التعبير ، أزلناه من المعادلة. وإذا أمكنك إزالة الشبكة من المعادلة ، فقد اتضح أنه يمكنك تنفيذ وظيفة دون اتصال في المشروع. دعمنا لوضع عدم الاتصال مباشر للغاية في الوقت الحالي. يمكن للمستخدم تنزيل العميل وقراءة الرسائل من المحادثات التي تم تنزيلها. يستعد النظام في نفس الوقت لعلامات المزامنة على رسائل القراءة. ولكن الآن لدينا أساس للتنفيذ المستقبلي لآليات أكثر تقدماً.
بعد عدة أشهر من التطوير والتجريب والتحسين ، تعلمنا الكثير حول كيفية عمل العاملين في الخدمة في الممارسة العملية. بالإضافة إلى ذلك ، اتضح أن هذه التكنولوجيا مناسبة تمامًا للمشاريع الكبيرة. في أقل من شهر من الإصدار العمومي للعميل مع أحد عمال الخدمة ، نجحنا في تلبية عشرات الملايين من الطلبات يوميًا من ملايين عمال الخدمة المثبتين. أدى ذلك إلى تقليل وقت تحميل العملاء الجدد بنسبة 50٪ تقريبًا مقارنة بالزبائن القدامى ، وحقيقة أن التحميل الساخن أسرع بنسبة 25٪ من البرد.
من اليسار إلى اليمين: تحميل عميل قديم ، تحميل بارد عميل جديد ، تحميل ساخن عميل جديد (كلما انخفض المؤشر ، كان ذلك أفضل)أعزائي القراء! هل تستخدم عمال الخدمة في مشاريعك؟
