Freigeben über


Vorgehensweise: Exportieren und Ändern von Registrierungen in Massen

In einigen Szenarios ist das Erstellen oder Anpassen großer Mengen von Registrierungen in einem Notification Hub erforderlich. Bei einigen dieser Szenarios handelt es sich um Tagaktualisierungen, die auf Batchberechnungen folgen, oder Migrationen vorhandener Pushimplementierungen zur Verwendung von Notification Hubs.

Dieses Thema erläutert die Verwendung der Massenunterstützung von Benachrichtigungshubs zum Durchführen vieler Vorgänge auf einem Benachrichtigungshub oder zum Exportieren aller Registrierungen.

Allgemeiner Ablauf

Die Batchunterstützung wurde dazu entworfen, Aufträge mit langer Ausführungszeit mit Millionen von Registrierungen zu unterstützen. Um diese Skalierung zu erreichen, verwendet die Batchverarbeitungsunterstützung Azure Storage, um Auftragsdetails und -ausgabe zu speichern. Für Massenaktualisierungsvorgänge muss der Benutzer eine Datei in einem Blobcontainer erstellen, die eine Liste der Registrierungaktualisierungsvorgänge enthält. Wenn der Auftrag gestartet wird, stellt der Benutzer die jeweiligen URLs zu den Blobcontainern für das Eingabe- und das Ausgabeverzeichnis bereit. Nachdem der Auftrag gestartet wurde, kann der Benutzer den Status durch Abfragen eines URL-Speicherorts, der beim Auftragsstart bereitgestellt wird, überprüfen. Beachten Sie, dass ein spezieller Auftrag nur Vorgänge eines speziellen Typs (Erstellen, Aktualisieren oder Löschen) ausführen kann. Exportvorgänge werden analog durchgeführt.

Importieren

Setup

Dieser Abschnitt nimmt an, dass Sie Folgendes haben:

  1. Ein bereitgestellter Notification Hub

  2. Ein Azure Storage-Blobcontainer

  3. Verweise auf die Azure Storage und Azure Service Bus NuGet Pakete.

Erstellen einer Eingabedatei und Speichern in einem Blob

Eine Eingabedatei enthält eine Liste von Registrierungen, die pro Zeile in XML serialisiert sind. Im folgenden Beispiel wird mithilfe des Azure SDK veranschaulicht, wie die Registrierungen serialisiert und in den Blobcontainer hochgeladen werden.

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);
    }
}

Wichtig

Im obigen Code werden die Registrierungen im Arbeitsspeicher serialisiert, und der gesamte Datenstrom wird dann in einen Blob hochgeladen. Wenn Sie eine Datei von mehr als nur wenigen Megabyte hochgeladen haben, lesen Sie bitte die Azure-Blob-Anleitung, wie Sie diese Schritte ausführen können; Blockieren sie beispielsweise Blobs.

Erstellen von URL-Token

Sobald die Eingabedatei hochgeladen wurde, müssen Sie die URLs generieren und dem Benachrichtigungshub für die Eingabedatei und das Ausgabeverzeichnis bereitstellen. Beachten Sie, dass Sie zwei verschiedene BLOB-Container für Eingabe und Ausgabe verwenden können.

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);
}

Übermitteln des Auftrags

Mit den beiden Eingabe- und Ausgabe-URLs kann der Batchauftrag jetzt gestartet werden.

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--;
}

Zusätzlich zu den Eingabe- und Ausgabe-URLs erstellt dieses Beispiel ein NotificationHubJob-Objekt, das ein JobType-Objekt enthält, das Folgendes sein kann:

  • ImportCreateRegistrations

  • ImportUpdateRegistrations

  • ImportDeleteRegistrations

Sobald der Aufruf abgeschlossen ist, wird der Auftrag von Notification Hub fortgesetzt, und Sie können seinen Status überprüfen, indem Sie GetNotificationHubJobAsync aufrufen.

Bei Abschluss des Auftrags können Sie die Ergebnisse untersuchen, indem Sie sich die folgenden Dateien in Ihrem Ausgabeverzeichnis ansehen:

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

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

Diese Dateien enthalten die Liste der erfolgreichen und fehlgeschlagenen Vorgänge im Batch. Das Dateiformat ist CSV. Jede Zeile verfügt über die Zeilennummer der ursprünglichen Eingabedatei und über die Ausgabe des Vorgangs (normalerweise die erstellte oder aktualisierte Registrierungsbeschreibung).

Exportieren

Das Exportieren von Registrierungen ähnelt dem Importieren mit den folgenden Unterschieden:

  1. Sie benötigen lediglich die Ausgabe-URL

  2. Sie müssen einen NotificationHubJob vom Typ "ExportRegistrations" erstellen.

Vollständiger Beispielcode

Es folgt ein vollständiges, funktionierendes Beispiel, das Registrierungen in einen Benachrichtigungshub importiert.

using Microsoft.ServiceBus.Notifications;
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);


            //Lets 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);
        }
    }
}