استكشاف المشكلات وإصلاحها عند استخدام Azure Cosmos DB Async Java SDK v2 مع واجهة برمجة التطبيقات لحسابات NoSQL

ينطبق على: NoSQL

هام

هذا ليس أحدث Java SDK لـ Azure Cosmos DB! يجب ترقية مشروعك إلى Azure Cosmos DB Java SDK v4 ثم قراءة Azure Cosmos DB Java SDK v4 دليل استكشاف الأخطاء وإصلاحها. اتبع الإرشادات الواردة في دليل الترحيل إلى Azure Cosmos DB Java SDK v4 ودليل Reactor vs RxJava للترقية.

تتناول هذه المقالة استكشاف الأخطاء وإصلاحها لـ Azure Cosmos DB Async Java SDK v2 فقط. راجع Azure Cosmos DB Async Java SDK v2 ملاحظات الإصدارو مستودع Maven ونصائح الأداء لمزيد من المعلومات.

هام

في 31 أغسطس 2024، سيتم إيقاف إصدار Azure Cosmos DB Async Java SDK v2.x ؛ SDK وجميع التطبيقات التي تستخدم SDK ستستمر في العمل؛ سيتوقف Azure Cosmos DB ببساطة عن توفير مزيد من الصيانة والدعم لحزمة SDK هذه. نوصي باتباع الإرشادات المذكورة أعلاه للترحيل إلى Azure Cosmos DB Java SDK v4.

تتناول هذه المقالة المشكلات الشائعة والحلول وخطوات التشخيص والأدوات عند استخدام Java Async SDK مع Azure Cosmos DB لحسابات NoSQL. يوفر Java Async SDK تمثيلا منطقيا من جانب العميل للوصول إلى Azure Cosmos DB ل NoSQL. توضح هذه المقالة الأدوات والأساليب التي تساعدك في حالة مواجهة أي مشكلات.

ابدأ بهذه القائمة:

المشكلات الشائعة والحلول

مشكلات الشبكة، فشل مهلة قراءة Netty، الإنتاجية المنخفضة، زمن الوصول العالي

اقتراحات عامة

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

تقييد الاتصال

يمكن أن يحدث تقييد الاتصال إما بسبب حد الاتصال على الجهاز المضيف أو استنفاد منفذ Azure SNAT (PAT).

حد الاتصال في جهاز مضيف

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

ulimit -a

يجب أن يكون عدد الملفات المفتوحة القصوى المسموح بها، والتي تم تحديدها على أنها "nofile"، ضعف حجم تجمع الاتصال على الأقل. لمزيد من المعلومات، راجع نصائح الأداء.

استنفاد منفذ Azure SNAT (PAT)

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

تُستخدم منافذ Azure SNAT فقط عندما يكون للجهاز الظاهري عنوان IP خاص وتحاول عملية من الجهاز الظاهري الاتصال بعنوان IP عام. هناك نوعان من الحلول لتجنب قيود Azure SNAT:

  • أضف نقطة نهاية خدمة Azure Cosmos DB إلى الشبكة الفرعية لشبكة Azure Virtual Machines الخاصة بك. لمزيد من المعلومات، راجع نقاط نهاية خدمة الشبكة الظاهرية Azure.

    عند تمكين نقطة نهاية الخدمة، لن يتم إرسال الطلبات من عنوان IP عام إلى Azure Cosmos DB. بدلاً من ذلك، يتم إرسال هوية الشبكة الافتراضية والشبكة الفرعية. قد يؤدي هذا التغيير إلى سقوط جدار الحماية إذا تم السماح بعناوين IP العامة فقط. إذا كنت تستخدم جدار حماية، فعند تمكين نقطة نهاية الخدمة، أضف شبكة فرعية إلى جدار الحماية باستخدام Virtual Network ACLs.

  • قم بتعيين IP عام لجهاز Azure VM الخاص بك.

لا يمكن الوصول إلى الخدمة - جدار الحماية

يشيرConnectTimeoutException إلى أن SDK لا يمكنها الوصول إلى الخدمة. قد تحصل على فشل مشابه لما يلي عند استخدام الوضع المباشر:

GoneException{error=null, resourceAddress='https://cdb-ms-prod-westus-fd4.documents.azure.com:14940/apps/e41242a5-2d71-5acb-2e00-5e5f744b12de/services/d8aa21a5-340b-21d4-b1a2-4a5333e7ed8a/partitions/ed028254-b613-4c2a-bf3c-14bd5eb64500/replicas/131298754052060051p//', statusCode=410, message=Message: The requested resource is no longer available at the server., getCauseInfo=[class: class io.netty.channel.ConnectTimeoutException, message: connection timed out: cdb-ms-prod-westus-fd4.documents.azure.com/101.13.12.5:14940]

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

وكيل HTTP

إذا كنت تستخدم وكيل HTTP، فتأكد من أنه يمكنه دعم عدد الاتصالات التي تم تكوينها في SDK ConnectionPolicy. خلاف ذلك، ستواجه مشكلات في الاتصال.

نمط الترميز غير صالح: حظر مؤشر ترابط Netty IO

تستخدم SDK مكتبة Netty IO للتواصل مع Azure Cosmos DB. يحتوي SDK على واجهات برمجة تطبيقات Async ويستخدم واجهات برمجة تطبيقات IO غير المحظورة لـ Netty. يتم تنفيذ عمل IO الخاص بـ SDK على خيوط IO Netty. تم تكوين عدد خيوط IO Netty ليكون هو نفسه عدد أنوية وحدة المعالجة المركزية لجهاز التطبيق.

من المفترض أن يتم استخدام مؤشرات ترابط Netty IO فقط لأعمال Netty IO غير المحظورة. تعرض SDK نتيجة استدعاء واجهة برمجة التطبيقات في إحدى سلاسل عمليات Netty IO إلى رمز التطبيق. إذا أجرى التطبيق عملية طويلة الأمد بعد أن يتلقى النتائج على مؤشر ترابط Netty، فقد لا يحتوي SDK على سلاسل عمليات إدخال / إخراج كافية لأداء عمل IO الداخلي. قد يؤدي ترميز التطبيق هذا إلى معدل نقل منخفض وزمن انتقال مرتفع وإخفاقات io.netty.handler.timeout.ReadTimeoutException. الحل هو تبديل مؤشر الترابط عندما تعلم أن العملية تستغرق وقتاً.

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

Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)

@Test
public void badCodeWithReadTimeoutException() throws Exception {
    int requestTimeoutInSeconds = 10;

    ConnectionPolicy policy = new ConnectionPolicy();
    policy.setRequestTimeoutInMillis(requestTimeoutInSeconds * 1000);

    AsyncDocumentClient testClient = new AsyncDocumentClient.Builder()
            .withServiceEndpoint(TestConfigurations.HOST)
            .withMasterKeyOrResourceToken(TestConfigurations.MASTER_KEY)
            .withConnectionPolicy(policy)
            .build();

    int numberOfCpuCores = Runtime.getRuntime().availableProcessors();
    int numberOfConcurrentWork = numberOfCpuCores + 1;
    CountDownLatch latch = new CountDownLatch(numberOfConcurrentWork);
    AtomicInteger failureCount = new AtomicInteger();

    for (int i = 0; i < numberOfConcurrentWork; i++) {
        Document docDefinition = getDocumentDefinition();
        Observable<ResourceResponse<Document>> createObservable = testClient
                .createDocument(getCollectionLink(), docDefinition, null, false);
        createObservable.subscribe(r -> {
                    try {
                        // Time-consuming work is, for example,
                        // writing to a file, computationally heavy work, or just sleep.
                        // Basically, it's anything that takes more than a few milliseconds.
                        // Doing such operations on the IO Netty thread
                        // without a proper scheduler will cause problems.
                        // The subscriber will get a ReadTimeoutException failure.
                        TimeUnit.SECONDS.sleep(2 * requestTimeoutInSeconds);
                    } catch (Exception e) {
                    }
                },

                exception -> {
                    //It will be io.netty.handler.timeout.ReadTimeoutException.
                    exception.printStackTrace();
                    failureCount.incrementAndGet();
                    latch.countDown();
                },
                () -> {
                    latch.countDown();
                });
    }

    latch.await();
    assertThat(failureCount.get()).isGreaterThan(0);
}

الحل هو تغيير مؤشر الترابط الذي تقوم بتنفيذ العمل الذي يستغرق وقتاً. حدد نسخة مفردة من برنامج الجدولة لتطبيقك.

Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)

// Have a singleton instance of an executor and a scheduler.
ExecutorService ex  = Executors.newFixedThreadPool(30);
Scheduler customScheduler = rx.schedulers.Schedulers.from(ex);

قد تحتاج إلى القيام بعمل يستغرق وقتاً، على سبيل المثال، عمل ثقيل حسابياً أو حظر الإدخال / الإخراج. في هذه الحالة، قم بتبديل مؤشر الترابط إلى عامل تم توفيره بواسطة customScheduler باستخدام واجهة برمجة التطبيقات .observeOn(customScheduler).

Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)

Observable<ResourceResponse<Document>> createObservable = client
        .createDocument(getCollectionLink(), docDefinition, null, false);

createObservable
        .observeOn(customScheduler) // Switches the thread.
        .subscribe(
            // ...
        );

باستخدام observeOn(customScheduler)، تقوم بتحرير مؤشر ترابط Netty IO والتبديل إلى سلسلة المحادثات المخصصة الخاصة بك والتي يوفرها المجدول المخصص. هذا التعديل يحل المشكلة. لن تحصل على إخفاق io.netty.handler.timeout.ReadTimeoutException بعد الآن.

استنفدت مشكلة تجمع الاتصال

يعدPoolExhaustedException إخفاقاً من جانب العميل. يشير هذا الفشل إلى أن عبء عمل التطبيق الخاص بك أعلى مما يمكن أن يقدمه تجمع اتصالات SDK. قم بزيادة حجم مجموعة الاتصال أو توزيع الحمل على تطبيقات متعددة.

معدل الطلب كبير جداً

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

فشل الاتصال بمحاكي Azure Cosmos DB

شهادة HTTPS لمحاكي Azure Cosmos DB هي شهادة موقعة ذاتياً. لكي تعمل SDK مع المحاكي، قم باستيراد شهادة المحاكي إلى Java TrustStore. لمزيد من المعلومات، راجع تصدير شهادات Azure Cosmos DB Emulator.

مشكلات تعارضات التبعية

Exception in thread "main" java.lang.NoSuchMethodError: rx.Observable.toSingle()Lrx/Single;

يشير الاستثناء أعلاه إلى أنك تعتمد على إصدار أقدم من RxJava lib (على سبيل المثال، 1.2.2). يعتمد SDK الخاص بنا على RxJava 1.3.8 الذي يحتوي على واجهات برمجة تطبيقات غير متوفرة في الإصدار السابق من RxJava.

الحل البديل لمثل هذه المشكلات هو تحديد التبعية الأخرى التي تجلب RxJava-1.2.2 واستبعاد التبعية المتعدية على RxJava-1.2.2، والسماح لـ CosmosDB SDK بإحضار الإصدار الأحدث.

لتحديد المكتبة التي تجلب RxJava-1.2.2، قم بتشغيل الأمر التالي بجوار ملف pom.xml الخاص بالمشروع:

mvn dependency:tree

لمزيد من المعلومات، راجع دليل شجرة التبعية المخضرم.

بمجرد تحديد أن RxJava-1.2.2 هي تبعية انتقالية تبعية أخرى لمشروعك، يمكنك تعديل التبعية على هذا lib في ملف pom واستبعاد التبعية العابرة لـ RxJava:

<dependency>
  <groupId>${groupid-of-lib-which-brings-in-rxjava1.2.2}</groupId>
  <artifactId>${artifactId-of-lib-which-brings-in-rxjava1.2.2}</artifactId>
  <version>${version-of-lib-which-brings-in-rxjava1.2.2}</version>
  <exclusions>
    <exclusion>
      <groupId>io.reactivex</groupId>
      <artifactId>rxjava</artifactId>
    </exclusion>
  </exclusions>
</dependency>

لمزيد من المعلومات، راجع دليل استبعاد التبعية المتعدية.

تمكين تسجيل عميل SDK

يستخدم Java Async SDK SLF4j كواجهة تسجيل تدعم تسجيل الدخول إلى أطر تسجيل شائعة مثل log4j وlogback.

على سبيل المثال، إذا كنت تريد استخدام log4j كإطار عمل للتسجيل، فقم بإضافة libs التالية في مسار فئة Java الخاص بك.

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>${slf4j.version}</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>${log4j.version}</version>
</dependency>

أضف أيضاً ملف التكوين log4j.

# this is a sample log4j configuration

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=INFO, A1

log4j.category.com.microsoft.azure.cosmosdb=DEBUG
#log4j.category.io.netty=INFO
#log4j.category.io.reactivex=INFO
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %5X{pid} [%t] %-5p %c - %m%n

لمزيد من المعلومات، راجع دليل تسجيل sfl4j.

إحصائيات شبكة نظام التشغيل

قم بتشغيل الأمر netstat للتعرف على عدد الاتصالات الموجودة في حالات مثل ESTABLISHED وCLOSE_WAIT.

في نظام Linux، يمكنك تشغيل الأمر التالي.

netstat -nap

قم بتصفية النتيجة إلى الاتصالات بنقطة نهاية Azure Cosmos DB فقط.

لا يمكن أن يكون عدد الاتصالات بنقطة نهاية Azure Cosmos DB في الحالة ESTABLISHED أكبر من حجم تجمع الاتصال الذي تم تكوينه.

قد تكون العديد من الاتصالات بنقطة نهاية Azure Cosmos DB في الحالة CLOSE_WAIT. قد يكون هناك أكثر من 1000. يشير الرقم المرتفع إلى أنه يتم إنشاء الاتصالات وقطعها بسرعة. هذا الموقف يحتمل أن يسبب مشاكل. لمزيد من المعلومات، راجع قسم المشكلات الشائعة والحلول.