استخدم خدمة رسائل Java 1.1 API من خلال العضوية المميزة لناقل خدمة Azure القياسي وAMQP 1.0

التحذير

تُقدم هذه المقالة دعم محدود لواجهة برمجة التطبيقات لخدمة رسالة Java 1.1 (JMS)، وهي موجودة لمستوى ناقل خدمة Microsoft Azure فقط.

يتوفر الدعم الكامل لواجهة برمجة تطبيقات Java Message Service 2.0 فقط على المستوى المتميز ناقل خدمة Azure. نُوصي باستخدام هذا المستوى.

توضح هذه المقالة كيفية استخدام ميزات مراسلة ناقل خدمة Microsoft Azure من تطبيقات Java باستخدام معيار واجهة برمجة التطبيقات JMS الشائع. تتضمن ميزات المراسلة هذه قوائم الانتظار ونشر الموضوعات أو الاشتراك فيها. تشرح مقالة مصاحب كيفية القيام بنفس الشيء باستخدام واجهة برمجة تطبيقات Microsoft .NET لناقل خدمة Microsoft Azure. يمكنك استخدام هاتين المقالتين معًا لمعرفة المزيد حول المراسلة عبر النظام الأساسي باستخدام البروتوكول المُتقدم لوضع الرسائل في قائمة الانتظار (AMQP) 1.0.

AMQP 1.0 هو بروتوكول مراسلة فعال وموثوق به على مستوى سلكي يمكنك استخدامه لبناء تطبيقات مراسلة قوية عبر النظام الأساسي.

دعم AMQP 1.0 في ناقل خدمة Microsoft Azure يعني أنه يمكنك استخدام ميزات قائمة انتظار والنشر أو الاشتراك في المراسلة الوسيطة من مجموعة من الأنظمة الأساسية باستخدام بروتوكول ثنائي فعال. يمكنك أيضًا إنشاء تطبيقات تتكون من مكونات مُنشأة باستخدام مزيج من اللغات والأطر وأنظمة التشغيل.

بدء استخدام ناقل خدمة Microsoft Azure

تفترض هذه المقالة أن لديك بالفعل مساحة اسم ناقل خدمة Microsoft Azure تحتوي على قائمة انتظار باسم basicqueue. إذا لم تقم بذلك، يمكنك إنشاء مساحة الاسم وقائمة الانتظار باستخدام مدخل Microsoft Azure. لمزيد من المعلومات حول كيفية إنشاء مساحات أسماء ناقل خدمة Microsoft Azure وقوائم انتظار، راجع البدء في قوائم انتظار ناقل خدمة Microsoft Azure.

إشعار

قوائم الانتظار والمواضيع المُقسمة تدعم أيضًا AMQP. لمزيد من المعلومات، راجع كيانات المراسلة المُقسمة ودعم AMQP 1.0 لقوائم الانتظار والمواضيع المُقسمة لناقل خدمة Microsoft Azure.

تنزيل مكتبة عميل AMQP 1.0 JMS

للحصول على معلومات حول مكان تنزيل أحدث إصدار من مكتبة عميل Apache Qpid JMS AMQP 1.0، راجع موقع تنزي Apache Qpid.

يجب إضافة ملفات JAR التالية من أرشيف التوزيع Apache Qpid JMS AMQP 1.0 إلى متغير بيئة Java CLASSPATH عند إنشاء تطبيقات JMS مع ناقل خدمة Microsoft Azure وتشغيلها:

  • geronimo-jms_1.1_spec-1.0.jar
  • qpid-jms-client-[version].jar

إشعار

قد تكون أسماء JMS JAR وإصداراتها قد تغيرت. لمزيد من المعلومات، راجع Qpid JMS AMQP 1.0.

تطبيقات تعليمات Java البرمجية

تسمية Java وواجهة الدليل

يستخدم JMS تسمية Java وواجهة الدليل (JNDI) لإنشاء فصل بين الأسماء المنطقية والأسماء الفعلية. يتم حل نوعين من كائنات JMS باستخدام JNDI: ConnectionFactory، والوجهة. يستخدم JNDI نموذج مُوفر يمكنك من خلاله توصيل خدمات دليل مختلفة لمعالجة واجبات تحليل الاسم. مكتبة Apache Qpid JMS AMQP 1.0 تأتي مع موفر JNDI مستند إلى ملف خاصية بسيط تم تكوينه باستخدام ملف خصائص بالتنسيق التالي:

# servicebus.properties - sample JNDI configuration

# Register a ConnectionFactory in JNDI using the form:
# connectionfactory.[jndi_name] = [ConnectionURL]
connectionfactory.SBCF = amqps://[SASPolicyName]:[SASPolicyKey]@[namespace].servicebus.windows.net

# Register some queues in JNDI using the form
# queue.[jndi_name] = [physical_name]
# topic.[jndi_name] = [physical_name]
queue.QUEUE = queue1

إعداد سياق JNDI وتكوين كائن ConnectionFactory

سلسلة الاتصال المشار إليها هي تلك المتوفرة في نُهج الوصول المشترك في مدخل Microsoft Azure ضمن سلسلة الاتصال الأساسية.

// The connection string builder is the only part of the azure-servicebus SDK library
// we use in this JMS sample and for the purpose of robustly parsing the Service Bus 
// connection string. 
ConnectionStringBuilder csb = new ConnectionStringBuilder(connectionString);
        
// Set up JNDI context
Hashtable<String, String> hashtable = new Hashtable<>();
hashtable.put("connectionfactory.SBCF", "amqps://" + csb.getEndpoint().getHost() + "?amqp.idleTimeout=120000&amqp.traceFrames=true");
hashtable.put("queue.QUEUE", "BasicQueue");
hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
Context context = new InitialContext(hashtable);

ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");

// Look up queue
Destination queue = (Destination) context.lookup("QUEUE");

تكوين قوائم انتظار وجهة المُنتِج والمستهلك

الإدخال المُستخدم لتعريف وجهة موفر JNDI في ملف خصائص Qpid يكون بالتنسيق التالي.

لإنشاء قائمة انتظار وجهة للمُنتج:

String queueName = "queueName";
Destination queue = (Destination) queueName;

ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");
Connection connection - cf.createConnection(csb.getSasKeyName(), csb.getSasKey());

Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);

// Create producer
MessageProducer producer = session.createProducer(queue);

لإنشاء قائمة انتظار وجهة للمستهلك:

String queueName = "queueName";
Destination queue = (Destination) queueName;

ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");
Connection connection - cf.createConnection(csb.getSasKeyName(), csb.getSasKey());

Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);

// Create consumer
MessageConsumer consumer = session.createConsumer(queue);

كتابة تطبيق JMS

لا يلزم وجود واجهات برمجة تطبيقات أو خيارات خاصة عند استخدام JMS مع ناقل خدمة Microsoft Azure. هناك بعض القيود التي سيتم تغطيتها في وقت لاحق. كما هو الحال مع أي تطبيق JMS، فإن أول شيء مطلوب هو تكوين بيئة JNDI لتكون قادرة على حل كائن ConnectionFactory والوجهات.

تكوين كائن JNDI InitialContext

يتم تكوين بيئة JNDI بتمرير جدول تجزئة معلومات التكوين إلى الدالة الإنشائية لفئة javax.naming.InitialContext. العنصران المطلوبان في جدول التجزئة هما اسم الفئة لمصنع السياق الأولي وعنوان URL للموفر. التعليمات البرمجية التالية تُوضح كيفية تكوين بيئة JNDI لاستخدام موفر JNDI المستند إلى ملف خصائص Qpid مع ملف خصائص مُسمى servicebus.properties.

// Set up JNDI context
Hashtable<String, String> hashtable = new Hashtable<>();
hashtable.put("connectionfactory.SBCF", "amqps://" + csb.getEndpoint().getHost() + \
"?amqp.idleTimeout=120000&amqp.traceFrames=true");
hashtable.put("queue.QUEUE", "BasicQueue");
hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
Context context = new InitialContext(hashtable);

تطبيق JMS بسيط يستخدم قائمة انتظار ناقل خدمة Microsoft Azure

يرسل مثال البرنامج التالي رسائل نصية JMS إلى قائمة انتظار ناقل خدمة Microsoft Azure مع اسم منطقي JNDI لـ QUEUE ويتلقى الرسائل مرة أخرى.

يمكنك الوصول إلى كافة التعليمات البرمجية المصدر ومعلومات التكوين من تشغيل سريع لقائمة انتظار JMS لنماذج ناقل خدمة Microsoft Azure.

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package com.microsoft.azure.servicebus.samples.jmsqueuequickstart;

import com.azure.core.amqp.implementation.ConnectionStringProperties;
import org.apache.commons.cli.*;
import org.apache.log4j.*;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;

/**
 * This sample demonstrates how to send messages from a JMS queue producer into
 * an Azure Service Bus queue and receive them with a JMS message consumer.
 * JMS queue. 
 */
public class JmsQueueQuickstart {

    // Number of messages to send
    private static int totalSend = 10;
    //Tracking counter for how many messages have been received; used as termination condition
    private static AtomicInteger totalReceived = new AtomicInteger(0);
    // log4j logger 
    private static Logger logger = Logger.getRootLogger();

    public void run(String connectionString) throws Exception {

        // The connection string properties is the only part of the azure-servicebus SDK library
        // we use in this JMS sample and for the purpose of robustly parsing the Service Bus 
        // connection string. 
        ConnectionStringProperties csb = new ConnectionStringProperties(connectionString);
        
        // Set up JNDI context
        Hashtable<String, String> hashtable = new Hashtable<>();
        hashtable.put("connectionfactory.SBCF", "amqps://" + csb.getEndpoint().getHost() + "?amqp.idleTimeout=120000&amqp.traceFrames=true");
        hashtable.put("queue.QUEUE", "BasicQueue");
        hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
        Context context = new InitialContext(hashtable);
        ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");
        
        // Look up queue
        Destination queue = (Destination) context.lookup("QUEUE");

        // We create a scope here so we can use the same set of local variables cleanly 
        // again to show the receive side separately with minimal clutter.
        {
            // Create connection
            Connection connection = cf.createConnection(csb.getSharedAccessKeyName(), csb.getSharedAccessKey());
            // Create session, no transaction, client ack
            Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);

            // Create producer
            MessageProducer producer = session.createProducer(queue);

            // Send messages
            for (int i = 0; i < totalSend; i++) {
                BytesMessage message = session.createBytesMessage();
                message.writeBytes(String.valueOf(i).getBytes());
                producer.send(message);
                System.out.printf("Sent message %d.\n", i + 1);
            }

            producer.close();
            session.close();
            connection.stop();
            connection.close();
        }

        {
            // Create connection
            Connection connection = cf.createConnection(csb.getSharedAccessKeyName(), csb.getSharedAccessKey());
            connection.start();
            // Create session, no transaction, client ack
            Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
            // Create consumer
            MessageConsumer consumer = session.createConsumer(queue);
            // Create a listener callback to receive the messages
            consumer.setMessageListener(message -> {
                try {
                    // Received message is passed to callback
                    System.out.printf("Received message %d with sq#: %s\n",
                            totalReceived.incrementAndGet(), // increments the tracking counter
                            message.getJMSMessageID());
                    message.acknowledge();
                } catch (Exception e) {
                    logger.error(e);
                }
            });

            // Wait on the main thread until all sent messages have been received
            while (totalReceived.get() < totalSend) {
                Thread.sleep(1000);
            }
            consumer.close();
            session.close();
            connection.stop();
            connection.close();
        }

        System.out.printf("Received all messages, exiting the sample.\n");
        System.out.printf("Closing queue client.\n");
    }

    public static void main(String[] args) {

        System.exit(runApp(args, (connectionString) -> {
            JmsQueueQuickstart app = new JmsQueueQuickstart();
            try {
                app.run(connectionString);
                return 0;
            } catch (Exception e) {
                System.out.printf("%s", e.toString());
                return 1;
            }
        }));
    }

    static final String SB_SAMPLES_CONNECTIONSTRING = "SB_SAMPLES_CONNECTIONSTRING";

    public static int runApp(String[] args, Function<String, Integer> run) {
        try {

            String connectionString = null;

            // Parse connection string from command line
            Options options = new Options();
            options.addOption(new Option("c", true, "Connection string"));
            CommandLineParser clp = new DefaultParser();
            CommandLine cl = clp.parse(options, args);
            if (cl.getOptionValue("c") != null) {
                connectionString = cl.getOptionValue("c");
            }

            // Get overrides from the environment
            String env = System.getenv(SB_SAMPLES_CONNECTIONSTRING);
            if (env != null) {
                connectionString = env;
            }

            if (connectionString == null) {
                HelpFormatter formatter = new HelpFormatter();
                formatter.printHelp("run jar with", "", options, "", true);
                return 2;
            }
            return run.apply(connectionString);
        } catch (Exception e) {
            System.out.printf("%s", e.toString());
            return 3;
        }
    }
}

شغّل التطبيق

مرر سلسلة الاتصال من نُهج الوصول المشترك لتشغيل التطبيق. الناتج التالي هو النموذج الذي يُشغل التطبيق:

> mvn clean package
>java -jar ./target/jmsqueuequickstart-1.0.0-jar-with-dependencies.jar -c "<CONNECTION_STRING>"

Sent message 1.
Sent message 2.
Sent message 3.
Sent message 4.
Sent message 5.
Sent message 6.
Sent message 7.
Sent message 8.
Sent message 9.
Sent message 10.
Received message 1 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-1
Received message 2 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-2
Received message 3 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-3
Received message 4 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-4
Received message 5 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-5
Received message 6 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-6
Received message 7 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-7
Received message 8 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-8
Received message 9 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-9
Received message 10 with sq#: ID:7f6a7659-bcdf-4af6-afc1-4011e2ddcb3c:1:1:1-10
Received all messages, exiting the sample.
Closing queue client.

تعيين ترتيب AMQP وعملية ناقل خدمة Microsoft Azure

إليك كيفية ترجمة ترتيب AMQP إلى عملية ناقل خدمة Microsoft Azure:

ACCEPTED = 1; -> Complete()
REJECTED = 2; -> DeadLetter()
RELEASED = 3; (just unlock the message in service bus, will then get redelivered)
MODIFIED_FAILED = 4; -> Abandon() which increases delivery count
MODIFIED_FAILED_UNDELIVERABLE = 5; -> Defer()

مواضيع JMS مقابل مواضيع ناقل خدمة Microsoft Azure

استخدام مواضيع ناقل خدمة Microsoft Azure والاشتراكات من خلال واجهة برمجة تطبيقات JMS يُوفر قدرات إرسال واستلام أساسية. إنه خيار مناسب عند نقل التطبيقات من وسطاء الرسائل الأخرين مع واجهات برمجة التطبيقات المتوافقة مع JMS، على الرغم من أن مواضيع ناقل خدمة Microsoft Azure تختلف عن مواضيع JMS وتتطلب بعض التعديلات.

تُوجه مواضيع ناقل خدمة Microsoft Azure الرسائل إلى الاشتراكات المسماة والمشتركة والدائمة التي تُدار من خلال واجهة إدارة موارد Azure أو أدوات سطر الأوامر Azure أو مدخل Microsoft Azure. يسمح كل اشتراك بما يصل إلى 2000 قاعدة اختيار، قد يكون لكل منها شرط تصفية، وبالنسبة لفلاتر SQL، وكذلك إجراء تحويل بيانات التعريف. مُطابقة كل شرط عامل تصفية تُحدد رسالة الإدخال المراد نسخها إلى الاشتراك.

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

تتيح مواضيع JMS للعملاء إنشاء مشتركين دائمين ومشتركين غير دائمين بشكل ديناميكي يسمحون بتصفية الرسائل باستخدام محددات الرسائل اختياريًا. هذه الكيانات غير المشتركة لا يدعمها ناقل خدمة Microsoft Azure. بناء جملة قاعدة عامل تصفية SQL لناقل خدمة Microsoft Azure مُشابه لبناء جملة محدد الرسالة الذي يدعمه JMS.

يتوافق جانب الناشر لموضوع JMS مع ناقل خدمة Microsoft Azure، كما هو موضح في هذه العينة، ولكن المشتركين الديناميكيين ليسوا كذلك. واجهات برمجة التطبيقات JMS المُتعلقة بالطبولوجيا التالية غير مدعومة بناقل خدمة Microsoft Azure.

الميزات والقيود غير المدعومة

تُوجد القيود التالية عند استخدام JMS عبر AMQP 1.0 مع ناقل خدمة Microsoft Azure، ألا وهي:

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

ناقل خدمة Microsoft Azure يُقسم وحدة التحكم عن وحدة البيانات، بحيث لا يدعم العديد من وظائف الطبولوجيا الحيوية لـ JMS.

أسلوب غير مدعوم استبدال بـ
إنشاء مشترك دائم إنشاء اشتراك موضوع ينقل مُحدد الرسالة.
إنشاء مستهلك دائم إنشاء اشتراك موضوع ينقل مُحدد الرسالة.
إنشاء مستهلك مشترك مواضيع ناقل خدمة Microsoft Azure قابلة للمشاركة دائمًا. راجع المقطع "مواضيع JMS مقابل مواضيع ناقل خدمة Microsoft Azure."
إنشاء مستهلك دائم مشترك مواضيع ناقل خدمة Microsoft Azure قابلة للمشاركة دائمًا. راجع المقطع "مواضيع JMS مقابل مواضيع ناقل خدمة Microsoft Azure."
إنشاء موضوع مؤقت إنشاء موضوع عبر واجهة برمجة تطبيقات الإدارة أو الأدوات أو المدخل مع تعيين AutoDeleteOnIdle إلى فترة انتهاء صلاحية.
إنشاء موضوع إنشاء موضوع عبر واجهة برمجة تطبيقات الإدارة أو الأدوات أو المدخل.
إلغاء الاشتراك حذف واجهة برمجة تطبيقات إدارة الموضوعات أو الأدوات أو المدخل.
إنشاء مستعرض غير مدعوم. استخدام وظيفة التحرير بسرعة () لواجهة برمجة التطبيقات (API) لناقل خدمة Microsoft Azure.
إنشاء قائمة الانتظار إنشاء قائمة انتظار عبر واجهة برمجة تطبيقات الإدارة أو الأدوات أو المدخل.
إنشاء قائمة انتظار مؤقتع إنشاء قائمة انتظار عبر واجهة برمجة تطبيقات الإدارة أو الأدوات أو المدخل مع تعيين AutoDeleteOnIdle إلى فترة انتهاء صلاحية.
لا للانتظار استخدم الأسلوب استلام() الذي تُوفره عدة تطوير البرامج (SDK) لناقل خدمة Microsoft Azure، وحدد مهلة منخفضة جدًا أو اجعلها صفر.

الملخص

هذه المقالة أظهرت لك كيفية استخدام ميزات المراسلة الوسيطة لناقل خدمة Microsoft Azure، مثل قوائم الانتظار ونشر المواضيع والاشتراك فيها، من Java باستخدام واجهة برمجة التطبيقات JMS شعبية وAMQP 1.0.

يمكنك أيضًا استخدام AMQP 1.0 لناقل خدمة Microsoft Azure من لغات أخرى، مثل Microsoft .NET, C, Python, PHP. المكونات التي تم إنشاؤها باستخدام هذه اللغات المختلفة يُمكنها تبادل الرسائل بشكل موثوق وبدقة كاملة باستخدام دعم AMQP 1.0 في ناقل خدمة Microsoft Azure.

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