Partager via


Guide pratique pour exporter et modifier des inscriptions en bloc

Certains scénarios nécessitent de créer ou modifier un grand nombre d’inscriptions dans un hub de notifications. Les mises à jour des étiquettes après des calculs par lots ou la migration d’une implémentation push existante pour utiliser Notification Hubs en sont deux exemples.

Cette rubrique explique comment utiliser le support en bloc de Notification Hubs pour exécuter un grand nombre d'opérations sur un hub de notifications ou pour exporter toutes les inscriptions.

Flux global

La prise en charge du traitement par lots vise à permettre l’exécution de travaux durables impliquant des millions d’inscriptions. Pour atteindre cette échelle, la prise en charge par lot utilise stockage Azure pour stocker les détails et la sortie du travail. Pour les opérations de mise à jour en bloc, l’utilisateur doit créer un fichier dans un conteneur d’objets blob, dans lequel sont listées les opérations de mise à jour des inscriptions. Quand il démarre le travail, l’utilisateur fournit une URL vers l’objet blob d’entrée ainsi qu’une URL vers un répertoire de sortie (également dans un conteneur d’objets blob). Une fois que le travail a démarré, l'utilisateur peut vérifier son statut en interrogeant un emplacement d'URL fourni au démarrage du travail. Notez qu'un travail spécifique ne peut exécuter que des opérations d'un type particulier (création, mise à jour ou suppression). Les opérations d’exportation sont effectuées de façon analogue.

Importer

Programme d’installation

Cette section suppose que vous disposiez des éléments suivants :

  1. Un hub de notifications provisionné.

  2. Un conteneur d’objets blob de stockage Azure.

  3. Références aux packages NuGet stockage Azure et Azure Service Bus.

Créer un fichier d’entrée et le stocker dans un objet blob

Un fichier d’entrée contient une liste d’inscriptions sérialisées en XML, à raison d’une par ligne. En utilisant le SDK Azure, l’exemple de code suivant montre comment sérialiser les inscriptions et les charger dans le conteneur d’objets 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);
    }
}

Important

Le code ci-dessus sérialise les inscriptions en mémoire, puis charge le flux entier dans un objet blob. Si vous avez chargé un fichier de plus que quelques mégaoctets, reportez-vous aux instructions de l’objet blob Azure pour savoir comment effectuer ces étapes ; par exemple, les objets blob de blocs.

Créer des jetons d’URL

Une fois que votre fichier d'entrée est chargé, vous devez générer les URL à fournir à votre hub de notifications à la fois pour le fichier d'entrée et le répertoire de sortie. Notez que vous pouvez utiliser deux conteneurs d'objets blob différents pour l'entrée et pour la sortie.

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

Envoi du travail

Avec les deux URL d'entrée et de sortie, nous pouvons maintenant démarrer le travail par lots.

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

En plus des URL d'entrée et de sortie, cet exemple crée un objet NotificationHubJob qui contient un JobType, qui peut être l'un des suivants :

  • ImportCreateRegistrations

  • ImportUpdateRegistrations

  • ImportDeleteRegistrations

Une fois l’appel terminé, le travail est poursuivi par le hub de notifications. Vous pouvez vérifier l’état du travail en appelant GetNotificationHubJobAsync.

À l’issue du travail, vous pouvez examiner les résultats dans les fichiers suivants situés dans votre répertoire de sortie :

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

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

Ces fichiers contiennent la liste des opérations traitées par lots qui ont réussi ou échoué. Le fichier est au format .cvs, dans lequel chaque ligne a le numéro de ligne du fichier d'entrée original et la sortie de l'opération (généralement la description de l'inscription créée ou mise à jour).

Exporter

L’exportation des inscriptions est similaire à leur importation, avec toutefois les différences suivantes :

  1. Vous avez uniquement besoin de l’URL de sortie.

  2. Vous devez créer un NotificationHubJob de type ExportRegistrations.

Exemple de code complet

L'exemple suivant est un exemple concret qui importe les inscriptions vers un hub de notifications.

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