تصدير واستيراد تسجيلات مراكز إعلام Azure بشكل مجمع

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

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

تدفق عالي المستوى

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

استيراد

الإعداد

يفترض هذا القسم أن لديك الكيانات التالية:

إنشاء ملف إدخال وتخزينه في نقطة

يحتوي ملف الإدخال على قائمة بالتسجيلات المسلسلة في XML، واحدة لكل صف. باستخدام Azure SDK، يوضح مثال التعليمات البرمجية التالي كيفية تسلسل التسجيلات وتحميلها إلى حاوية blob:

private static void SerializeToBlob(CloudBlobContainer container, RegistrationDescription[] descriptions)
{
    StringBuilder builder = new StringBuilder();
    foreach (var registrationDescription in descriptions)
    {
        builder.AppendLine(registrationDescription.Serialize());
    }

    var inputBlob = container.GetBlockBlobReference(INPUT_FILE_NAME);
    using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(builder.ToString())))
    {
        inputBlob.UploadFromStream(stream);
    }
}

هام

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

إنشاء رموز URL المميزة

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

static Uri GetOutputDirectoryUrl(CloudBlobContainer container)
{
    SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy
    {
        SharedAccessExpiryTime = DateTime.UtcNow.AddDays(1),
        Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.List | SharedAccessBlobPermissions.Read
    };

    string sasContainerToken = container.GetSharedAccessSignature(sasConstraints);
    return new Uri(container.Uri + sasContainerToken);
}

static Uri GetInputFileUrl(CloudBlobContainer container, string filePath)
{
    SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy
    {
        SharedAccessExpiryTime = DateTime.UtcNow.AddDays(1),
        Permissions = SharedAccessBlobPermissions.Read
    };
    string sasToken = container.GetBlockBlobReference(filePath).GetSharedAccessSignature(sasConstraints);
    return new Uri(container.Uri + "/" + filePath + sasToken);
}

إرسال المهمة

باستخدام عنواني URL للإدخال والإخراج، يمكنك الآن بدء المهمة الدفعية.

NotificationHubClient client = NotificationHubClient.CreateClientFromConnectionString(CONNECTION_STRING, HUB_NAME);
var createTask = client.SubmitNotificationHubJobAsync(
new NotificationHubJob {
        JobType = NotificationHubJobType.ImportCreateRegistrations,
        OutputContainerUri = outputContainerSasUri,
        ImportFileUri = inputFileSasUri
    }
);
createTask.Wait();

var job = createTask.Result;
long i = 10;
while (i > 0 && job.Status != NotificationHubJobStatus.Completed)
{
    var getJobTask = client.GetNotificationHubJobAsync(job.JobId);
    getJobTask.Wait();
    job = getJobTask.Result;
    Thread.Sleep(1000);
    i--;
}

بالإضافة إلى عناوين URL للإدخال والإخراج، ينشئ NotificationHubJob هذا المثال كائنا يحتوي على JobType كائن، والذي يمكن أن يكون أحد الأنواع التالية:

  • ImportCreateRegistrations
  • ImportUpdateRegistrations
  • ImportDeleteRegistrations

بمجرد اكتمال المكالمة، تستمر المهمة بواسطة مركز الإشعارات، ويمكنك التحقق من حالتها باستخدام المكالمة إلى GetNotificationHubJobAsync.

عند الانتهاء من المهمة ، يمكنك فحص النتائج من خلال النظر إلى الملفات التالية في دليل الإخراج الخاص بك:

  • /<hub>/<jobid>/Failed.txt
  • /<hub>/<jobid>/Output.txt

تحتوي هذه الملفات على قائمة العمليات الناجحة والفاشلة من الدفعة. تنسيق الملف هو .cvs، حيث يحتوي كل صف على رقم سطر ملف الإدخال الأصلي ، وإخراج العملية (عادة ما يكون وصف التسجيل الذي تم إنشاؤه أو تحديثه).

نموذج التعليمات البرمجية الكاملة

يقوم نموذج التعليمات البرمجية التالي باستيراد التسجيلات إلى مركز إعلام.

using Microsoft.Azure.NotificationHubs;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;

namespace ConsoleApplication1
{
    class Program
    {
        private static string CONNECTION_STRING = "---";
        private static string HUB_NAME = "---";
        private static string INPUT_FILE_NAME = "CreateFile.txt";
        private static string STORAGE_ACCOUNT = "---";
        private static string STORAGE_PASSWORD = "---";
        private static StorageUri STORAGE_ENDPOINT = new StorageUri(new Uri("---"));

        static void Main(string[] args)
        {
            var descriptions = new[]
            {
                new MpnsRegistrationDescription(@"http://dm2.notify.live.net/throttledthirdparty/01.00/12G9Ed13dLb5RbCii5fWzpFpAgAAAAADAQAAAAQUZm52OkJCMjg1QTg1QkZDMkUxREQFBlVTTkMwMQ"),
                new MpnsRegistrationDescription(@"http://dm2.notify.live.net/throttledthirdparty/01.00/12G9Ed13dLb5RbCii5fWzpFpAgAAAAADAQAAAAQUZm52OkJCMjg1QTg1QkZDMjUxREQFBlVTTkMwMQ"),
                new MpnsRegistrationDescription(@"http://dm2.notify.live.net/throttledthirdparty/01.00/12G9Ed13dLb5RbCii5fWzpFpAgAAAAADAQAAAAQUZm52OkJCMjg1QTg1QkZDMhUxREQFBlVTTkMwMQ"),
                new MpnsRegistrationDescription(@"http://dm2.notify.live.net/throttledthirdparty/01.00/12G9Ed13dLb5RbCii5fWzpFpAgAAAAADAQAAAAQUZm52OkJCMjg1QTg1QkZDMdUxREQFBlVTTkMwMQ"),
            };

            // Write to blob store to create an input file
            var blobClient = new CloudBlobClient(STORAGE_ENDPOINT, new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(STORAGE_ACCOUNT, STORAGE_PASSWORD));
            var container = blobClient.GetContainerReference("testjobs");
            container.CreateIfNotExists();

            SerializeToBlob(container, descriptions);

            // TODO then create Sas
            var outputContainerSasUri = GetOutputDirectoryUrl(container);
            var inputFileSasUri = GetInputFileUrl(container, INPUT_FILE_NAME);


            // Import this file
            NotificationHubClient client = NotificationHubClient.CreateClientFromConnectionString(CONNECTION_STRING, HUB_NAME);
            var createTask = client.SubmitNotificationHubJobAsync(
                new NotificationHubJob {
                    JobType = NotificationHubJobType.ImportCreateRegistrations,
                    OutputContainerUri = outputContainerSasUri,
                    ImportFileUri = inputFileSasUri
                }
            );
            createTask.Wait();

            var job = createTask.Result;
            long i = 10;
            while (i > 0 && job.Status != NotificationHubJobStatus.Completed)
            {
                var getJobTask = client.GetNotificationHubJobAsync(job.JobId);
                getJobTask.Wait();
                job = getJobTask.Result;
                Thread.Sleep(1000);
                i--;
            }
        }

        private static void SerializeToBlob(CloudBlobContainer container, RegistrationDescription[] descriptions)
        {
            StringBuilder builder = new StringBuilder();
            foreach (var registrationDescription in descriptions)
            {
                builder.AppendLine(registrationDescription.Serialize());
            }

            var inputBlob = container.GetBlockBlobReference(INPUT_FILE_NAME);
            using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(builder.ToString())))
            {
                inputBlob.UploadFromStream(stream);
            }
        }

        static Uri GetOutputDirectoryUrl(CloudBlobContainer container)
        {
            // Set the expiry time and permissions for the container.
            // In this case no start time is specified, so the shared access signature becomes valid immediately.
            SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy
            {
                SharedAccessExpiryTime = DateTime.UtcNow.AddHours(4),
                Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.List | SharedAccessBlobPermissions.Read
            };

            // Generate the shared access signature on the container, setting the constraints directly on the signature.
            string sasContainerToken = container.GetSharedAccessSignature(sasConstraints);

            // Return the URI string for the container, including the SAS token.
            return new Uri(container.Uri + sasContainerToken);
        }

        static Uri GetInputFileUrl(CloudBlobContainer container, string filePath)
        {
            // Set the expiry time and permissions for the container.
            // In this case no start time is specified, so the shared access signature becomes valid immediately.
            SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy
            {
                SharedAccessExpiryTime = DateTime.UtcNow.AddHours(4),
                Permissions = SharedAccessBlobPermissions.Read
            };

            // Generate the shared access signature on the container, setting the constraints directly on the signature.
            string sasToken = container.GetBlockBlobReference(filePath).GetSharedAccessSignature(sasConstraints);

            // Return the URI string for the container, including the SAS token.
            return new Uri(container.Uri + "/" + filePath + sasToken);
        }

        static string GetJobPath(string namespaceName, string notificationHubPath, string jobId)
        {
            return string.Format(CultureInfo.InvariantCulture, @"{0}//{1}/{2}/", namespaceName, notificationHubPath, jobId);
        }
    }
}

تصدير

تسجيل التصدير مشابه للاستيراد ، مع الاختلافات التالية:

  • تحتاج فقط إلى عنوان URL للإخراج.
  • إنشاء إعلامHubJob من نوع ExportRegistrations.

نموذج مقتطف التعليمات البرمجية

فيما يلي مقتطف نموذجي للتعليمات البرمجية لتصدير التسجيلات في Java:

// Submit an export job
NotificationHubJob job = new NotificationHubJob();
job.setJobType(NotificationHubJobType.ExportRegistrations);
job.setOutputContainerUri("container uri with SAS signature");
job = hub.submitNotificationHubJob(job);

// Wait until the job is done
while(true){
    Thread.sleep(1000);
    job = hub.getNotificationHubJob(job.getJobId());
    if(job.getJobStatus() == NotificationHubJobStatus.Completed)
        break;
}

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

لمعرفة المزيد حول التسجيلات، راجع المقالات التالية: