ابدأ مع خدمات موثوقة في Java

تشرح هذه المقالة أساسيات الخدمات الموثوقة من Azure Service Fabric وترشدك خلال إنشاء وتوزيع تطبيق خدمة موثوقة بسيط مكتوب بلغة Java.

تحقق من هذه الصفحة للحصول على فيديو تدريبي يوضح لك كيفية إنشاء خدمة موثوقة عديمة الحالة:

التثبيت والإعداد

قبل أن تبدأ، تأكد من إعداد بيئة تطوير Service Fabric على جهازك. إذا كنت بحاجة إلى إعداده، فانتقل إلى البدء على نظام Mac أو بدء استخدام Linux.

المفاهيم الأساسية

لبدء استخدام الخدمات الموثوقة، ما عليك سوى فهم بعض المفاهيم الأساسية:

  • نوع الخدمة: هذا هو تنفيذ خدمتك. يتم تعريفه بالفئة التي تكتبها والتي تمتد StatelessService وأي كود أو تبعيات أخرى مستخدمة فيه، إلى جانب الاسم ورقم الإصدار.
  • مثيل الخدمة المُسمَّى: لتشغيل خدمتك، يمكنك إنشاء مثيلات مسماة لنوع خدمتك، تمامًا مثل إنشاء مثيلات كائن من نوع فئة. مثيلات الخدمة هي في الواقع عمليات إنشاء مثيل لفئة الخدمة التي تكتبها.
  • مضيف الخدمة: تحتاج مثيلات الخدمة المسماة التي تنشئها إلى التشغيل داخل مضيف. مضيف الخدمة هو مجرد عملية حيث يمكن تشغيل مثيلات خدمتك.
  • التسجيل في الخدمة: التسجيل يجمع كل شيء. يجب تسجيل نوع الخدمة مع وقت تشغيل Service Fabric في مضيف الخدمة للسماح لـ Service Fabric بإنشاء مثيلات لها ليتم تشغيلها.

إنشاء خدمة بدون حالة

ابدأ بإنشاء تطبيق Service Fabric. تتضمن Service Fabric SDK لنظام Linux مولد Yeoman لتوفير السقالات لتطبيق Service Fabric مع خدمة عديمة الحالة. ابدأ بتشغيل الأمر Yeoman التالي:

$ yo azuresfjava

اتبع التعليمات لإنشاء خدمة موثوقة لعديمي الجنسية. في هذا البرنامج التعليمي، قم بتسمية التطبيق "HelloWorldApplication" والخدمة "HelloWorld". تتضمن النتيجة دلائل لـ HelloWorldApplication وHelloWorld.

HelloWorldApplication/
├── build.gradle
├── HelloWorld
│   ├── build.gradle
│   └── src
│       └── statelessservice
│           ├── HelloWorldServiceHost.java
│           └── HelloWorldService.java
├── HelloWorldApplication
│   ├── ApplicationManifest.xml
│   └── HelloWorldPkg
│       ├── Code
│       │   ├── entryPoint.sh
│       │   └── _readme.txt
│       ├── Config
│       │   └── _readme.txt
│       ├── Data
│       │   └── _readme.txt
│       └── ServiceManifest.xml
├── install.sh
├── settings.gradle
└── uninstall.sh

تسجيل الخدمة

يجب تسجيل أنواع الخدمة في وقت تشغيل نسيج الخدمة. يتم تحديد نوع الخدمة في ServiceManifest.xml وفئة الخدمة التي تنفذ StatelessService. يتم تنفيذ تسجيل الخدمة في نقطة الدخول الرئيسية للعملية. في هذا المثال، نقطة إدخال العملية الرئيسية هي HelloWorldServiceHost.java:

public static void main(String[] args) throws Exception {
    try {
        ServiceRuntime.registerStatelessServiceAsync("HelloWorldType", (context) -> new HelloWorldService(), Duration.ofSeconds(10));
        logger.log(Level.INFO, "Registered stateless service type HelloWorldType.");
        Thread.sleep(Long.MAX_VALUE);
    }
    catch (Exception ex) {
        logger.log(Level.SEVERE, "Exception in registration:", ex);
        throw ex;
    }
}

تنفيذ الخدمة

افتح HelloWorldApplication/HelloWorld/src/statelessservice/HelloWorldService.java. تحدد هذه الفئة نوع الخدمة، ويمكنها تشغيل أي رمز. توفر واجهة برمجة تطبيقات الخدمة نقطتي إدخال لرمزك:

  • طريقة نقطة دخول مفتوحة، تسمى runAsync()، ومنها يمكنك البدء في تنفيذ أي أحمال عمل بما في ذلك أحمال عمل الحوسبة طويلة الأمد.
@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {
    ...
}
  • نقطة دخول اتصال يمكنك منها توصيل مكدس الاتصالات الذي تختاره. ومن هنا يمكنك البدء في تلقي الطلبات من المستخدمين والخدمات الأخرى.
@Override
protected List<ServiceInstanceListener> createServiceInstanceListeners() {
    ...
}

يركز هذا البرنامج التعليمي على أسلوب نقطة الدخول runAsync(). هذا هو المكان الذي يمكنك فيه بدء تشغيل التعليمات البرمجية الخاصة بك على الفور.

RunAsync

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

  • أن النظام يقوم بنقل مثيلات الخدمة الخاصة بك لتحقيق التوازن بين الموارد.
  • ومنها حودث أخطاء في التعليمة البرمجية الخاصة بك.
  • ترقية التطبيق أو النظام.
  • حدوث انقطاع في الأجهزة الأساسية.

تتم إدارة هذا التنسيق بواسطة Service Fabric للحفاظ على خدمتك متاحة للغاية ومتوازنة بشكل صحيح.

runAsync() ينبغي ألا تمنع بشكل متزامن. ينبغي أن يؤدي تطبيقك لـ runAsync إلى إرجاع CompletableFuture للسماح بوقت التشغيل بالمتابعة. إذا كان عبء العمل الخاص بك يحتاج إلى تنفيذ مهمة طويلة الأمد يجب القيام بها داخل كومبليتابل فيوتشر.

الإلغاء

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

@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {

    // TODO: Replace the following sample code with your own logic
    // or remove this runAsync override if it's not needed in your service.

    return CompletableFuture.runAsync(() -> {
        long iterations = 0;
        while(true)
        {
        cancellationToken.throwIfCancellationRequested();
        logger.log(Level.INFO, "Working-{0}", ++iterations);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex){}
        }
    });
}

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

إنشاء خدمة ذات حالة

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

لتحويل قيمة عداد من بدون حالة إلى حالة إتاحة عالية ومستمرة، حتى عند نقل الخدمة أو إعادة تشغيلها، تحتاج إلى خدمة ذات حالة.

في نفس الدليل مثل تطبيق HelloWorld، يمكنك إضافة خدمة جديدة عن طريق تشغيل الأمر yo azuresfjava:AddService. اختر "خدمة موثوقة الحالة" لإطار العمل الخاص بك وقم بتسمية الخدمة "HelloWorldStateful".

يجب أن يشتمل تطبيقك الآن على خدمتين: خدمة عديمي الجنسية HelloWorld وخدمة HelloWorldStateful ذات الحالة.

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

افتح HelloWorldStateful.java في HelloWorldStateful -> src، والتي تحتوي على طريقة RunAsync التالية:

@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {
    Transaction tx = stateManager.createTransaction();
    return this.stateManager.<String, Long>getOrAddReliableHashMapAsync("myHashMap").thenCompose((map) -> {
        return map.computeAsync(tx, "counter", (k, v) -> {
            if (v == null)
                return 1L;
            else
                return ++v;
            }, Duration.ofSeconds(4), cancellationToken)
                .thenCompose((r) -> tx.commitAsync())
                .whenComplete((r, e) -> {
            try {
                tx.close();
            } catch (Exception e) {
                logger.log(Level.SEVERE, e.getMessage());
            }
        });
    });
}

RunAsync

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

المجموعات الموثوقة ومدير الحالة الموثوق به

ReliableHashMap<String,Long> map = this.stateManager.<String, Long>getOrAddReliableHashMapAsync("myHashMap")

IReliableDictionary هو تطبيق قاموس يمكنك استخدامه لتخزين الحالة بشكل موثوق في الخدمة. باستخدام Service Fabric وHashMaps الموثوقة، يمكنك تخزين البيانات مباشرة في خدمتك دون الحاجة إلى مخزن خارجي ثابت. تجعل HashMaps الموثوقة بياناتك متاحة إلى حد كبير. يقوم Service Fabric بتنفيذ هذه الخطوة من خلال إنشاء وإدارة نسخ متماثلة متعددة من الخدمة الخاصة بك. كما يوفر واجهة برمجة تطبيقات تقوم بتبسيط تعقيدات إدارة تلك النسخ المتماثلة والتنقل بين حالاتها.

يمكن للمجموعات الموثوقة تخزين أي نوع Java، بما في ذلك الأنواع المخصصة الخاصة بك، مع بعض المحاذير:

  • يجعل نسيج الخدمة حالتك متاحة بشكل كبير عن طريق نسخ الحالة عبر العقد، ويقوم HashMap الموثوق بتخزين بياناتك على القرص المحلي في كل نسخة متماثلة. هذا يعني أن كل ما يتم تخزينه في HashMaps موثوقة يجب أن يكون قابلا للتسلسل.

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

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

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

المعاملات والعمليات غير المتزامنة

return map.computeAsync(tx, "counter", (k, v) -> {
    if (v == null)
        return 1L;
    else
        return ++v;
    }, Duration.ofSeconds(4), cancellationToken)
        .thenCompose((r) -> tx.commitAsync())
        .whenComplete((r, e) -> {
    try {
        tx.close();
    } catch (Exception e) {
        logger.log(Level.SEVERE, e.getMessage());
    }
});

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

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

إنشاء التطبيق

تشتمل سقالات Yeoman على برنامج نصي متدرج لإنشاء التطبيق والبرامج النصية bash لتوزيع التطبيق وإزالته. لتشغيل التطبيق، قم أولاً ببناء التطبيق باستخدام gradle:

$ gradle

ينتج عن ذلك حزمة تطبيق Service Fabric التي يمكن توزيعها باستخدام Service Fabric CLI.

نشر التطبيق

بمجرد إنشاء التطبيق، يمكنك توزيعه في الكتلة المحلية.

  1. قم بالاتصال بمجموعة نسيج الخدمة المحلية.

    sfctl cluster select --endpoint http://localhost:19080
    
  2. قم بتشغيل نص التثبيت المتوفر في القالب لنسخ حزمة التطبيق إلى مخزن صور المجموعة، وتسجيل نوع التطبيق، وإنشاء مثيل للتطبيق.

    ./install.sh
    

يعد توزيع التطبيق المدمج هو نفسه مثل أي تطبيق Service Fabric آخر. راجع الوثائق الخاصة بإدارة تطبيق Service Fabric مع Service Fabric CLI للحصول على إرشادات مفصلة.

يمكن العثور على معلمات هذه الأوامر في البيانات التي تم إنشاؤها داخل حزمة التطبيق.

بمجرد توزيع التطبيق، افتح متصفحًا وانتقل إلى Service Fabric Explorer على http://localhost:19080/Explorer. ثم قم بتوسيع عقدة التطبيقات ولاحظ أنه يوجد الآن إدخال لنوع التطبيق الخاص بك وآخر للمثيل الأول من هذا النوع.

هام

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

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