العمال الدائمون

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

العامل الدائم هو عملية طويلة الأمد بدأها خادم Bazel، وهو يعمل كأداة تضمين حول الأداة الفعلية (عادةً في برنامج التجميع)، أو الأداة نفسها. للاستفادة من العاملين الدائمين، يجب أن تتيح الأداة إجراء سلسلة من التجميع، ويجب أن يشمل البرنامج الترجمة بين واجهة برمجة التطبيقات للأداة وتنسيق الطلب/الاستجابة الموضح أدناه. يمكن استدعاء العامل نفسه باستخدام العلامة --persistent_worker في الإصدار نفسه وبدونها، وهو مسؤول عن تشغيل الأداة والتحدّث إليها بشكل مناسب، بالإضافة إلى إيقاف تشغيل الموظفين عند الخروج. يتم تخصيص دليل عمل منفصل لكل <outputBase>/bazel-workers في نسخة افتراضية من العمل (ولكن لا يتم اقتطاعه).

إنّ استخدام العاملين الدائمين هو استراتيجية تنفيذ تقلّل من النفقات العامة، وتتيح المزيد من التجميع أثناء التنفيذ، وتتيح تفعيل التخزين المؤقت بأشجار البنية التجريدية في عملية تنفيذ الإجراء. وتنطوي هذه الاستراتيجية على هذه التحسينات من خلال إرسال طلبات متعددة إلى عملية طويلة الأمد.

يتم تنفيذ الموظفين الدائمين للغات متعددة، بما في ذلك Java وScala وKotlin وغيرها.

يمكن للبرامج التي تستخدم وقت تشغيل NodeJS استخدام مكتبة المساعدة @bazel/agent من أجل تنفيذ بروتوكول العامل.

استخدام العاملين الدائمين

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

bazel build //my:target --strategy=Javac=worker,local

يمكن أن يؤدي استخدام استراتيجية العاملين بدلاً من الاستراتيجية المحلية إلى تعزيز سرعة التجميع بشكل كبير، اعتمادًا على التنفيذ. بالنسبة إلى Java، يمكن أن تكون الإصدارات أسرع بمعدل مرتين إلى 4 مرات، وأحيانًا أكثر للتجميع المتزايد. يستغرق تجميع محتوى Bazel حوالي 2.5 مرة مقارنةً بالعمّال. لمزيد من التفاصيل، راجع القسم "اختيار عدد العاملين".

إذا كانت لديك بيئة إصدار عن بُعد تتطابق مع بيئتك المحلية، يمكنك استخدام الاستراتيجية الديناميكية التجريبية التي تعمل على تنفيذ التنفيذ عن بُعد وتنفيذ العاملين. لتفعيل الاستراتيجية الديناميكية، مرِّر العلامة --experimental_spawn_scheduler. تفعّل هذه الاستراتيجية العاملين تلقائيًا، لذلك لا حاجة إلى تحديد الاستراتيجية worker، ولكن لا يزال بإمكانك استخدام local أو sandboxed كعناصر احتياطية.

اختيار عدد العاملين

يمثّل العدد التلقائي لمثيلات العامل في كلية منى 4، ولكن يمكن تعديله باستخدام العلامة worker_max_instances. وهناك فرق بين الاستفادة من وحدات المعالجة المركزية (CPU) المتاحة ومقدار تجميع البيانات أثناء التنفيذ وتجميع نتائج ذاكرة التخزين المؤقت التي تحصل عليها. مع المزيد من العاملين، سيدفع المزيد من الأهداف المستهدفة تكاليف بدء تشغيل الرمز غير المركب وضغط ذاكرة التخزين المؤقت البارد. إذا كان لديك عدد قليل من الأهداف المطلوب إنشاؤها، يمكن أن يقدّم عامل واحد أفضل العروض من حيث سرعة التجميع عند استخدام الموارد واستخدام الموارد (على سبيل المثال، يمكنك مراجعة المشكلة #8586). تحدّد العلامة worker_max_instances الحد الأقصى لعدد مثيلات العاملين لكل تذكير أو مجموعة علامات (يمكنك الاطّلاع على المعلومات أدناه)، لذا قد ينتهي بك الأمر في استخدام نظام مختلَط باستخدام الكثير من الذاكرة في حال الاحتفاظ بالقيمة التلقائية. بالنسبة إلى الإصدارات التدريجية، تصبح فائدة مثيلات العاملين المتعددة أصغر حجمًا.

يعرض هذا الرسم البياني أوقات التجميع من الخدش لتطبيق Bazel (المستهدَف //src:bazel) على محطة عمل Intel Xeon بسرعة 3 نواة ومعالج بسرعة 3.5 غيغاهرتز مع ذاكرة وصول عشوائي بسعة 64 غيغابايت. بالنسبة إلى كل عملية إعداد للعاملين، يتم تنفيذ خمسة إصدارات سليمة ويتم أخذ متوسط الإصدارات الأربعة الأخيرة.

رسم بياني لتحسين الأداء للمباني النظيفة

الشكل 1. رسم بياني لتحسينات الأداء للمباني الواضحة.

بالنسبة إلى هذه الإعدادات، يقدّم عاملان أسرع تجميع، لكن عند التحسّن بنسبة %14 فقط مقارنةً بعامل واحد. ويُعد العامل خيارًا جيدًا إذا كنت تريد استخدام ذاكرة أقل.

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

يؤدي تجميع مصادر Java فقط (//src/main/java/com/google/devtools/build/lib/bazel:BazelServer_deploy.jar) بعد تغيير ثابت سلسلة داخلي في AbstractContainerizingSandboxedSpawn.java يمنح السرعة 3 أضعاف (بمتوسط 20 إصدارًا متزايدًا مع تصميم واحد للتسخين):

رسم بياني لتحسينات الأداء للإصدارات المتزايدة

الشكل 2. رسم بياني لتحسينات الأداء للإصدارات المتزايدة.

وتعتمد السرعة على التغيير الذي يتم إجراؤه. تُقاس سرعة العامل 6 في الحالة أعلاه عند تغيير ثابت ثابت الاستخدام.

تعديل العاملين الدائمين

يمكنك تمرير علامة --worker_extra_flag لتحديد علامات بدء التشغيل للعاملين، مع الضغط على رمز الذاكرة. على سبيل المثال، يؤدي تفعيل --worker_extra_flag=javac=--debug إلى تفعيل تصحيح الأخطاء في JavaScript فقط. لا يمكن ضبط أكثر من علامة عامل واحدة لكل استخدام لهذه العلامة، ولتذكّر واحد فقط. لا يتم إنشاء العاملين بشكلٍ منفصل لكل تذكير فقط، بل أيضًا للصيغ المختلفة في علامات بدء التشغيل. يتم دمج كل مجموعة من علامات الذاكرة وبدء التشغيل في WorkerKey، ويمكن إنشاء ما يصل إلى worker_max_instances عامل لكل WorkerKey. يُرجى الاطّلاع على القسم التالي للتعرّف على طريقة ضبط الإجراء بين علامات الإعداد.

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

يؤدي تمرير العلامة --worker_sandboxing إلى جعل كل طلب عامل يستخدم دليلاً منفصلاً لوضع الحماية لكل إدخالاته. يستغرِق إعداد وضع الحماية بعض الوقت الإضافي، خاصةً على نظام التشغيل macOS، ولكنه يوفّر ضمانًا أفضل للصحّة.

تقدّم العلامة --worker_quit_after_build فائدة أساسية في تصحيح الأخطاء وتصحيح ملفات التعريف. وتفرض هذه العلامة على جميع العاملين الخروج بعد اكتمال التصميم. ويمكنك أيضًا تمرير --worker_verbose للحصول على مزيد من النتائج حول أداء العاملين. تنعكس هذه العلامة في الحقل verbosity في WorkRequest، ما يسمح بعمليات تنفيذ الموظفين أن تكون أكثر تفصيلاً.

يُخزّن العاملون في سجلاتهم الدليل <outputBase>/bazel-workers، على سبيل المثال /tmp/_bazel_larsrc/191013354bebe14fdddae77f2679c3ef/bazel-workers/worker-1-Javac.log. يتضمّن اسم الملف رقم تعريف العامل والتذكير. بما أنّه يمكن استخدام أكثر من ملف WorkerKey واحد لكل الذاكرة، قد ترى أكثر من worker_max_instances ملف سجلّ مخصّص لذكرى معيّنة.

وبالنسبة إلى إصدارات Android، يُرجى الاطّلاع على التفاصيل في صفحة أداء إصدار Android.

تنفيذ العاملين الدائمين

يمكنك الاطّلاع على صفحة إنشاء عمّال مستمرين للحصول على مزيد من المعلومات حول كيفية جعل العمّال.

يوضح هذا المثال إعداد Starlark للعامل الذي يستخدم JSON:

args_file = ctx.actions.declare_file(ctx.label.name + "_args_file")
ctx.actions.write(
    output = args_file,
    content = "\n".join(["-g", "-source", "1.5"] + ctx.files.srcs),
)
ctx.actions.run(
    mnemonic = "SomeCompiler",
    executable = "bin/some_compiler_wrapper",
    inputs = inputs,
    outputs = outputs,
    arguments = [ "-max_mem=4G",  "@%s" % args_file.path],
    execution_requirements = {
        "supports-workers" : "1", "requires-worker-protocol" : "json" }
)

من خلال هذا التعريف، سيبدأ الاستخدام الأول لهذا الإجراء بتنفيذ سطر الأوامر /bin/some_compiler -max_mem=4G --persistent_worker. سيظهر عندئذٍ طلب تجميع Foo.java على النحو التالي:

ملاحظة: تستخدم مواصفات المخزن المؤقت للبروتوكولات &"snake case" (request_id)، يتم استخدام حالة JSON مع "quot;جمال>&quot؛ (requestId). في هذا المستند، سنستخدم حالة الجمل في أمثلة JSON، ولكن بالنسبة إلى حالة الثعبان عند التحدث عن الحقل، بغض النظر عن البروتوكول.

{
  "arguments": [ "-g", "-source", "1.5", "Foo.java" ]
  "inputs": [
    { "path": "symlinkfarm/input1", "digest": "d49a..." },
    { "path": "symlinkfarm/input2", "digest": "093d..." },
  ],
}

يتلقّى العامل ذلك على stdin بتنسيق JSON المحدَّد بفواصل (لأنّه تم ضبط السمة requires-worker-protocol على JSON). بعد ذلك، ينفِّذ العامل الإجراء ويرسل WorkResponse بتنسيق JSON إلى Bazel على ملف stdout الخاص به. بعد ذلك، يحلِّل"بازل"هذا الردّ ويحوِّله يدويًا إلى نموذج أوّلي من WorkResponse. للتواصل مع العامل المرتبط باستخدام بروتوكول Protobuf الثنائي بدلاً من JSON، سيتم ضبط requires-worker-protocol على proto، كما يلي:

  execution_requirements = {
    "supports-workers" : "1" ,
    "requires-worker-protocol" : "proto"
  }

في حال عدم تضمين requires-worker-protocol في متطلبات التنفيذ، ستعمل Bazel على التواصل تلقائيًا مع العاملين لاستخدام Protobuf.

يستمد Bazel العلامة WorkerKey من التذكّر والعلامات المشتركة، لذا إذا سمح هذا الضبط بتغيير المعلَمة max_mem، سيتم تشغيل عامل منفصل لكل قيمة مستخدَمة. وقد يؤدي ذلك إلى استهلاك زائد للذاكرة إذا تم استخدام عدد كبير جدًا من الصيغ.

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

في أداة GitHub هذه، يمكنك الاطّلاع على أمثلة عن برامج تضمين العاملين المكتوبة بلغة Java، بالإضافة إلى لغة Python. إذا كنت تعمل على JavaScript أو TypeScript، قد تكون حزمة@bazel/agent ومثال العامل nodejs مفيدة.

كيف يؤثر العاملون في وضع الحماية؟

لا يؤدي استخدام استراتيجية worker بشكل تلقائي إلى تنفيذ الإجراء في وضع الحماية، كما هو الحال مع استراتيجية local. يمكنك ضبط العلامة --worker_sandboxing لتشغيل جميع العاملين داخل وضع الحماية، مع التأكد من أن كل عملية تنفيذ للأداة لا ترى سوى ملفات الإدخال التي من المفترض أن تشتمل عليها. وقد تستمر الأداة في تسرب المعلومات بين الطلبات داخليًا، مثلاً من خلال ذاكرة تخزين مؤقت. يتطلب استخدام الاستراتيجية dynamic وضع العمّال في وضع الحماية.

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

حتى عند استخدام ملخصات الإدخال للحماية من التخزين المؤقت غير المرغوب فيه، يوفر العاملون في وضع الحماية وضع حماية أقل صرامة من وضع الحماية الخالص، لأن الأداة قد تحتفظ بحالة داخلية أخرى تأثرت بالطلبات السابقة.

لا يمكن استخدام وضع الحماية المتعدد إلا في وضع الحماية إذا كان العامل يدعمه، ويجب تفعيل وضع الحماية هذا بشكل منفصل باستخدام العلامة --experimental_worker_multiplex_sandboxing. اطّلع على مزيد من التفاصيل في مستند التصميم.

المزيد من القراءة

لمزيد من المعلومات عن العاملين الدائمين، راجع: