تقسيم خدمات Service Fabric موثوقة

توفر هذه المقالة مقدمة للمفاهيم الأساسية لتقسيم خدمات Azure Service Fabric الموثوقة. يتيح التقسيم تخزين البيانات على الأجهزة المحلية بحيث يمكن توسيع نطاق البيانات والحوسبة معاً.

تلميح

تتوفر عينة كاملة من التعليمات البرمجية في هذه المقالة على GitHub.

التقسيم

التقسيم ليس فريداً من نوعه لـ Service Fabric. في الواقع، إنه نمط أساسي لبناء خدمات قابلة للتطوير. بمعنى أوسع، يمكننا التفكير في التقسيم كمفهوم لتقسيم الحالة (البيانات) والحساب إلى وحدات أصغر يمكن الوصول إليها لتحسين قابلية التوسع والأداء. وهناك شكلٌ معروفٌ من أشكال التقسيم يتمثل في تقسيم البيانات، أو التجزئة.

تقسيم خدمات Service Fabric "عديمة الحالة"

بالنسبة للخدمات "عديمة الحالة"، يمكنك التفكير في أن القسم هو وحدة منطقية تحتوي على مثيلٍ واحدٍ أو أكثر من الخدمة. يوضح الشكل 1 خدمة "عديمة الحالة" مع خمس مثيلات مُوزَّعة عبر نظام مجموعة باستخدام قسمٍ واحدٍ.

خدمة عديمة الحالة

هناك بالفعل نوعان من حلول الخدمات "عديمة الحالة". الأولى هي خدمة تستمر في حالتها خارجياً، على سبيل المثال في قاعدة بيانات في Azure SQL Database (مثل موقع ويب يخزن معلومات الجلسة وبياناتها). والثانية هي خدمات الحساب فقط (مثل الآلة الحاسبة أو الصورة المصغرة) التي لا تدير أي حالة ثابتة.

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

على سبيل المثال، ضع في اعتبارك الحالة التي يجب فيها تقديم المستخدمين الذين لديهم معرفات في نطاقٍ معينٍ فقط من خلال مثيل خدمة معين. مثال آخر على الوقت الذي يمكنك فيه تقسيم خدمة "عديمة الحالة" هو عندما يكون لديك واجهة خلفية مُقسَّمة حقاً (على سبيل المثال قاعدة بيانات مُجزأة في قاعدة بيانات SQL) وتريد التحكم في مثيل الخدمة الذي يجب أن يكتب إلى جزئية قاعدة البيانات - أو تنفيذ أعمال إعداد أخرى داخل الخدمة "عديمة الحالة" التي تتطلب معلومات التقسيم نفسها المُستخدمة في الواجهة الخلفية. يمكن أيضاً حل هذه الأنواع من السيناريوهات بطرقٍ مختلفةٍ ولا تتطلب بالضرورة تقسيم الخدمة.

يركز الجزء المتبقي من هذه الإرشادات التفصيلية على الخدمات "ذات الحالة".

تقسيم خدمات Service Fabric "ذات الحالة"

تسهِّل Service Fabric تطوير الخدمات "ذات الحالة" القابلة للتطوير من خلال تقديم طريقة من الدرجة الأولى لتقسيم الحالة (البيانات). من الناحية المفاهيمية، يمكنك التفكير في قسم من الخدمة "ذات الحالة" كخادم مخصص موثوق للغاية من خلال النُسخ المتماثلة التي يتم توزيعها وموازنتها عبر العُقد في نظام المجموعة.

يشير التقسيم في سياق خدمات Service Fabric "ذات الحالة" إلى عملية تحديد أن قسماً معيناً من أقسام الخدمة مسؤولٌ عن جزء من الحالة الكاملة للخدمة. (كما ذكرنا من قبل، فإن القسم عبارة عن مجموعة من النُسخ المتماثلة). إن الشيء الرائع في Service Fabric هو أنه يضع الأقسام على عُقدٍ مختلفةٍ. هذا يسمح لهم بالنمو إلى حد موارد العُقدة. مع نمو احتياجات البيانات، تنمو الأقسام، وتعيد خدمة Service Fabric موازنة الأقسام عبر العُقد. وهذا يضمن استمرار الاستخدام الفعال لموارد الأجهزة.

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

يوضح الشكل 2 توزيع 10 أقسام قبل تغيير حجم نظام المجموعة وبعده.

خدمة ذات حالة

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

خطة للتقسيم

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

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

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

تقسيم بسيط

نظراً لاختلاف عدد سكان المدن اختلافاً كبيراً، فقد ينتهي بك الأمر ببعض الأقسام التي تحتوي على الكثير من البيانات (مثل سياتل) وأقسام أخرى ذات حالة ضئيلة جداً (مثل كيركلاند). إذن ما هو تأثير وجود أقسام ذات قدر غير متساوٍ من الحالات؟

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

من أجل تجنب ذلك، يجب عليك القيام بشيئين، من وجهة نظر التقسيم:

  • حاول تقسيم الحالة بحيث يتم توزيعها بالتساوي عبر جميع الأقسام.
  • الإبلاغ عن الحمل من كل من النُسخ المتماثلة للخدمة. (للحصول على معلومات عن كيفية القيام بذلك، راجع هذه المقالة عن المقاييس والتحميل). توفر خدمة Service Fabric القدرة على الإبلاغ عن الحمل الذي تستهلكه الخدمات، مثل مقدار الذاكرة أو عدد السجلات. استنادا إلى المقاييس التي تم الإبلاغ عنها، تكتشف خدمة Service Fabric أن بعض الأقسام تخدم أحمالاً أعلى من غيرها ويعيد توازن نظام المجموعة عن طريق نقل النُسخ المتماثلة إلى عُقد أكثر ملاءمةً، بحيث لا يتم تحميل أي عُقدة بوجهٍ عامٍ.

في بعض الأحيان، لا يمكنك معرفة مقدار البيانات التي ستكون في قسمٍ معينٍ. لذا فإن التوصية العامة هي القيام بالأمرين معاً؛ أولاً، من خلال اعتماد استراتيجية تقسيم تنشر البيانات بالتساوي عبر الأقسام وثانياً، من خلال الإبلاغ عن الحمل. يمنع الأسلوب الأول المواقف المُوضَّحة في مثال التصويت، بينما يساعد الأسلوب الثاني على تسوية الاختلافات المؤقتة في الوصول أو التحميل بمرور الوقت.

جانب آخر من تخطيط الأقسام هو اختيار العدد الصحيح من الأقسام لتبدأ. من منظور Service Fabric، لا يُوجد شيء يمنعك من البدء بعدد أكبر من الأقسام مما كان متوقعاً للسيناريو خاصتك. في الواقع، يعتبر افتراض الحد الأقصى لعدد الأقسام نهجاً صالحاً.

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

هناك اعتبار آخر لتقسيم التخطيط وهو موارد الكمبيوتر المتاحة. نظرا لأن الحالة تحتاج إلى الوصول إليها وتخزينها، فأنت ملزم بمتابعة:

  • حدود النطاق الترددي للشبكة
  • حدود ذاكرة النظام
  • حدود موقع تخزين القرص

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

دليل تخطيط السعة يقدم إرشادات بشأن كيفية تحديد عدد العُقد التي يحتاجها نظام مجموعتك.

بدء التقسيم

يوضح هذا القسم كيفية بدء تقسيم الخدمة لديك.

تقدم Service Fabric خياراً من ثلاثة أنظمة تقسيم:

  • التقسيم المتنوع (المعروف أيضاً باسم UniformInt64Partition).
  • التقسيم المُسمى. عادةً ما تحتوي التطبيقات التي تستخدم هذا النموذج على بيانات يمكن تجميعها ضمن مجموعة محدودة. بعض الأمثلة الشائعة لحقول البيانات المُستخدمة كمفاتيح أقسام مُسماة قد تكون المناطق أو الرموز البريدية أو مجموعات العملاء أو حدود الأعمال الأخرى.
  • تقسيم قاعدة بيانات أحادية. عادةً ما يتم استخدام أقسام ذات قاعدة بيانات أحادية عندما لا تتطلب الخدمة أي توجيه إضافي. على سبيل المثال، تستخدم الخدمات "عديمة الحالة" نظام التقسيم هذا بشكل افتراضي.

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

نظام التقسيم ذو النطاقات

يُستخدم هذا لتحديد نطاق عدد صحيح (يتم تحديده بواسطة مفتاح منخفض ومفتاح مرتفع) وعدد من الأقسام (n). إنه ينشئ عدد (n) من الأقسام، كلٌ منها مسؤول عن نطاق فرعي غير متداخل لنطاق مفتاح القسم العام. على سبيل المثال، إن نظام التقسيم ذو النطاقات مع مفتاح منخفض يبلغ 0 ومفتاح مرتفع يبلغ 99، وعدد 4، من شأنه إنشاء أربعة أقسام، كما هو موضح أدناه.

تقسيم النطاقات

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

حدد لوغاريتم التجزئة

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

تتمثل خصائص لوغاريتم تجزئة التوزيع الجيد في أنه من السهل حسابها، ولديها عدد قليل من التصادمات، وتوزع المفاتيح بالتساوي. هناك مثالٌ جيدٌ على لوغاريتم التجزئة الفعالة وهو خوارزمية التجزئة FNV-1.

ثمة مورد جيد لخيارات لوغاريتم شفرة التجزئة العامة يتمثل في صفحة ويكيبيديا عن وظائف التجزئة.

إنشاء خدمة "ذات حالة" مع أقسام متعددة

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

قبل كتابة أي تعليمة برمجية، تحتاج إلى التفكير في الأقسام ومفاتيح الأقسام. تحتاج إلى 26 قسماً (واحد لكل حرف في الأبجدية)، ولكن ماذا عن المفاتيح المنخفضة والمرتفعة؟ نظراً لأننا نريد حرفياً أن يكون لدينا قسم واحد لكل حرف، يمكننا استخدام 0 كمفتاح منخفض و25 كمفتاح مرتفع، حيث أن كل حرف هو مفتاحه الخاص.

ملاحظة

هذا سيناريو مبسط، لأنه في الواقع سيكون التوزيع غير متساوٍ. الأسماء الأخيرة التي تبدأ بالحروف "S" أو "M" أكثر شيوعاً من تلك التي تبدأ ب "X" أو "Y".

  1. افتح Visual Studio>ملف>جديد>مشروع.

  2. في مربع حوار المشروع الجديد، اختر تطبيق Service Fabric.

  3. بادر بتسمية المشروع بـ "AlphabetPartitions".

  4. في مربع الحوار إنشاء خدمة، اختر خدمة ذات حالة وأطلِق عليها اسم "Alphabet.Processing".

  5. بادر بتعيين عدد الأقسام. افتح ملف ApplicationManifest.xml الموجود في المجلد ApplicationPackageRoot الخاص بمشروع AlphabetPartitions وحدِّث المعلمة Processing_PartitionCount إلى 26 كما هو موضح أدناه.

    <Parameter Name="Processing_PartitionCount" DefaultValue="26" />
    

    تحتاج أيضاً إلى تحديث خصائص LowKey وHighKey لعنصر StatefulService في ApplicationManifest.xml كما هو موضح أدناه.

    <Service Name="Alphabet.Processing">
      <StatefulService ServiceTypeName="Alphabet.ProcessingType" TargetReplicaSetSize="[Processing_TargetReplicaSetSize]" MinReplicaSetSize="[Processing_MinReplicaSetSize]">
        <UniformInt64Partition PartitionCount="[Processing_PartitionCount]" LowKey="0" HighKey="25" />
      </StatefulService>
    </Service>    
    
  6. لكي يمكن الوصول إلى الخدمة، افتح نقطة نهاية على منفذ عن طريق إضافة عنصر نقطة النهاية ServiceManifest.xml (الموجود في المجلد PackageRoot) لخدمة Alphabet.Processing كما هو موضح أدناه:

    <Endpoint Name="ProcessingServiceEndpoint" Port="8089" Protocol="http" Type="Internal" />
    

    الآن يتم تكوين الخدمة للاستماع إلى نقطة نهاية داخلية مع 26 قسماً.

  7. بعد ذلك، تحتاج إلى تجاوز الأسلوب CreateServiceReplicaListeners() الخاص بفئة المعالجة.

    ملاحظة

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

  8. النمط الموصى به لعنوان URL الذي تستمع إليه نسخة متماثلة هو التنسيق التالي: {scheme}://{nodeIp}:{port}/{partitionid}/{replicaid}/{guid}. لذلك تريد تكوين مستمع الاتصال لديك للاستماع إلى نقاط النهاية الصحيحة ومع هذا النمط.

    قد تتم استضافة نُسخ متماثلة متعددة من هذه الخدمة على الكمبيوتر نفسه، لذلك يجب أن يكون هذا العنوان فريداً للنسخة المتماثلة. هذا هو السبب في وجود معرف القسم + معرف النسخة المتماثلة في عنوان URL. يمكن لـ HttpListener الاستماع إلى عناوين متعددة على نفس المنفذ طالما أن بادئة عنوان URL فريدة من نوعها.

    يتوفر المعرف الفريد العمومي الإضافي لحالة متقدمة حيث تستمع النُسخ المتماثلة الثانوية أيضاً إلى طلبات القراءة فقط. عندما تكون هذه هي الحالة، فأنت تريد التأكد من استخدام عنوان فريد جديد عند الانتقال من أساسي إلى ثانوي لإجبار العملاء على إعادة حل العنوان. يتم استخدام "+" كعنوان هنا بحيث تستمع النسخة المتماثلة إلى جميع المضيفين المتاحين (IP وFQDN والمضيف المحلي وما إلى ذلك). توضح التعليمة البرمجية أدناه مثالاً.

    protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
    {
         return new[] { new ServiceReplicaListener(context => this.CreateInternalListener(context))};
    }
    private ICommunicationListener CreateInternalListener(ServiceContext context)
    {
    
         EndpointResourceDescription internalEndpoint = context.CodePackageActivationContext.GetEndpoint("ProcessingServiceEndpoint");
         string uriPrefix = String.Format(
                "{0}://+:{1}/{2}/{3}-{4}/",
                internalEndpoint.Protocol,
                internalEndpoint.Port,
                context.PartitionId,
                context.ReplicaOrInstanceId,
                Guid.NewGuid());
    
         string nodeIP = FabricRuntime.GetNodeContext().IPAddressOrFQDN;
    
         string uriPublished = uriPrefix.Replace("+", nodeIP);
         return new HttpCommunicationListener(uriPrefix, uriPublished, this.ProcessInternalRequest);
    }
    

    تجدر الإشارة أيضاً إلى أن عنوان URL المنشور يختلف قليلاً عن بادئة عنوان URL للاستماع. يتم إعطاء عنوان URL الخاص بالاستماع إلى HttpListener. عنوان URL المنشور هو عنوان URL الذي يتم نشره إلى خدمة تسمية Service Fabric، والذي يُستخدم لاكتشاف الخدمة. سيطلب العملاء هذا العنوان من خلال خدمة الاكتشاف هذه. يجب أن يكون العنوان الذي يحصل عليه العملاء عنوان IP الفعلي أو اسم FQDN الخاص بالعُقدة من أجل الاتصال. لذلك تحتاج إلى استبدال "+" بعنوان IP أو اسم FQDN الخاص بالعُقدة كما هو موضح أعلاه.

  9. الخطوة الأخيرة هي إضافة منطق المعالجة إلى الخدمة كما هو موضح أدناه.

    private async Task ProcessInternalRequest(HttpListenerContext context, CancellationToken cancelRequest)
    {
        string output = null;
        string user = context.Request.QueryString["lastname"].ToString();
    
        try
        {
            output = await this.AddUserAsync(user);
        }
        catch (Exception ex)
        {
            output = ex.Message;
        }
    
        using (HttpListenerResponse response = context.Response)
        {
            if (output != null)
            {
                byte[] outBytes = Encoding.UTF8.GetBytes(output);
                response.OutputStream.Write(outBytes, 0, outBytes.Length);
            }
        }
    }
    private async Task<string> AddUserAsync(string user)
    {
        IReliableDictionary<String, String> dictionary = await this.StateManager.GetOrAddAsync<IReliableDictionary<String, String>>("dictionary");
    
        using (ITransaction tx = this.StateManager.CreateTransaction())
        {
            bool addResult = await dictionary.TryAddAsync(tx, user.ToUpperInvariant(), user);
    
            await tx.CommitAsync();
    
            return String.Format(
                "User {0} {1}",
                user,
                addResult ? "successfully added" : "already exists");
        }
    }
    

    ProcessInternalRequest يقرأ قيم معلمة سلسلة الاستعلام المُستخدمة لاستدعاء القسم ويستدعِ AddUserAsync إلى إضافة اسم العائلة إلى القاموس الموثوق به dictionary.

  10. دعنا نضيف خدمة "عديمة الحالة" إلى المشروع لمعرفة كيف يمكنك استدعاء قسم معين.

    تعمل هذه الخدمة كواجهة ويب بسيطة تقبل اسم العائلة كمعلمة سلسلة استعلام وتحدد مفتاح القسم وترسله إلى خدمة Alphabet.Processing للمعالجة.

  11. في مربع الحوار إنشاء خدمة، اختر خدمة عديمة الحالة وأطلق عليها اسم "Alphabet.Web" كما هو موضح أدناه.

    لقطة شاشة خدمة عديمة الحالة.

  12. بادر بتحديث معلومات نقطة النهاية في ServiceManifest.xml لخدمة Alphabet.WebApi لفتح منفذ كما هو موضح أدناه.

    <Endpoint Name="WebApiServiceEndpoint" Protocol="http" Port="8081"/>
    
  13. تحتاج إلى إرجاع مجموعة من ServiceInstanceListeners في فئة الويب. مرةً أخرى، يمكنك اختيار تنفيذ HttpCommunicationListener بسيطاً.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        return new[] {new ServiceInstanceListener(context => this.CreateInputListener(context))};
    }
    private ICommunicationListener CreateInputListener(ServiceContext context)
    {
        // Service instance's URL is the node's IP & desired port
        EndpointResourceDescription inputEndpoint = context.CodePackageActivationContext.GetEndpoint("WebApiServiceEndpoint")
        string uriPrefix = String.Format("{0}://+:{1}/alphabetpartitions/", inputEndpoint.Protocol, inputEndpoint.Port);
        var uriPublished = uriPrefix.Replace("+", FabricRuntime.GetNodeContext().IPAddressOrFQDN);
        return new HttpCommunicationListener(uriPrefix, uriPublished, this.ProcessInputRequest);
    }
    
  14. الآن تحتاج إلى تنفيذ منطق المعالجة. HttpCommunicationListener يستدعي ProcessInputRequest عند وصول طلب. لذلك دعونا نمضي قدماً ونضيف التعليمة البرمجية أدناه.

    private async Task ProcessInputRequest(HttpListenerContext context, CancellationToken cancelRequest)
    {
        String output = null;
        try
        {
            string lastname = context.Request.QueryString["lastname"];
            char firstLetterOfLastName = lastname.First();
            ServicePartitionKey partitionKey = new ServicePartitionKey(Char.ToUpper(firstLetterOfLastName) - 'A');
    
            ResolvedServicePartition partition = await this.servicePartitionResolver.ResolveAsync(alphabetServiceUri, partitionKey, cancelRequest);
            ResolvedServiceEndpoint ep = partition.GetEndpoint();
    
            JObject addresses = JObject.Parse(ep.Address);
            string primaryReplicaAddress = (string)addresses["Endpoints"].First();
    
            UriBuilder primaryReplicaUriBuilder = new UriBuilder(primaryReplicaAddress);
            primaryReplicaUriBuilder.Query = "lastname=" + lastname;
    
            string result = await this.httpClient.GetStringAsync(primaryReplicaUriBuilder.Uri);
    
            output = String.Format(
                    "Result: {0}. <p>Partition key: '{1}' generated from the first letter '{2}' of input value '{3}'. <br>Processing service partition ID: {4}. <br>Processing service replica address: {5}",
                    result,
                    partitionKey,
                    firstLetterOfLastName,
                    lastname,
                    partition.Info.Id,
                    primaryReplicaAddress);
        }
        catch (Exception ex) { output = ex.Message; }
    
        using (var response = context.Response)
        {
            if (output != null)
            {
                output = output + "added to Partition: " + primaryReplicaAddress;
                byte[] outBytes = Encoding.UTF8.GetBytes(output);
                response.OutputStream.Write(outBytes, 0, outBytes.Length);
            }
        }
    }
    

    دعنا نتصفحه خطوةً بخطوةٍ. تقرأ التعليمات البرمجية الحرف الأول من معلمة سلسلة الاستعلام lastname في حرف. بعد ذلك، يحدد مفتاح القسم لهذا الحرف عن طريق طرح القيمة السداسية العشرية لـ A من القيمة السداسية العشرية للحرف الأول من أسماء العائلة.

    string lastname = context.Request.QueryString["lastname"];
    char firstLetterOfLastName = lastname.First();
    ServicePartitionKey partitionKey = new ServicePartitionKey(Char.ToUpper(firstLetterOfLastName) - 'A');
    

    تذكر، على سبيل المثال، أننا نستخدم 26 قسماً مع مفتاح قسم واحد لكل قسم. بعد ذلك، نحصل على قسم الخدمة partition لهذا المفتاح باستخدام الأسلوب ResolveAsyncالموجود على الكائن servicePartitionResolver. servicePartitionResolver يُعرَّف على النحو التالي

    private readonly ServicePartitionResolver servicePartitionResolver = ServicePartitionResolver.GetDefault();
    

    يتخذ الأسلوب ResolveAsync معرف URI للخدمة ومفتاح القسم ورمز الإلغاء المميز كمعلمات. معرف URI الخاص بالخدمة لخدمة المعالجة هو fabric:/AlphabetPartitions/Processing. بعد ذلك، نحصل على نقطة نهاية القسم.

    ResolvedServiceEndpoint ep = partition.GetEndpoint()
    

    أخيراً، ننشئ عنوان URL لنقطة النهاية بالإضافة إلى سلسلة الاستعلام ونستدعي خدمة المعالجة.

    JObject addresses = JObject.Parse(ep.Address);
    string primaryReplicaAddress = (string)addresses["Endpoints"].First();
    
    UriBuilder primaryReplicaUriBuilder = new UriBuilder(primaryReplicaAddress);
    primaryReplicaUriBuilder.Query = "lastname=" + lastname;
    
    string result = await this.httpClient.GetStringAsync(primaryReplicaUriBuilder.Uri);
    

    بمجرد الانتهاء من المعالجة، نكتب الإخراج مرةً أخرى.

  15. الخطوة الأخيرة هي اختبار الخدمة. يستخدم Visual Studio معلمات التطبيق للتوزيع المحلي والسحابي. لاختبار الخدمة باستخدام 26 قسماً محلياً، تحتاج إلى تحديث الملف Local.xml في مجلد ApplicationParameters الخاص بمشروع AlphabetPartitions كما هو موضح أدناه:

    <Parameters>
      <Parameter Name="Processing_PartitionCount" Value="26" />
      <Parameter Name="WebApi_InstanceCount" Value="1" />
    </Parameters>
    
  16. بمجرد الانتهاء من التوزيع، يمكنك التحقق من الخدمة وجميع أقسامها في Service Fabric Explorer.

    لقطة شاشة مستكشف تصميم الخدمة

  17. في المستعرض، يمكنك اختبار منطق التقسيم عن طريق إدخال http://localhost:8081/?lastname=somename. سترى أن كل اسم عائلة يبدأ بالحرف نفسه يتم تخزينه في القسم نفسه.

    لقطة شاشة المستعرض

يتوفر الحل الكامل للتعليمة البرمجية المستخدمة في هذه المقالة هنا: https://github.com/Azure-Samples/service-fabric-dotnet-getting-started/tree/classic/Services/AlphabetPartitions.

الخطوات التالية

معرفة المزيد عن خدمات Service Fabric: