
هذه المقالة هي استمرار لمقال تأملي نشر قبل شهر ، "
هل من السهل إضافة ميزات جديدة إلى الإطار القديم؟ عذاب الاختيار القائم على مثال تطوير SObjectizer ". وصفت هذه المقالة المهمة التي أردنا حلها في الإصدار التالي من SObjectizer ، وفحصت طريقتين لحلها وأدرجت مزايا وعيوب كل نهج.
مع مرور الوقت ، تم تنفيذ أحد النهجين وإصدارات جديدة من
SObjectizer ، بالإضافة إلى
المشروع المصاحب
so_5_extra ، الذي يسمى بالفعل "
التنفس بعمق ". يمكنك أن تأخذ وتحاول حرفيا.
سنتحدث اليوم عما تم فعله ولماذا تم تنفيذه وما الذي أدى إليه. إذا كان شخص ما مهتمًا بمتابعة كيفية تطور أحد إطارات الممثلين القليلة الحية والمنصات والمفتوحة ل C ++ ، فأنت مرحب بك في القط.
كيف بدأ كل شيء؟
بدأ كل شيء بمحاولة حل مشكلة الإلغاء المضمون للمؤقتات. جوهر المشكلة هو أنه عندما يتم إرسال رسالة متأخرة أو دورية ، يمكن للمبرمج إلغاء تسليم الرسالة. على سبيل المثال:
auto timer_id = so_5::send_periodic<my_message>(my_agent, 10s, 10s, ...); ...
بعد استدعاء
timer_id.release () ، لن يرسل المؤقت الوقت مثيلات جديدة لرسالة my_message. لكن تلك النسخ التي تم إرسالها بالفعل وهي في قائمة انتظار المستلمين لن تذهب إلى أي مكان. بمرور الوقت ، سيتم استخراجها من قوائم الانتظار نفسها وسيتم نقلها إلى وكلاء المستلمين للمعالجة.
هذه المشكلة هي نتيجة للمبادئ الأساسية لتشغيل SObjectizer-5 وليس لها حل بسيط بسبب حقيقة أن SObjectizer لا يمكنه استخراج الرسائل من قوائم الانتظار. لا يمكن ذلك لأن قوائم الانتظار في SObjectizer تنتمي إلى المرسلين ، والمرسلون مختلفون ، كما يتم تنظيم قوائم الانتظار الخاصة بهم بشكل مختلف. بما في ذلك
وجود مرسلين ليسوا جزءًا من SObjectizer ، ولا يستطيع SObjectizer ، من حيث المبدأ ، معرفة كيفية عمل هؤلاء المرسلين.
بشكل عام ، هناك مثل هذه الميزة في مؤقتات SObjectizer الأصلية. لا يعني ذلك أنه يفسد المطورين كثيرًا. ولكن يجب توخي الحذر. خاصة للمبتدئين الذين يتعرفون على إطار العمل.
ثم ، أخيرًا ، ذهبت الأيدي إلى حد اقتراح حل لهذه المشكلة.
ما هو مسار الحل الذي تم اختياره؟
في
مقال سابق ، تم النظر في خيارين ممكنين. لا يتطلب الخيار الأول تعديلات على آلية تسليم الرسائل في SObjectizer ، ولكنه يتطلب من المبرمج تغيير نوع الرسالة المرسلة / المستلمة بشكل صريح.
يتطلب الخيار الثاني تعديل آلية تسليم رسائل SObjectizer. تم اختيار هذا المسار ، لأنه سمح للاختباء من مستلم الرسالة بحقيقة أن الرسالة تم إرسالها بطريقة معينة.
ما الذي تغير في SObjectizer؟
مفهوم جديد: مغلف مع رسالة بداخله
المكون الأول للحل الذي تم تنفيذه هو إضافة مفهوم مثل مغلف إلى SObjectizer. المغلف هو رسالة خاصة ، بداخلها الرسالة الحالية (الحمولة). يسلم SObjectizer المغلف بالرسالة إلى المستلم بالطريقة المعتادة تقريبًا. يتم الكشف عن الفرق الأساسي في معالجة المغلفات فقط في المرحلة الأخيرة من التسليم:
- عند تسليم رسالة عادية ، يبحث وكيل المستلم ببساطة عن معالج لهذا النوع من الرسائل ، وإذا تم العثور على مثل هذا المعالج ، يتم استدعاء المعالج الموجود ويتم إرجاع الرسالة التي تم تسليمها كمعلمة ؛
- وعند تسليم المغلف بالرسالة بعد العثور على المعالج ، يتم أولاً محاولة إخراج الرسالة من المغلف. وفقط إذا أعطى المغلف الرسالة المخزنة فيه ، عندها فقط يتم استدعاء المعالج.
هناك نقطتان رئيسيتان هنا لهما تأثير كبير على سبب وكيفية استخدام مغلفات الرسائل.
النقطة الأساسية الأولى هي أن الرسالة لا تُطلب من مظروف إلا عند العثور على معالج الرسالة عند المستلم. على سبيل المثال فقط عندما يتم تسليم الرسالة بالفعل إلى المستلم ويكون المستلم موجودًا الآن وسيعالج هذه الرسالة الآن.
النقطة الرئيسية الثانية هنا هي أن الظرف قد لا يعطي الرسالة فيه. أي ، على سبيل المثال ، يمكن للمظروف أن يتحقق من الوقت الحالي ويقرر أن جميع تواريخ التسليم قد فاتتها ، وبالتالي ، لم تعد الرسالة ذات صلة ولا يمكن معالجتها. لذلك ، لن يعطي الظرف الرسالة. وبناءً على ذلك ، سيتجاهل SObjectizer هذا الظرف ببساطة ولن يتخذ أي إجراءات إضافية.
كيف يبدو المغلف؟
المغلف هو تنفيذ للواجهة envelope_t ، والذي يتم تعريفه على النحو التالي:
class SO_5_TYPE envelope_t : public message_t { public: ...
على سبيل المثال المغلف هو في الأساس نفس الرسالة مثل أي شخص آخر. ولكن بسمة خاصة يتم إرجاعها بواسطة طريقة so5_message_kind ().
يمكن للمبرمج أن يطور
مغلفاته الموروثة من envelope_t (أو ، بشكل أكثر ملاءمة ، من
so_5 :: extra :: enveloped_msg :: just_envelope_t ) وتجاوز معالجة طرق الخطاف handler_found_hook () و shift_hook ().
داخل طرق الخطاف ، يقرر مطور المغلف ما إذا كان يريد إعطاء الرسالة داخل المغلف للمعالجة / التحويل أم لا. إذا رغب في ذلك ، فيجب على المطور استدعاء طريقة invoke () وكائن invoker. إذا لم يرغب ، فلن يتصل ، في هذه الحالة سيتم تجاهل الظرف ومحتوياته.
كيف تحل المغلفات مشكلة إلغاء المؤقتات؟
الحل ، الذي يتم تطبيقه الآن في so_5_extra في شكل مساحة الاسم so_5 :: extra :: revocable_timer ، بسيط للغاية: عندما يتم إرسال رسالة متأخرة أو دورية بشكل خاص ، يتم إنشاء مغلف خاص ، لا يوجد داخله فقط الرسالة نفسها ، ولكن أيضًا تم إبطال العلم الذري. إذا تم مسح هذه العلامة ، فإن الرسالة تعتبر ذات صلة. إذا تم تعيينها ، فسيتم اعتبار الرسالة منسحبة.
عندما يتم استدعاء طريقة الخطاف على المغلف ، يتحقق المغلف من قيمة العلامة المبطلة. إذا تم تعيين العلم ، لا يعطي المغلف رسالة. وبالتالي ، لا تتم معالجة الرسالة حتى إذا تمكن الموقت بالفعل من وضع الرسالة في قائمة انتظار جهاز الاستقبال.
ملحق الواجهة abstract_message_box_t
إن إضافة واجهة envelope_t هي جزء واحد فقط من تنفيذ المغلفات في SObjectizer. الجزء الثاني يأخذ في الاعتبار حقيقة وجود المغلفات في آلية تسليم الرسائل داخل SObjectizer.
هنا ، لسوء الحظ ، لا يمكن الاستغناء عن إجراء تغييرات مرئية للمستخدم. على وجه الخصوص ، في الفئة abstract_message_box_t ، التي تحدد واجهة جميع صناديق البريد في SObjectizer ، كان من الضروري إضافة طريقة افتراضية أخرى:
virtual void do_deliver_enveloped_msg( const std::type_index & msg_type, const message_ref_t & message, unsigned int overlimit_reaction_deep );
هذه الطريقة مسؤولة عن تسليم مغلف رسالة برسالة من النوع msg_type إلى جهاز الاستقبال. قد يختلف هذا التسليم في تفاصيل التنفيذ اعتمادًا على نوع mbox.
عند إضافة do_deliver_enveloped_msg () إلى abstract_message_box_t ، كان لدينا خيار: جعلها طريقة افتراضية خالصة أو تقديم بعض التنفيذ الافتراضي.
إذا جعلنا do_deliver_enveloped_msg () طريقة افتراضية خالصة ، فإننا سنكسر التوافق بين إصدارات SObjectizer في الفرع 5.5. بعد كل شيء ، سيتعين على هؤلاء المستخدمين الذين كتبوا تطبيقات mbox الخاصة بهم تعديل صناديق mbox الخاصة بهم عند التبديل إلى SObjectizer-5.5.23 ، وإلا فلن يتمكنوا من الترجمة مع الإصدار الجديد من SObjectizer.
لم نرغب في ذلك ، لذا لم نجعل do_deliver_enveloped_msg () طريقة افتراضية خالصة في الإصدار 5.5.23. لديه تطبيق افتراضي يطرح استثناء فقط. وبالتالي ، سيتمكن mbox-s المستخدم المخصص من مواصلة العمل بشكل طبيعي مع الرسائل العادية ، ولكنه سيرفض تلقائيًا قبول المغلفات. وجدنا هذا السلوك أكثر قبولا. علاوة على ذلك ، في المرحلة الأولية ، من غير المرجح أن يتم استخدام المغلفات مع الرسائل على نطاق واسع ، ومن غير المحتمل أن يتم العثور على عمليات التنفيذ المخصصة "البرية" الخاصة بصناديق SObjectizer ؛
بالإضافة إلى ذلك ، ليس هناك فرصة على الإطلاق أنه في الإصدارات الرئيسية اللاحقة من SObjectizer ، حيث لن ننظر إلى التوافق مع الفرع 5.5 ، ستخضع واجهة abstract_message_box_t لتغييرات كبيرة. لكننا بالفعل نتقدم على أنفسنا ...
كيفية إرسال المغلفات مع الرسائل
لا يوفر SObjectizer-5.5.23 نفسه وسيلة بسيطة لإرسال المغلفات. من المفترض أن يتم تطوير نوع معين من المغلفات والأدوات المناسبة لمهمة محددة لإرسال المغلفات من نوع معين بشكل ملائم. يمكن رؤية مثال على ذلك في
so_5 :: extra :: revocable_timer ، حيث لا تحتاج فقط إلى إرسال المغلف ، ولكن أيضًا منح المستخدم timer_id خاصًا.
للحالات الأبسط ، يمكنك استخدام الأدوات من
so_5 :: extra :: enveloped_msg . على سبيل المثال ، هذه هي الطريقة التي يتم بها إرسال رسالة بحد معين في وقت التسليم:
لجعل كل شيء ممتع: المغلفات في المغلفات
تم تصميم المغلفات لحمل بعض الرسائل داخل نفسها. لكن أي منها؟
أي.
وهذا ينقلنا إلى سؤال مثير للاهتمام: هل من الممكن وضع مظروف داخل مظروف آخر؟
نعم يمكنك ذلك. بقدر ما تريد. يقتصر عمق التعشيش فقط على الحس السليم للمطور وعمق المكدس لمكالمة العودية العائدة.
في نفس الوقت ، يذهب SObjectizer نحو مطوري المغلفات الخاصة به: يجب ألا يفكر المغلف في ما بداخله - رسالة محددة أو مغلف آخر. عندما يتم استدعاء طريقة الخطاف على الظرف ويقرر المغلف أنه يمكنه إعطاء محتوياته ، يقوم الظرف ببساطة باستدعاء () على handler_invoker_t ويمرر رابطًا إلى محتوياته في الاستدعاء (). وسيستدعي بالفعل () الداخل معرفة ما يتم التعامل معه. وإذا كان هذا مغلفًا آخر ، فإن استدعاء () نفسه سيدعو طريقة الخطاف المطلوبة على هذا المغلف.
باستخدام مجموعة الأدوات الموضحة أعلاه من so_5 :: extra :: enveloped_msg يمكن للمستخدم إنشاء العديد من المغلفات المتداخلة مثل هذا:
so_5::extra::enveloped_msg::make<my_message>(...)
بعض الأمثلة على استخدام المغلفات
الآن ، بعد أن مررنا من خلال المكونات الداخلية لـ SObjectizer-5.5.23 ، حان الوقت للانتقال إلى جزء التطبيق الأكثر فائدة للمستخدمين. فيما يلي بعض الأمثلة التي إما تستند إلى ما تم تنفيذه بالفعل في so_5_extra ، أو استخدم الأدوات من so_5_extra.
مؤقتات قابلة للإلغاء
نظرًا لأن هذا المطبخ بأكمله مع المغلفات قد تم تصميمه من أجل حل مشكلة الاستدعاء المضمون لرسائل المؤقت ، فلنرى ما حدث في النهاية. سنستخدم المثال من so_5_extra-1.2.0 ، الذي يستخدم الأدوات من مساحة الاسم الجديدة so_5 :: extra :: revocable_timer:
رمز مثال مع مؤقتات قابلة للإلغاء #include <so_5_extra/revocable_timer/pub.hpp> #include <so_5/all.hpp> namespace timer_ns = so_5::extra::revocable_timer; class example_t final : public so_5::agent_t { // , // . struct first_delayed final : public so_5::signal_t {}; struct second_delayed final : public so_5::signal_t {}; struct last_delayed final : public so_5::signal_t {}; struct periodic final : public so_5::signal_t {}; // . timer_ns::timer_id_t m_first; timer_ns::timer_id_t m_second; timer_ns::timer_id_t m_last; timer_ns::timer_id_t m_periodic; public : example_t( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { so_subscribe_self() .event( &example_t::on_first_delayed ) .event( &example_t::on_second_delayed ) .event( &example_t::on_last_delayed ) .event( &example_t::on_periodic ); } void so_evt_start() override { using namespace std::chrono_literals; // ... m_first = timer_ns::send_delayed< first_delayed >( *this, 100ms ); m_second = timer_ns::send_delayed< second_delayed >( *this, 200ms ); m_last = timer_ns::send_delayed< last_delayed >( *this, 300ms ); // ... . m_periodic = timer_ns::send_periodic< periodic >( *this, 75ms, 75ms ); // 220ms. // first_delaye, second_delayed // periodic. std::cout << "hang the agent..." << std::flush; std::this_thread::sleep_for( 220ms ); std::cout << "done" << std::endl; } private : void on_first_delayed( mhood_t<first_delayed> ) { std::cout << "first_delayed received" << std::endl; // second_delayed periodic. // , // . m_second.revoke(); m_periodic.revoke(); } void on_second_delayed( mhood_t<second_delayed> ) { std::cout << "second_delayed received" << std::endl; } void on_last_delayed( mhood_t<last_delayed> ) { std::cout << "last_delayed received" << std::endl; so_deregister_agent_coop_normally(); } void on_periodic( mhood_t<periodic> ) { std::cout << "periodic received" << std::endl; } }; int main() { so_5::launch( [](so_5::environment_t & env) { env.register_agent_as_coop( "example", env.make_agent<example_t>() ); } ); return 0; }
ماذا لدينا هنا؟
لدينا وكيل يقوم أولاً ببدء العديد من رسائل المؤقت ، ثم يحظر سلسلة العمل الخاصة به لفترة من الوقت. خلال هذا الوقت ، يدير الموقت طابور الوكيل عدة طلبات نتيجة للمؤقتات التي تم تشغيلها: عدة حالات دورية ، مثيل واحد لـ first_delayed و second_delayed.
وفقا لذلك ، عندما يفتح الوكيل خيطه ، يجب أن يتلقى الدوري الأول والتأخير الأول. عند معالجة first_delayed ، يقوم الوكيل بإلغاء التسليم الدوري والتأخير الثاني. لذلك ، يجب ألا تصل هذه الإشارات إلى الوكيل ، بغض النظر عما إذا كانت موجودة بالفعل في قائمة انتظار الوكيل أم لا (وهي كذلك).
ننظر إلى نتيجة المثال:
hang the agent...done periodic received first_delayed received last_delayed received
نعم إنه كذلك. حصل على الدوري الأول وأول تأخير. ثم ليس هناك تأخير دوري ولا ثاني.
ولكن إذا استبدلنا في هذا المثال "المؤقتات" من so_5 :: extra :: revocable_timer بالمؤقتات القياسية من SObjectizer ، فستكون النتيجة مختلفة: ستصل مثيلات الإشارات الدورية والثانية التي وصلت بالفعل إلى قائمة انتظار الوكيل إلى الوكيل.
الرسائل المقيدة بوقت التسليم
شيء آخر مفيد ، في بعض الأحيان ، الشيء الذي سيصبح متاحًا في so_5_extra-1.2.0 هو تسليم الرسالة بحد زمني. على سبيل المثال ، يرسل وكيل request_handler رسالة check_signature إلى وكيل crypto_master. في الوقت نفسه ، يريد request_handler تسليم عملية التحقق من التوقيع في غضون 5 ثوانٍ. إذا لم يحدث هذا ، فلن يكون هناك أي معنى في معالجة verity_signature ، وسيوقف وكيل request_handler عمله بالفعل.
وكيل crypto_master هو الرفيق الذي يحب أن يكون "عنق الزجاجة": في بعض الأحيان يبدأ في التباطؤ. في مثل هذه اللحظة ، يتم تجميع الرسائل في قائمة الانتظار ، مثل علامة التحقق_المؤقتة أعلاه ، والتي يمكن أن تنتظر حتى يتم إلغاء crypto_master.
لنفترض أن عامل الطلب قد أرسل رسالة التحقق من الصحة إلى وكيل crypto_master ، ولكن تعطل crypto_master ثم توقف لمدة 10 ثوانٍ. وكيل طلب_المُعامل قد "سقط" بالفعل ، أي أرسل بالفعل الجميع رفض الخدمة وأكمل عمله. ولكن تظل رسالة التحقق من صحة التوقيع في قائمة انتظار crypto_master! لذا ، عندما "unsticks" crypto_master ، ستأخذ هذه الرسالة وستعالج هذه الرسالة. على الرغم من أن هذا لم يعد ضروريا.
باستخدام المغلف الجديد so_5 :: extra :: enveloped_msg :: time_limited_delivery_t ، يمكننا حل هذه المشكلة: سيرسل وكيل عامل الطلب request_signature time_limited_delivery_t داخل المغلف مع حد زمني للتسليم:
so_5::extra::enveloped_msg::make<verify_signature>(...) .envelope<so_5::extra::enveloped_msg::time_limited_delivery_t>(5s) .send_to(crypto_master_mbox);
الآن إذا كان crypto_master "يلتصق" ولم يتمكن من الوصول إلى التحقق من التوقيع في 5 ثوانٍ ، فلن يقوم المغلف ببساطة بإرسال هذه الرسالة للمعالجة. ولن يقوم crypto_master بعمل لا يحتاجه أي شخص آخر.
تقارير تسليم المستلمين
وأخيرًا ، مثال على شيء غريب لا يتم تطبيقه بانتظام سواء في SObjectizer أو so_5_extra ، ولكن يمكن القيام به بشكل مستقل.
في بعض الأحيان تريد تلقي شيء من SObjectizer مثل رسالة "تقرير التسليم" إلى المستلم. بعد كل شيء ، شيء واحد عندما وصلت الرسالة إلى المستلم ، ولكن المستلم لسبب ما لم يستجب لها. شيء آخر هو عندما لم تصل الرسالة إلى المستلم على الإطلاق. على سبيل المثال ، تم حظره بواسطة
آلية حماية ضد الحمل الزائد . في الحالة الأولى ، يمكن حذف رسالة لم ننتظر إجابة عليها. ولكن في الحالة الثانية ، قد يكون من المنطقي إعادة إرسال الرسالة بعد مرور بعض الوقت.
سننظر الآن في كيفية تنفيذ أبسط آلية "تقارير التسليم" باستخدام المغلفات.
لذا ، نقوم أولاً بالخطوات التحضيرية اللازمة:
#include <so_5_extra/enveloped_msg/just_envelope.hpp> #include <so_5_extra/enveloped_msg/send_functions.hpp> #include <so_5/all.hpp> using namespace std::chrono_literals; namespace envelope_ns = so_5::extra::enveloped_msg; using request_id_t = int;
الآن يمكننا تحديد الرسائل التي سيتم استخدامها في المثال. الرسالة الأولى هي طلب تنفيذ بعض الإجراءات التي نحتاجها. والرسالة الثانية هي تأكيد وصول الرسالة الأولى إلى المستلم:
struct request_t final { request_id_t m_id; std::string m_data; }; struct delivery_receipt_t final {
بعد ذلك ، يمكننا تحديد وكيل agent_took الذي سيعالج الرسائل من النوع request_t. لكن المعالجة ستكون بتقليد "الالتصاق". على سبيل المثال يقوم بمعالجة request_t ، وبعد ذلك يغير حالته من st_normal إلى st_busy. في حالة st_busy ، لا تفعل شيئًا وتتجاهل جميع الرسائل التي تصل إليها.
هذا يعني أنه إذا أرسل وكيل المعالج_t_t_reet_res.png طلبات متتالية ، فإنه سيعالج الرسالة الأولى وسيتم إلقاء الرسائل الأخرى ، لأن عند معالجة الرسالة الأولى ، سيذهب الوكيل إلى st_busy ويتجاهل ما سيأتي إليه أثناء وجوده في st_busy.
في st_busy ، سوف يقضي الوكيل المعالج_t ثانيتين ، وبعد ذلك سيعود مرة أخرى إلى st_normal وسيكون جاهزًا لمعالجة الرسائل الجديدة.
إليك ما يبدو عليه وكيل المعالج:
class processor_t final : public so_5::agent_t {
الآن يمكننا تحديد وكيل الوكيل customers_generator_t ، والذي يحتوي على مجموعة من الطلبات التي يجب تسليمها إلى المعالج_t. يرسل وكيل request_generator_t الحزمة بأكملها كل 3 ثوانٍ ، ثم ينتظر تأكيد التسليم في شكل delivery_receipt_t.
عند وصول delivery_recept_t ، يطرد وكيل request_generator_t الطلب الذي تم تسليمه خارج الحزمة. إذا كانت العبوة فارغة تمامًا ، فسيتم إكمال المثال. إذا بقي شيء آخر ، فسيتم إرسال الحزمة المتبقية مرة أخرى عندما تصل المرة التالية لإعادة الإرسال.
حتى هنا هو رمز عامل request_generator_t. إنه ضخم للغاية ، ولكنه بدائي.
يمكنك فقط الانتباه إلى العناصر الداخلية لطريقة send_requests () ، التي يتم من خلالها إرسال رسائل request_t ، وتوضع في مغلف خاص.request_generator_t رمز الوكيل class requests_generator_t final : public so_5::agent_t {
الآن لدينا رسائل ووكلاء يتعين عليهم استخدام هذه الرسائل للتواصل. لم يبق سوى القليل - جعل رسائل delivery_receipt_t تصل بطريقة أو بأخرى عند تسليم request_t إلى المعالج_t.يتم ذلك باستخدام هذا الظرف: class custom_envelope_t final : public envelope_ns::just_envelope_t { // . const so_5::mbox_t m_to; // ID . const request_id_t m_id; public: custom_envelope_t(so_5::message_ref_t payload, so_5::mbox_t to, request_id_t id) : envelope_ns::just_envelope_t{std::move(payload)} , m_to{std::move(to)} , m_id{id} {} void handler_found_hook(handler_invoker_t & invoker) noexcept override { // , . // . so_5::send<delivery_receipt_t>(m_to, m_id); // . envelope_ns::just_envelope_t::handler_found_hook(invoker); } };
بشكل عام ، لا يوجد شيء معقد. نرث من so_5 :: extra :: enveloped_msg :: just_envelope_t. هذا هو نوع إضافي من المغلف الذي يقوم بتخزين الرسالة المرفقة فيه ويوفر التنفيذ الأساسيللخطافات handler_found_hook () و shift_hook (). لذلك ، يمكننا فقط حفظ السمات التي نحتاجها داخل custom_envelope_t وإرسال delivery_receipt_t داخل ربط handler_found_hook ().هذا ، في الواقع ، كل شيء.
إذا قمنا بتشغيل هذا المثال ، نحصل على ما يلي: sending request: (0, First) sending request: (1, Second) sending request: (2, Third) sending request: (3, Four) processor: on_request(0, First) request delivered: 0 time to resend requests, pending requests: 3 sending request: (1, Second) sending request: (2, Third) sending request: (3, Four) processor: on_request(1, Second) request delivered: 1 time to resend requests, pending requests: 2 sending request: (2, Third) sending request: (3, Four) processor: on_request(2, Third) request delivered: 2 time to resend requests, pending requests: 1 sending request: (3, Four) processor: on_request(3, Four) request delivered: 3
بالإضافة إلى ذلك ، يجب أن أقول أنه من الناحية العملية ، فإن مثل هذه custom_envelope_t البسيطة لإنشاء تقارير التسليم بالكاد مناسبة. ولكن إذا كان شخص ما مهتمًا بهذا الموضوع ، فيمكن مناقشته في التعليقات ، وليس زيادة حجم المقالة.ماذا يمكن فعله بالمغلفات؟
سؤال عظيم!
التي ليس لدينا أنفسنا إجابة شاملة. ربما ، الإمكانيات محدودة فقط بخيال المستخدمين. حسنًا ، إذا كان هناك شيء مفقود من أجل تحقيق التخيلات في SObjectizer ، فيمكن إخبارنا بذلك. نستمع دائما. والأهم من ذلك أننا نقوم بذلك في بعض الأحيان :)تكامل العوامل مع mchain
بالحديث بشكل أكثر جدية ، هذه ميزة أخرى أود أن أحصل عليها من وقت لآخر والتي تم التخطيط لها حتى so_5_extra-1.2.0. ولكن هذا ، على الأرجح ، لن يقع في الإصدار 1.2.0.يتعلق الأمر بتبسيط تكامل المجموعات والوكلاء.والحقيقة هي أنه في البداية تمت إضافة سلاسل إلى SObjectizer من أجل تبسيط تواصل العملاء مع أجزاء أخرى من التطبيق مكتوبة بدون وكلاء. على سبيل المثال ، هناك الخيط الرئيسي للتطبيق ، الذي يتفاعل معه المستخدم باستخدام واجهة المستخدم الرسومية. وهناك العديد من العاملين بالوكلاء الذين يقومون بالعمل "الشاق" في الخلفية. لا يمثل إرسال رسالة إلى وكيل من سلسلة المحادثات الرئيسية مشكلة: ما عليك سوى الاتصال بإرسال عادي. ولكن كيف يمكن إعادة المعلومات؟لهذا ، تم إضافة mchain-s.ولكن مع مرور الوقت ، اتضح أن المجموعات يمكنها أن تلعب دورًا أكبر بكثير. من الممكن ، من حيث المبدأ ، إنشاء تطبيقات متعددة مؤشرات الترابط على SObjectizer بدون أي عوامل على الإطلاق ، فقط على mchain-ahs (مزيد من التفاصيل هنا ). ويمكنك استخدام mchain-s كوسيلة لموازنة الحمل على الوكلاء. كآلية لحل مشاكل المنتجين والمستهلكين.المشكلة مع المنتج - المستهلك أنه إذا كان المنتج يولد رسائل أسرع مما يستطيع المستهلك التعامل معها ، فإننا في مشكلة. ستنمو قوائم انتظار الرسائل ، وقد ينخفض الأداء بمرور الوقت ، أو يتعطل التطبيق تمامًا بسبب استنفاد الذاكرة.الحل المعتاد الذي اقترحنا استخدامه في هذه الحالة هو الاستخدامزوج من وكلاء جامعي الأداء . يمكنك أيضًا استخدام حدود الرسائل (إما كآلية الحماية الرئيسية ، أو كإضافة إلى أداة التجميع - الأداء). لكن كتابة جامع الأداء تتطلب عمل إضافي من المبرمج.ولكن يمكن استخدام mchains لهذه الأغراض بأقل جهد من المطور. لذا ، سيضع المنتج الرسالة التالية في mchain ، وسيأخذ المستهلك رسائل من هذا mchain.ولكن المشكلة هي أنه عندما يكون المستهلك وكيلًا ، فإنه ليس من الملائم جدًا أن يعمل الوكيل مع mchain من خلال وظائف التلقي () المتاحة وتحديد (). ويمكن محاولة التخلص من هذا الإزعاج بمساعدة بعض الأدوات لدمج الوكلاء و mchain-s.عند تطوير مثل هذه الأداة ، سيكون من الضروري حل العديد من المشاكل. على سبيل المثال ، عندما تصل رسالة إلى mchain ، متى يجب استخراجها من mchain؟ إذا كان المستهلك مجانيًا ولا يعالج أي شيء ، فيمكنك استلام الرسالة من mchain على الفور وإعطائها لوكيل المستهلك. إذا تم بالفعل إرسال رسالة إلى المستهلك من mchain ، فهو لم يتمكن بعد من معالجة هذه الرسالة ، ولكن رسالة جديدة تصل بالفعل إلى mchain ... ما الذي يجب فعله في هذه الحالة؟هناك تكهنات بأن المغلفات قد تساعد في هذه الحالة. لذا ، عندما نأخذ الرسالة الأولى من mchain ونرسلها إلى المستهلك ، نلف هذه الرسالة في مظروف خاص. عندما يرى المغلف أنه تم تسليم الرسالة ومعالجتها ، فإنه يطلب الرسالة التالية من mchain (إذا كان هناك رسالة).بالطبع ، كل شيء ليس بهذه البساطة هنا. لكن حتى الآن تبدو قابلة للحل تمامًا. وآمل أن تظهر آلية مماثلة في أحد الإصدارات القادمة من so_5_extra.هل سنفتح صندوق باندورا؟
وتجدر الإشارة إلى أن القدرات المضافة نفسها تسبب مشاعر مزدوجة.من ناحية ، سمحت / سمحت المغلفات فعلًا بالأشياء التي سبق ذكرها (ولكن حلمت بشيء ما). على سبيل المثال ، يعد هذا إلغاءًا مضمونًا للمؤقتات وتقييدًا لوقت التسليم وتقارير التسليم والقدرة على استدعاء رسالة مرسلة سابقًا.من ناحية أخرى ، ليس من الواضح ما الذي سيؤدي إليه ذلك لاحقًا. بعد كل شيء ، يمكنك أن تثير مشكلة من أي فرصة إذا بدأت في استخدام هذه الفرصة حيثما تريد وأينما لا تحتاج. لذا ربما نفتح صندوق Pandora وما زلنا لا نتخيل ما ينتظرنا؟يبقى فقط أن نتحلى بالصبر ونرى إلى أين سيقودنا كل هذا.حول خطط التطوير الفورية SObjectizer بدلاً من الانتهاء
بدلاً من الاستنتاج ، أود أن أتحدث عن كيف نرى المستقبل القريب جدًا (وليس فقط) لـ SObjectizer. إذا لم يكن شخص ما سعيدًا بشيء في خططنا ، فيمكنك التحدث والتأثير على كيفية تطوير SObjectizer-5.تم بالفعل تثبيت الإصدارات التجريبية الأولى من SObjectizer-5.5.23 و so_5_extra-1.2.0 وهي متاحة للتنزيل والتجارب. سيظل هناك الكثير من العمل الذي يتعين القيام به في مجال التوثيق وحالات الاستخدام. لذلك ، تم التخطيط للإصدار الرسمي في العقد الأول من شهر نوفمبر. إذا نجح الأمر مبكرًا ، فسنفعل ذلك مبكرًا.يبدو أن إصدار SObjectizer-5.5.23 يعني أن تطور الفرع 5.5 يقترب من نهايته. تم إصدار الإصدار الأول في هذا الموضوع قبل أربع سنوات ، في أكتوبر 2014.. منذ ذلك الحين ، تطور SObjectizer-5 داخل الفرع 5.5 دون أي تغييرات كبيرة بين الإصدارات. لم يكن الأمر سهلاً. خاصة بالنظر إلى حقيقة أنه كان علينا كل هذا الوقت أن ننظر إلى الوراء في المترجمات التي كانت بعيدة عن الدعم المثالي لـ C ++ 11.الآن لا نرى أي سبب للنظر إلى التوافق داخل الفرع 5.5 ، وخاصة في المترجمين الأقدم C ++. ما يمكن تبريره في عام 2014 ، عندما كان C ++ 14 يستعد للتو ليتم اعتماده رسميًا ، ولم يكن C ++ 17 في الأفق بعد ، والآن يبدو مختلفًا تمامًا.بالإضافة إلى ذلك ، في SObjectizer-5.5 نفسه ، هناك بالفعل تراكم كمية لا بأس بها من أشعل النار والنسخ الاحتياطي ، والتي ظهرت بسبب هذا التوافق نفسه والتي تعقد التطوير الإضافي لـ SObjectizer.لذلك ، في الأشهر المقبلة ، سنعمل وفقًا للسيناريو التالي:1. تطوير الإصدار التالي من so_5_extra ، حيث أريد إضافة أدوات لتبسيط كتابة الاختبارات للوكلاء. ما إذا كان سيكون so_5_extra-1.3.0 (أي مع كسر التغييرات نسبة إلى 1.2.0) أو ما إذا كان سيكون so_5_extra-1.2.1 (أي دون كسر التغييرات) غير واضح حتى الآن. دعونا نرى كيف ستسير الامور. من الواضح فقط أن الإصدار التالي من so_5_extra سيعتمد على SObjectizer-5.5.1 أ. إذا كان الإصدار التالي من so_5_extra بحاجة إلى القيام بشيء إضافي في SObjectizer-5.5 ، فسيتم إصدار الإصدار التالي 5.5.24. إذا لم يكن من الضروري بالنسبة لـ so_5_extra إجراء تحسينات على kernel لـ SObjectizer ، فإن الإصدار 5.5.23 سيتحول إلى الإصدار الأخير المهم في إطار الفرع 5.5. ستظهر إصدارات بسيطة لإصلاح الأخطاء. ولكن تطوير الفرع 5.5 نفسه يتوقف على الإصدار 5.5.23 أو 5.5.24.2. ثم سيتم إصدار إصدار SObjectizer-5.6.0 ، والذي سيفتح فرعًا جديدًا. في الفرع 5.6 ، سننظف رمز SObjectizer من جميع العكازات والنسخ الاحتياطية المتراكمة ، وكذلك من سلة المهملات القديمة التي تم وضع علامة عليها منذ فترة طويلة على أنها موقوفة. من المحتمل أن تخضع بعض الأشياء لإعادة هيكلة (على سبيل المثال ، يمكن تغيير abstract_message_box_t) ، ولكن بالكاد يكون أصليًا. ستظل المبادئ الأساسية للعمل والسمات المميزة لـ SObjectizer-5.5 في SObjectizer-5.6 بنفس الشكل.سيتطلب SObjectizer-5.6 بالفعل C ++ 14 (على الأقل على مستوى GCC-5.5). لن يتم دعم برامج التحويل البرمجي Visual C ++ أدناه VC ++ 15 (وهو من Visual Studio 2017).نعتبر الفرع 5.6 فرعاً مستقراً من SObjectizer ، والذي سيكون ذا صلة حتى ظهور الإصدار الأول من SObjectizer-5.7.أود إصدار الإصدار 5.6.0 في بداية عام 2019 ، مؤقتًا في فبراير.3. بعد استقرار الفرع 5.6 ، نود أن نبدأ العمل على الفرع 5.7 ، حيث يمكننا مراجعة بعض المبادئ الأساسية لعمل SObjectizer. على سبيل المثال ، التخلي تمامًا عن المرسلين العامين ، تاركين فقط المرسلون الخاصون. إعادة آلية التعاونيات وعلاقاتها بين الوالدين والطفل ، وبالتالي التخلص من الاختناق أثناء تسجيل / إلغاء تسجيل التعاونيات. إزالة القسمة عن طريق الرسالة / الإشارة. اسمح فقط بإرسال / إرسال_ تأخير / إرسال_الفترة الزمنية لإرسال الرسائل وإخفاء طرق التسليم_الرسالة والجدول الزمني "تحت الغطاء". قم بتعديل آلية إرسال الرسائل بحيث تقوم إما بإزالة البث الديناميكي بالكامل من هذه العملية ، أو تقليلها إلى الحد الأدنى.بشكل عام ، هناك مكان للاستدارة. في نفس الوقت ، سيتطلب SObjectizer-5.7 بالفعل C ++ 17 ، بغض النظر عن C ++ 14.إذا نظرت إلى الأشياء بدون نظارات وردية ، من الجيد أن يحدث الإصدار 5.7.0 في أواخر خريف 2019. سيكون إصدار العمل الرئيسي من SObjectizer لعام 2019 هو الفرع 5.6.4. بالتوازي مع كل هذا ، سوف تتطور so_5_extra. على الأرجح ، سيتم إصدار نسخة so_5_extra-2 جنبًا إلى جنب مع SObjectizer-5.6 ، والتي ستشمل وظائف جديدة على مدار عام 2019 ، ولكن استنادًا إلى SObjectizer-5.6.وهكذا ، نرى أنفسنا تطورًا تدريجيًا لـ SObjectizer-5 مع مراجعة تدريجية لبعض المبادئ الأساسية لـ SObjectizer-5. في الوقت نفسه ، سنحاول القيام بذلك بسلاسة قدر الإمكان بحيث يمكن التبديل من إصدار إلى آخر بأقل قدر من الألم.ومع ذلك ، إذا كان شخص ما يريد تغييرات جذرية وهامة من SObjectizer ، فلدينا بعض الأفكار حول هذا الأمر . باختصار: يمكنك إعادة إنشاء SObjectizer كما تريد ، مباشرة لتنفيذ SObjectizer-6 للغة برمجة أخرى. لكننا لن نقوم بذلك بالكامل على نفقتنا الخاصة ، حيث يحدث هذا مع تطور SObjectizer-5.ربما هذا كل شيء.
تحولت التعليقات على المقال السابق إلى مناقشة جيدة وبناءة. سيكون من المفيد لنا إذا حدثت مناقشة مماثلة هذه المرة. كما هو الحال دائمًا ، نحن على استعداد للإجابة على أي أسئلة ، ولكن للأسئلة المعقولة وبسرور.ولأكثر القراء صبرًا الذين وصلوا إلى هذه السطور ، نشكرك كثيرًا على الوقت الذي أمضيته في قراءة المقالة.