ASP.NET الأساسية في الخدمات الموثوقة لنسيج خدمة Azure

ASP.NET Core هو إطار مفتوح المصدر ومتعدد المنصات. صُمم هذا الإطار لإنشاء تطبيقات قائمة على السحابة ومتصلة بالإنترنت، مثل تطبيقات الويب وتطبيقات إنترنت الأشياء والنهايات الخلفية للجوال.

هذه المقالة هي دليل متعمق لاستضافة الخدمات الأساسية ASP.NET في "خدمات نسيج الخدمة" الموثوقة باستخدام ⁧⁩Microsoft.ServiceFabric.AspNetCore. ⁧⁩ مجموعة من حزم NuGet.

للحصول على برنامج تعليمي تمهيدي حول ASP.NET Core in Service Fabric وإرشادات حول إعداد بيئة التطوير خاصتك، راجع ⁧⁩البرنامج التعليمي: إنشاء تطبيق ونشره باستخدام خدمة الواجهة الأمامية لواجهة برمجة تطبيقات الويب الأساسية ASP.NET وخدمة خلفية رائعة⁧⁩.

تفترض بقية هذه المقالة أنك تعرف ASP.NET Core فعلاً. إذا لم يكن الأمر كذلك، يُرجى قراءة ⁧⁩ASP.NET الأساسية ⁧⁩.

ASP.NET الأساسية في بيئة نسيج الخدمة

يمكن تشغيل كل من تطبيقات ASP.NET Core ونسيج الخدمة على .NET Core أو .NET Framework الكامل. يمكنك استخدام ASP.NET Core بطريقتين مختلفتين في نسيج الخدمة:

  • ⁩ مستضافة كضيف قابل للتنفيذ⁧⁩. تُستخدم هذه الطريقة بشكل أساسي لتشغيل تطبيقات ASP.NET Core الموجودة على نسيج الخدمة دون أي تغييرات في التعليمات البرمجية.
  • ⁩ تشغيل داخل خدمة موثوق بها⁧⁩. تتيح هذه الطريقة تكاملاً أفضل مع وقت تشغيل نسيج الخدمة وتسمح بخدمات ASP.NET الأساسية الرائعة.

تُبين بقية هذه المقالة كيفية استخدام ASP.NET Core داخل خدمة موثوقة، من خلال مكونات تكامل Core ASP.NET التي تُشحن مع Service Fabric SDK.

استضافة خدمة نسيج الخدمة

في نسيج الخدمة، يتم تشغيل مثيل واحد أو أكثر و/أو نسخ متماثلة من خدمتك في ⁧⁩عملية مضيف الخدمة⁧⁩: ملف قابل للتنفيذ يقوم بتشغيل التعليمات البرمجية للخدمة. بصفتك مؤلف خدمة، تمتلك عملية مضيف الخدمة، ويقوم نسيج الخدمة بتنشيطها ومراقبتها نيابة عنك.

يقترن ASP.NET التقليدي (حتى MVC 5) بإحكام ب IIS من خلال System.Web.dll. يوفر ASP.NET Core فصلاً بين خادم الويب وتطبيق الويب خاصتك. يسمح هذا الفصل لتطبيقات الويب بأن تكون محمولة بين خوادم الويب المختلفة. كما يسمح باستضافة خوادم الويب ⁧⁩الذاتية⁧⁩. هذا يعني أنه يمكنك بدء تشغيل خادم ويب في العملية خاصتك، بدلاً من العملية التي يملكها برنامج خادم ويب مخصص، مثل IIS.

للجمع بين خدمة Service Fabric ASP.NET، إما كضيف قابل للتنفيذ أو في خدمة موثوقة، يجب أن تكون قادرًا على بدء ASP.NET داخل عملية مضيف الخدمة. تتيح لك ASP.NET Core الاستضافة الذاتية للقيام بذلك.

Hosting ASP.NET الأساسية في خدمة موثوقة

عادةً ما تقوم تطبيقات ASP.NET Core المستضافة ذاتيًا بإنشاء WebHost في نقطة دخول التطبيق، مثل الطريقة ⁧static void Main()⁩ في ⁧Program.cs⁩. في هذه الحالة، ترتبط دورة حياة WebHost بدورة حياة العملية.

استضافة ASP.NET Core في عملية

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

يتم تمثيل مثيل الخدمة الموثوقة من خلال فئة الخدمة خاصتك المستمدة من ⁧StatelessService⁩ أو ⁧StatefulService⁩. يتم احتواء مكدس ذاكرة مؤقتة للاتصالات لخدمة في تنفيذ ⁧ICommunicationListener⁩ فئة الخدمة خاصتك. تحتوي حزم NuGet ⁧Microsoft.ServiceFabric.AspNetCore.*⁩ على تطبيقات ⁧ICommunicationListener⁩ التي تُشغل وتدير ASP.NET Core WebHost إما لـ Kestrel أو HTTP.sys في خدمة موثوقة.

رسم تخطيطي لاستضافة ASP.NET Core في خدمة موثوقة

ASP.NET Core ICommunicationListeners

تحتوي التطبيقات ⁧ICommunicationListener⁩ لـ Kestrel وHTTP.sys في حزم NuGet ⁧Microsoft.ServiceFabric.AspNetCore.*⁩ على أنماط استخدام مماثلة. لكنهم يقومون بإجراءات مختلفة قليلاً خاصة بكل خادم ويب.

يوفر كل من مستمعي الاتصال منشئًا يأخذ الوسيطات التالية:

  • ServiceContext serviceContext⁩: هذا هو العنصر ⁧ServiceContext⁩ الذي يحتوي على معلومات حول الخدمة الجارية التشغيل.
  • string endpointName⁩: هذا هو اسم تكوين ⁧Endpoint⁩ في ServiceManifest.xml. إنه في المقام الأول المكان الذي يختلف فيه اثنان من مستمعو التواصل. HTTP.sys ⁧⁩يتطلب⁧⁩ تكوين ⁧Endpoint⁩، في حين أن Kestrel لا يتطلب.
  • Func<string, AspNetCoreCommunicationListener, IWebHost> build⁩: هذا هو lambda التي تنفذها، والتي تقوم فيها بإنشاء وإرجاع ⁧IWebHost⁩. يسمح لك بتكوين ⁧IWebHost⁩ الطريقة التي تريدها عادةً في تطبيق ASP.NET Core. يوفر lambda عنوان URL أُنشئ لك، اعتمادًا على خيارات تكامل نسيج الخدمة التي تستخدمها والتكوين ⁧Endpoint⁩ الذي تقدمه. يمكنك بعد ذلك تعديل عنوان URL هذا أو استخدامه لبدء تشغيل خادم الويب.

تكامل نسيج الخدمة الوسيطة

تتضمن حزمة NuGet ⁧Microsoft.ServiceFabric.AspNetCore⁩ طريقة التوسيع ⁧UseServiceFabricIntegration⁩ ⁧IWebHostBuilder⁩ التي تضيف برامج وسيطة بنسيج الخدمة. تُكون هذه البرامج الوسيطة Kestrel أو HTTP.sys ⁧ICommunicationListener⁩ لتسجيل عنوان URL فريد للخدمة مع خدمة تسمية نسيج الخدمة. ثم يتحقق من صحة طلبات العملاء لضمان اتصال العملاء بالخدمة الصحيحة.

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

حالة من الهوية الخطأ

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

  1. تستمع الخدمة A على 10.0.0.1:30000 عبر HTTP.
  2. يحل العميل الخدمة A ويحصل على العنوان 10.0.0.1:30000.
  3. تنتقل الخدمة A إلى عقدة مختلفة.
  4. تُوضع الخدمة B على 10.0.0.1 وتستخدم بالصدفة نفس المنفذ 30000.
  5. يحاول العميل الاتصال بالخدمة A باستخدام العنوان المخزن مؤقتًا 10.0.0.1:30000.
  6. يتم الآن توصيل العميل بنجاح بالخدمة B، دون إدراك أنه متصل بالخدمة الخطأ.

هذا يمكن أن يسبب أخطاء في أوقات عشوائية يمكن أن يكون من الصعب تشخيصها.

استخدام عناوين URL الفريدة للخدمة

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

في بيئة موثوق بها، تقوم البرامج الوسيطة التي تتم إضافتها بواسطة طريقة ⁧UseServiceFabricIntegration⁩ تلقائيًا بإلحاق معرف فريد بالعنوان المذكور في "خدمة التسمية". ويتحقق من صحة هذا المعرف في كل طلب. إذا لم يتطابق المعرف، فسيقوم البرنامج الوسيط على الفور بإرجاع استجابة HTTP 410 Gone.

يجب أن تستفيد الخدمات التي تستخدم منفذًا معينًا ديناميكيًا من هذه البرامج الوسيطة.

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

يُبين الرسم التخطيطي التالي تدفق الطلب مع تمكين البرامج الوسيطة:

تكامل Service Fabric ASP.NET Core

تستخدم كل من تطبيقات Kestrel وHTTP.sys ⁧ICommunicationListener⁩ هذه الآلية بنفس الطريقة تمامًا. على الرغم من أن HTTP.sys يمكنه التمييز داخليًا بين الطلبات استنادًا إلى مسارات عناوين URL الفريدة باستخدام ميزة مشاركة المنفذ ⁧⁩HTTP.sys⁧⁩ الأساسية، فإن هذه الوظيفة ⁧⁩لا⁧⁩ تُستخدم من قِبل تطبيق HTTP.sys ⁧ICommunicationListener⁩. هذا لأنه ينتج عنه رموز حالة خطأ HTTP 503 وHTTP 404 في السيناريو الموضح سابقًا. وهذا بدوره يجعل من الصعب على العملاء تحديد الهدف من الخطأ، حيث يتم استخدام HTTP 503 وHTTP 404 بشكل شائع للإشارة إلى الأخطاء الأخرى.

ومن ثمّ، فإن تطبيقات Kestrel وHTTP.sys ⁧ICommunicationListener⁩ توحد البرامج الوسيطة التي توفرها طريقة توسيع ⁧UseServiceFabricIntegration⁩. لذلك، يحتاج العملاء فقط إلى تنفيذ إجراء إعادة حل نقطة نهاية الخدمة على استجابات HTTP 410.

HTTP.sys في خدمات موثوقة

يمكنك استخدام HTTP.sys في "خدمات موثوقة" عن طريق استيراد حزمة NuGet ⁧⁩Microsoft.ServiceFabric.AspNetCore.HttpSys⁧⁩. تحتوي هذه الحزمة على ⁧HttpSysCommunicationListener⁩، تنفيذ ⁧ICommunicationListener⁩. ⁧HttpSysCommunicationListener⁩يسمح لك بإنشاء ASP.NET Core WebHost داخل خدمة موثوقة باستخدام HTTP.sys كخادم ويب.

أُنشئ HTTP.sys على واجهة برمجة تطبيقات خادم HTTP ⁧⁩Windows⁧⁩. تستخدم واجهة برمجة التطبيقات هذه برنامج تشغيل⁧⁩ kernel⁧⁩HTTP.sys لمعالجة طلبات HTTP وتوجيهها إلى العمليات التي تُشغل تطبيقات الويب. يسمح ذلك لعمليات متعددة على نفس الجهاز الفعلي أو الظاهري باستضافة تطبيقات الويب على نفس المنفذ، مع إزالة الغموض إما عن طريق مسار عنوان URL فريد أو اسم مضيف. هذه الميزات مفيدة في نسيج الخدمة لاستضافة مواقع ويب متعددة في نفس المجموعة.

ملاحظة

HTTP.sys التنفيذ يعمل فقط على نظام Windows.

يُبين الرسم التخطيطي التالي كيفية استخدام HTTP.sys لبرنامج تشغيل kernel⁧⁩HTTP.sys⁧⁩ على Windows لمشاركة المنفذ:

 رسم تخطيطيHTTP.sys

HTTP.sys في خدمة غير متماثلة

للاستخدام ⁧HttpSys⁩ في خدمة غير متماثلة، تجاوز طريقة ⁧CreateServiceInstanceListeners⁩ وأَرجِع مثيل ⁧HttpSysCommunicationListener⁩:

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
                new WebHostBuilder()
                    .UseHttpSys()
                    .ConfigureServices(
                        services => services
                            .AddSingleton<StatelessServiceContext>(serviceContext))
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                    .UseStartup<Startup>()
                    .UseUrls(url)
                    .Build()))
    };
}

HTTP.sys في خدمة راقية

HttpSysCommunicationListener⁩لم يُصَمَّم حاليًا للاستخدام في الخدمات ذات الحالة بسبب التعقيدات مع ميزة مشاركة منفذ ⁧⁩HTTP.sys⁧⁩ الأساسية. لمزيد من المعلومات، راجع القسم التالي حول تخصيص المنفذ الديناميكي باستخدام HTTP.sys. بالنسبة إلى الخدمات المتميزة، فإن Kestrel هو خادم الويب المقترح.

تكوين نقطة النهاية

يلزم تكوين ⁧Endpoint⁩ لخوادم الويب التي تستخدم واجهة برمجة تطبيقات Windows HTTP Server، بما في ذلك HTTP.sys. يجب على خوادم الويب التي تستخدم واجهة برمجة تطبيقات Windows HTTP Server أولاً حجز عنوان URL خاصتها باستخدام HTTP.sys (يتحقق ذلك عادة باستخدام الأداة ⁧⁩netsh⁧⁩).

يتطلب هذا الإجراء امتيازات مرتفعة لا تتمتع بها خدماتك افتراضيًا. تُستخدم خيارات "http" أو "https" الخاصة بخاصية ⁧Protocol⁩ التكوين ⁧Endpoint⁩ في ServiceManifest.xml خصيصًا لتوجيه وقت تشغيل نسيج الخدمة لتسجيل عنوان URL باستخدام HTTP.sys نيابة عنك. يفعل باستخدام بادئة عنوان URL ⁧⁩⁧⁩القوية لحرف البدل⁧⁩⁧⁩.

على سبيل المثال، للحجز ⁧http://+:80⁩ لخدمة ما، استخدم التكوين التالي في ServiceManifest.xml:

<ServiceManifest ... >
    ...
    <Resources>
        <Endpoints>
            <Endpoint Name="ServiceEndpoint" Protocol="http" Port="80" />
        </Endpoints>
    </Resources>

</ServiceManifest>

ويجب تمرير اسم نقطة النهاية إلى منشئ ⁧HttpSysCommunicationListener⁩:

 new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
 {
     return new WebHostBuilder()
         .UseHttpSys()
         .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
         .UseUrls(url)
         .Build();
 })

استخدام HTTP.sys مع منفذ ثابت

لاستخدام منفذ ثابت مع HTTP.sys، وفِّر رقم المنفذ في تكوين ⁧Endpoint⁩:

  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" Port="80" />
    </Endpoints>
  </Resources>

استخدام HTTP.sys مع منفذ ديناميكي

لاستخدام منفذ معين ديناميكيًا مع HTTP.sys، احذف الخاصية ⁧Port⁩ في تكوين ⁧Endpoint⁩:

  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" />
    </Endpoints>
  </Resources>

يوفر المنفذ الديناميكي المخصص بواسطة تكوين ⁧Endpoint⁩ منفذًا واحدًا فقط ⁧⁩لكل عملية مضيف⁧⁩. يسمح نموذج استضافة نسيج الخدمة الحالي باستضافة مثيلات خدمة متعددة و/أو نُسخ متماثلة في نفس العملية. وهذا يعني أن كل واحد سيشارك نفس المنفذ عند تخصيصه من خلال تكوين ⁧Endpoint⁩. يمكن لمثيلات ⁧⁩HTTP.sys⁧⁩ المتعددة مشاركة منفذ باستخدام ميزة مشاركة منفذ ⁧⁩HTTP.sys⁧⁩ الأساسية. لكنه غير مدعوم ⁧HttpSysCommunicationListener⁩ بسبب التعقيدات التي يقدمها لطلبات العملاء. لاستخدام المنفذ الديناميكي، Kestrel هو خادم الويب المقترح.

Kestrel في خدمات موثوقة

يمكنك استخدام Kestrel في "الخدمات الموثوقة" عن طريق استيراد حزمة NuGet ⁧⁩Microsoft.ServiceFabric.AspNetCore.Kestrel⁧⁩. تحتوي هذه الحزمة على ⁧KestrelCommunicationListener⁩، تنفيذ ⁧ICommunicationListener⁩. ⁧KestrelCommunicationListener⁩يسمح لك بإنشاء ASP.NET Core WebHost داخل خدمة موثوقة باستخدام Kestrel كخادم ويب.

Kestrel هو خادم ويب عبر الأنظمة الأساسية ل ASP.NET Core. على عكس HTTP.sys، لا يستخدم Kestrel مدير نقطة نهاية مركزي. أيضًا على عكس HTTP.sys، لا يدعم Kestrel مشاركة المنفذ بين عمليات متعددة. يجب أن يستخدم كل مثيل من Kestrel منفذًا فريدًا. لمزيد من المعلومات حول Kestrel، راجع ⁧⁩تفاصيل التنفيذ⁧⁩.

رسم تخطيطي ل Kestrel

Kestrel في خدمة غير متماثلة

للاستخدام ⁧Kestrel⁩ في خدمة غير متماثلة، تجاوز طريقة ⁧CreateServiceInstanceListeners⁩ وأَرجِع مثيل ⁧KestrelCommunicationListener⁩:

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
                new WebHostBuilder()
                    .UseKestrel()
                    .ConfigureServices(
                        services => services
                            .AddSingleton<StatelessServiceContext>(serviceContext))
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                    .UseStartup<Startup>()
                    .UseUrls(url)
                    .Build();
            ))
    };
}

Kestrel في خدمة رائعة

للاستخدام ⁧Kestrel⁩ في خدمة ذات حالة، تجاوز طريقة ⁧CreateServiceReplicaListeners⁩ وإرجاع مثيل ⁧KestrelCommunicationListener⁩:

protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
    return new ServiceReplicaListener[]
    {
        new ServiceReplicaListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, (url, listener) =>
                new WebHostBuilder()
                    .UseKestrel()
                    .ConfigureServices(
                         services => services
                             .AddSingleton<StatefulServiceContext>(serviceContext)
                             .AddSingleton<IReliableStateManager>(this.StateManager))
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                    .UseStartup<Startup>()
                    .UseUrls(url)
                    .Build();
            ))
    };
}

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

يتم ⁧Endpoint⁩ توفير اسم تكوين ⁧⁩لا⁧⁩ ⁧KestrelCommunicationListener⁩ في خدمة الحالة. يُشرح ذلك بمزيد من التفصيل في القسم التالي.

تكوين Kestrel لاستخدام HTTPS

عند تمكين HTTPS باستخدام Kestrel في خدمتك، ستحتاج إلى تعيين العديد من خيارات الاستماع. حدِث ⁧ServiceInstanceListener⁩ لاستخدام نقطة نهاية ⁧⁩EndpointHttps⁧⁩ والاستماع إلى منفذ معين (مثل المنفذ 443). عند تكوين مضيف الويب لاستخدام خادم الويب Kestrel، يجب عليك تكوين Kestrel للاستماع إلى عناوين IPv6 على جميع واجهات الشبكة:

new ServiceInstanceListener(
serviceContext =>
    new KestrelCommunicationListener(
        serviceContext,
        "EndpointHttps",
        (url, listener) =>
        {
            ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

            return new WebHostBuilder()
                .UseKestrel(opt =>
                {
                    int port = serviceContext.CodePackageActivationContext.GetEndpoint("EndpointHttps").Port;
                    opt.Listen(IPAddress.IPv6Any, port, listenOptions =>
                    {
                        listenOptions.UseHttps(GetCertificateFromStore());
                        listenOptions.NoDelay = true;
                    });
                })
                .ConfigureAppConfiguration((builderContext, config) =>
                {
                    config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                })

                .ConfigureServices(
                    services => services
                        .AddSingleton<HttpClient>(new HttpClient())
                        .AddSingleton<FabricClient>(new FabricClient())
                        .AddSingleton<StatelessServiceContext>(serviceContext))
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                .UseUrls(url)
                .Build();
        }))

للحصول على مثال كامل في برنامج تعليمي، راجع ⁧⁩تكوين Kestrel لاستخدام HTTPS⁧⁩.

تكوين نقطة النهاية

لا يلزم تكوين ⁧Endpoint⁩ لاستخدام Kestrel.

Kestrel هو خادم ويب مستقل بسيط. على عكس HTTP.sys (أو HttpListener)، فإنه لا يحتاج إلى تكوين ⁧Endpoint⁩ في ServiceManifest.xml لأنه لا يتطلب تسجيل عنوان URL قبل البدء.

استخدام Kestrel مع منفذ ثابت

يمكنك تكوين منفذ ثابت في التكوين ⁧Endpoint⁩ لـ ServiceManifest.xml للاستخدام مع Kestrel. على الرغم من أن هذا ليس ضروريًا للغاية، فإنه يوفر فائدتين محتملتين:

  • إذا لم يقع المنفذ في نطاق منفذ التطبيق، فإنه مفتوح من خلال جدار حماية نظام التشغيل بواسطة نسيج الخدمة.
  • سيستخدم عنوان URL المقدم لك من خلال ⁧KestrelCommunicationListener⁩ هذا المنفذ.
  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" Port="80" />
    </Endpoints>
  </Resources>

إذا كُون ⁧Endpoint⁩، فيجب تمرير اسمه إلى منشئ ⁧KestrelCommunicationListener⁩:

new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) => ...

إذا كان ServiceManifest.xml لا يستخدم تكوين ⁧Endpoint⁩، فاحذف الاسم في منشئ ⁧KestrelCommunicationListener⁩. في هذه الحالة، سيستخدم منفذًا ديناميكيًا. راجع القسم التالي للحصول على مزيد من المعلومات حول هذا.

استخدام Kestrel مع منفذ ديناميكي

يتعذر على Kestrel استخدام تعيين المنفذ التلقائي من تكوين ⁧Endpoint⁩ في ServiceManifest.xml. وذلك لأن تعيين المنفذ التلقائي من تكوين ⁧Endpoint⁩ يعين منفذًا فريدًا لكل⁧⁩عملية مضيف⁧⁩، ويمكن أن تحتوي عملية مضيف واحد على مثيلات Kestrel متعددة. هذا لا يعمل مع Kestrel لأنه لا يدعم مشاركة المنفذ. لذلك، يجب فتح كل مثيل Kestrel على منفذ فريد.

لاستخدام تعيين المنفذ الديناميكي مع Kestrel، احذف تكوين ⁧Endpoint⁩ في ServiceManifest.xml بالكامل، ولا تقم بتمرير اسم نقطة نهاية إلى منشئ ⁧KestrelCommunicationListener⁩، كما يلي:

new KestrelCommunicationListener(serviceContext, (url, listener) => ...

في هذا التكوين، سيحدد ⁧KestrelCommunicationListener⁩ تلقائيًا منفذ غير مستخدم من نطاق منفذ التطبيق.

بالنسبة إلى HTTPS، يجب أن تحتوي على نقطة النهاية التي كُونت باستخدام بروتوكول HTTPS بدون منفذ محدد في ServiceManifest.xml وتمرير اسم نقطة النهاية إلى منشئ KestrelCommunicationListener.

تكامل IHost ونموذج الحد الأدنى من الاستضافة

بالإضافة إلى IWebHost/IWebHostBuilder، يدعم KestrelCommunicationListener وHttpSysCommunicationListener بناء خدمات ASP.NET Core باستخدام IHost/IHostBuilder. يتوفر هذا بدءًا من الإصدار 5.2.1363 من Microsoft.ServiceFabric.AspNetCore.Kestrel والحزم Microsoft.ServiceFabric.AspNetCore.HttpSys.

// Stateless Service
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                return Host.CreateDefaultBuilder()
                        .ConfigureWebHostDefaults(webBuilder =>
                        {
                            webBuilder.UseKestrel()
                                .UseStartup<Startup>()
                                .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                                .UseContentRoot(Directory.GetCurrentDirectory())
                                .UseUrls(url);
                        })
                        .ConfigureServices(services => services.AddSingleton<StatelessServiceContext>(serviceContext))
                        .Build();
            }))
    };
}

// Stateful Service
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
    return new ServiceReplicaListener[]
    {
        new ServiceReplicaListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                return Host.CreateDefaultBuilder()
                        .ConfigureWebHostDefaults(webBuilder =>
                        {
                            webBuilder.UseKestrel()
                                .UseStartup<Startup>()
                                .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                                .UseContentRoot(Directory.GetCurrentDirectory())
                                .UseUrls(url);
                        })
                        .ConfigureServices(services =>
                        {
                            services.AddSingleton<StatefulServiceContext>(serviceContext);
                            services.AddSingleton<IReliableStateManager>(this.StateManager);
                        })
                        .Build();
            }))
    };
}

ملاحظة

نظرًا لأن KestrelCommunicationListener وHttpSysCommunicationListener مخصصان لخدمات الويب، فمن المطلوب تسجيل/تكوين خادم ويب (باستخدام أسلوب ConfigureWebHostDefaults أو ConfigureWebHost) عبر IHost

قدم ASP.NET 6 نموذج الحد الأدنى من الاستضافة وهو طريقة أكثر تبسيطًا لإنشاء تطبيقات الويب. يمكن أيضًا استخدام نموذج الحد الأدنى من الاستضافة مع KestrelCommunicationListener وHttpSysCommunicationListener.

// Stateless Service
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                var builder = WebApplication.CreateBuilder();

                builder.Services.AddSingleton<StatelessServiceContext>(serviceContext);
                builder.WebHost
                            .UseKestrel()
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                            .UseUrls(url);

                builder.Services.AddControllersWithViews();

                var app = builder.Build();

                if (!app.Environment.IsDevelopment())
                {
                    app.UseExceptionHandler("/Home/Error");
                }

                app.UseHttpsRedirection();
                app.UseStaticFiles();
                app.UseRouting();
                app.UseAuthorization();
                app.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");

                return app;
            }))
    };
}
// Stateful Service
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
    return new ServiceReplicaListener[]
    {
        new ServiceReplicaListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                var builder = WebApplication.CreateBuilder();

                builder.Services
                            .AddSingleton<StatefulServiceContext>(serviceContext)
                            .AddSingleton<IReliableStateManager>(this.StateManager);
                builder.WebHost
                            .UseKestrel()
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                            .UseUrls(url);

                builder.Services.AddControllersWithViews();

                var app = builder.Build();

                if (!app.Environment.IsDevelopment())
                {
                    app.UseExceptionHandler("/Home/Error");
                }
                app.UseStaticFiles();
                app.UseRouting();
                app.UseAuthorization();
                app.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");

                return app;
            }))
    };
}

مزود تكوين نسيج الخدمة

يعتمد تكوين التطبيق في ASP.NET Core على أزواج القيم الرئيسية التي أنشأها موفر التكوين. اقرأ ⁧⁩التكوين في ASP.NET Core⁧⁩ لفهم المزيد حول دعم تكوين ASP.NET Core العام.

يوضح هذا القسم كيفية تكامل موفر تكوين نسيج ىالخدمة مع التكوين الأساسي ASP.NET عن طريق استيراد حزمة NuGet ⁧Microsoft.ServiceFabric.AspNetCore.Configuration⁩.

ملحقات بدء تشغيل AddServiceFabricConfiguration

بعد استيراد حزمة NuGet ⁧Microsoft.ServiceFabric.AspNetCore.Configuration⁩، تحتاج إلى تسجيل مصدر تكوين نسيج الخدمة مع واجهة برمجة تطبيقات تكوين ASP.NET الأساسية. يمكنك القيام بذلك عن طريق التحقق من ملحقات ⁧⁩AddServiceFabricConfiguration⁧⁩ في مساحة اسم ⁧Microsoft.ServiceFabric.AspNetCore.Configuration⁩ مقابل ⁧IConfigurationBuilder⁩.

using Microsoft.ServiceFabric.AspNetCore.Configuration;

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddServiceFabricConfiguration() // Add Service Fabric configuration settings.
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; }

الآن يمكن لخدمة ASP.NET Core الوصول إلى إعدادات تكوين نسيج الخدمة، تمامًا مثل أي إعدادات تطبيق أخرى. على سبيل المثال، يمكنك استخدام نمط الخيارات لتحميل الإعدادات إلى كائنات مكتوبة بقوة.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MyOptions>(Configuration);  // Strongly typed configuration object.
    services.AddMvc();
}

تعيين المفتاح الافتراضي

افتراضيًا، يتضمن موفر تكوين نسيج الخدمة اسم الحزمة واسم القسم واسم العقار. تُشكل هذه معًا مفتاح تكوين ASP.NET Core، كما يلي:

$"{this.PackageName}{ConfigurationPath.KeyDelimiter}{section.Name}{ConfigurationPath.KeyDelimiter}{property.Name}"

على سبيل المثال، إذا كان لديك حزمة تكوين سُميت ⁧MyConfigPackage⁩ مع المحتوى التالي، فستتوفر قيمة التكوين على ASP.NET Core ⁧IConfiguration⁩ من خلال ⁧⁩MyConfigPackage:MyConfigSection:MyParameter⁧⁩.

<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">  
  <Section Name="MyConfigSection">
    <Parameter Name="MyParameter" Value="Value1" />
  </Section>  
</Settings>

خيارات تكوين نسيج الخدمة

يدعم موفر تكوين نسيج الخدمة أيضًا ⁧ServiceFabricConfigurationOptions⁩ لتغيير السلوك الافتراضي لتعيين المفاتيح.

الإعدادات المُشَفرة

يدعم نسيج الخدمة الإعدادات المشفرة، وكذلك موفر تكوين نسيج الخدمة. لا يُفك تشفير الإعدادات المشفرة إلى ASP.NET Core ⁧IConfiguration⁩ افتراضيًا. تُخَزن القيم المشفرة هناك بدلاً من ذلك. ولكن إذا كنت ترغب في فك تشفير القيمة المراد تخزينها في ASP.NET Core IConfiguration، فإنه يمكنك تعيين العلامة ⁧⁩DecryptValue⁧⁩ إلى خطأ في ملحق ⁧AddServiceFabricConfiguration⁩، كما يلي:

public Startup()
{
    ICodePackageActivationContext activationContext = FabricRuntime.GetActivationContext();
    var builder = new ConfigurationBuilder()        
        .AddServiceFabricConfiguration(activationContext, (options) => options.DecryptValue = false); // set flag to decrypt the value
    Configuration = builder.Build();
}

حزم تكوين متعددة

يدعم نسيج الخدمة حزم تكوين متعددة. افتراضيًا، يتم تضمين اسم الحزمة في مفتاح التكوين. ولكن يمكنك تعيين العلامة ⁧IncludePackageName⁩ إلى خطأ، على النحو التالي:

public Startup()
{
    ICodePackageActivationContext activationContext = FabricRuntime.GetActivationContext();
    var builder = new ConfigurationBuilder()        
        // exclude package name from key.
        .AddServiceFabricConfiguration(activationContext, (options) => options.IncludePackageName = false); 
    Configuration = builder.Build();
}

تعيين المفاتيح المخصصة واستخراج القيمة ومجموعة البيانات

يدعم موفر تكوين نسيج الخدمة أيضًا سيناريوهات أكثر تقدمًا لتخصيص تعيين المفاتيح باستخدام ⁧ExtractKeyFunc⁩ واستخراج القيم حسب الطلب باستخدام ⁧ExtractValueFunc⁩. يمكنك حتى تغيير العملية الكاملة لملء البيانات من تكوين نسيج الخدمة إلى تكوين ASP.NET Core باستخدام ⁧ConfigAction⁩.

تُبين الأمثلة التالية كيفية الاستخدام ⁧ConfigAction⁩ لتخصيص مجتمع البيانات:

public Startup()
{
    ICodePackageActivationContext activationContext = FabricRuntime.GetActivationContext();
    
    this.valueCount = 0;
    this.sectionCount = 0;
    var builder = new ConfigurationBuilder();
    builder.AddServiceFabricConfiguration(activationContext, (options) =>
        {
            options.ConfigAction = (package, configData) =>
            {
                ILogger logger = new ConsoleLogger("Test", null, false);
                logger.LogInformation($"Config Update for package {package.Path} started");

                foreach (var section in package.Settings.Sections)
                {
                    this.sectionCount++;

                    foreach (var param in section.Parameters)
                    {
                        configData[options.ExtractKeyFunc(section, param)] = options.ExtractValueFunc(section, param);
                        this.valueCount++;
                    }
                }

                logger.LogInformation($"Config Update for package {package.Path} finished");
            };
        });
  Configuration = builder.Build();
}

تحديثات التكوين

يدعم موفر تكوين نسيج الخدمة أيضًا تحديثات التكوين. يمكنك استخدام ASP.NET Core ⁧IOptionsMonitor⁩ لتلقي إعلامات التغيير، ثم الاستخدام ⁧IOptionsSnapshot⁩ لإعادة تحميل بيانات التكوين. لمزيد من المعلومات، راجع خيارات Core ⁧⁩ASP.NET⁧⁩.

يتم دعم هذه الخيارات افتراضيًا. ليست هناك حاجة إلى مزيد من الترميز لتمكين تحديثات التكوين.

السيناريوهات والتكوينات

يوفر هذا القسم مجموعة من خادم الويب وتكوين المنفذ وخيارات تكامل نسيج الخدمة والإعدادات المتنوعة التي نوصي بها لاستكشاف السيناريوهات التالية وإصلاحها:

  • ASP.NET الأساسية غير المتماثلة المعروضة خارجيًا
  • خدمات ASP.NET الأساسية غير المتماثلة الداخلية فقط
  • الخدمات الداخلية ASP.NET الأساسية فقط

الخدمة ⁧⁩المعروضة خارجيًا⁧⁩ هي الخدمة التي تعرض نقطة نهاية يتم استدعاؤها من خارج المجموعة، عادةً من خلال موازن التحميل.

الخدمة ⁧⁩الداخلية⁧⁩ فقط هي الخدمة التي تُستدعى نقطة نهايتها فقط من داخل المجموعة.

ملاحظة

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

ASP.NET الأساسية غير المتماثلة المعروضة خارجيًا

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

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

النوع التوصية ملاحظات
خادم الويب Kestrel Kestrel هو خادم الويب المفضل، حيث يُدعم عبر نظامي التشغيل Windows وLinux.
تكوين المنفذ ثابت يجب تكوين منفذ ثابت معروف في التكوين ⁧Endpoints⁩ لـ ServiceManifest.xml، مثل 80 لـ HTTP أو 443 لـ HTTPS.
خيارات تكامل نسيج الخدمة بلا استخدم الخيار ⁧ServiceFabricIntegrationOptions.None⁩ عند تكوين البرامج الوسيطة لتكامل نسيج الخدمة، بحيث لا تحاول الخدمة التحقق من صحة الطلبات الواردة لمعرف فريد. لن يعرف المستخدمون الخارجيون لتطبيقك معلومات التعريف الفريدة التي تستخدمها البرامج الوسيطة.
عدد المثيلات -1 في حالات الاستخدام النموذجية، يجب تعيين إنشاء عدد المثيلات إلى ⁧⁩-1⁧⁩. يتم ذلك بحيث يتوفر مثيل على جميع العقد التي تتلقى حركة المرور من موازن التحميل.

إذا كانت هناك عدة خدمات معروضة خارجيًا تشترك في نفس مجموعة العقد، فإنه يمكنك استخدام HTTP.sys مع مسار عنوان URL فريد ولكنه مستقر. يمكنك تحقيق ذلك عن طريق تعديل عنوان URL المقدم عند تكوين IWebHost. لاحظ أن هذا ينطبق على HTTP.sys فقط.

new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
{
    url += "/MyUniqueServicePath";

    return new WebHostBuilder()
        .UseHttpSys()
        ...
        .UseUrls(url)
        .Build();
})

الخدمة الأساسية ASP.NET الداخلية فقط غير المتماثلة

يجب أن تستخدم الخدمات غير المتماثلة التي تُستدعى فقط من داخل المجموعة عناوين URL فريدة ومنافذ معينة ديناميكيًا لضمان التعاون بين خدمات متعددة. نوصي بإجراء التكوين التالي:

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

الخدمة الأساسية ASP.NET الداخلية فقط

وينبغي أن تستخدم الخدمات الحكومية التي لا تُستدعى إلا من داخل المجموعة المخصصة ديناميكيًا لضمان التعاون بين خدمات متعددة. نوصي بإجراء التكوين التالي:

النوع التوصية ملاحظات
خادم الويب Kestrel لم يُصِمم ⁧HttpSysCommunicationListener⁩ للاستخدام من قبل الخدمات ذات الحالة التي تشترك فيها النسخ المتماثلة في عملية مضيف.
تكوين المنفذ تعيين ديناميكي قد تشترك النسخ المتماثلة المتعددة لخدمة ذات حالة في عملية مضيف أو نظام تشغيل مضيف، وبالتالي ستحتاج إلى منافذ فريدة.
خيارات تكامل نسيج الخدمة UseUniqueServiceUrl باستخدام تعيين المنفذ الديناميكي، يمنع هذا الإعداد مشكلة الهوية الخاطئة الموضحة سابقًا.

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

⁩ تصحيح أخطاء تطبيق نسيج الخدمة باستخدام Visual Studio⁧