Partager via



Juillet 2015

Volume 30, numéro 7

Azur Insider - événement Hubs d'Analytique et de visualisation, partie 3

Par Bruno Terkaly | Juillet 2015

Si vous avez suivi cette série, vous verrez enfin la réalisation de l'objectif principal du précédent travaillent dans cette dernière tranche, de visualiser les données provenant de dispositifs de Raspberry Pi dans le domaine. Il s'agit de la dernière tranche d'une série en trois parties autour d'un scénario de l'Internet des objets (IDO). Pour les deux précédents articles, vérifiez le numéro d'avril (msdn.microsoft.com/magazine/dn948106) et le numéro de juin (msdn.microsoft.com/magazine/mt147243).

Les objectifs de haut niveau pour mois-ci ce sont assez simples. Je vais créer une application de Node.js qui agit comme un site Web et fournit des données sur le périphérique mobile, qui pourront alors afficher un histogramme des données de précipitations. J'ai besoin du serveur Web de Node.js parce qu'il n'est généralement pas conseillé pour un appareil mobile accéder directement à des magasins de données.

Autres technologies pourraient avoir exécuté cette fonction, comme les API Web ASP.NET ou Microsoft Azure API Apps. Plupart des architectes vous conseille de filtrer, trier et analyser les données dans la couche intermédiaire sur un serveur, et non sur l'appareil mobile lui-même. C'est parce que les périphériques mobiles ont moins de puissance de calcul et vous ne voulez pas envoyer de grandes quantités d'informations à travers le fil à l'appareil. En bref, laisser le serveur faire le gros et laisser l'appareil à afficher des effets visuels.

Obtenir commencé

Pour exécuter les exemples dans mois-ci ce, vous devez avoir terminé la précédente deux pour les questions d'avril et juin. Si vous n'avez pas, vous pouvez probablement obtenir en construction manuellement votre propre magasin de données DocumentDB.

En supposant que vous avez déjà créé un magasin de données DocumentDB, commencez en allant sur le portail Azure et afficher la base de données DocumentDB. Il y a quelques outillage nice sur le portail, qui vous permet de consulter vos données et exécuter des requêtes différentes. Une fois que vous validez les données est là et comprennent sa structure de base, vous pouvez commencer à construire le serveur Web de Node.js.

Généralement, les développeurs comme valider le logiciel tel qu'ils le construire sur. Beaucoup de développeurs commencent avec les tests unitaires, par exemple, avec des outils tels que moka ou Jasmine. Par exemple, avant de générer le client mobile, il est logique de s'assurer que le serveur Web de Node.js fonctionne comme prévu. Une approche consiste à utiliser un outil de proxy Web, tel que Fiddler. Qui le rend facile d'émettre des requêtes Web (tel que HTTP GET) et Découvre la réponse au format JSON natif. Cela peut être utile, parce que lorsque vous générez le client mobile, vous pouvez être sûr de que tous les problèmes sont liés au client mobile, pas le service Web.

Pour garder les choses simples, utilisez un périphérique Windows Phone que le client, même si iOS et appareils Android sont plus fréquents. Peut-être la meilleure approche serait une technologie basée sur le Web. Cela permet de consommer ou de visualiser les données de n'importe quel appareil, donc ne pas vous limiter aux périphériques mobiles. Une autre approche consiste à utiliser des produits multi-plateforme natives, tels que Novell.

Il y a plusieurs sources d'information, que vous pouvez utiliser pour construire un client mobile basé sur le Web, ne pas le moindre de ce qui est l'organisme de normalisation du W3C. Vous pouvez en savoir plus sur eux dans les documents de normes à bit.ly/1PQ6uOt. L'Apache Software Foundation a aussi fait un groupe de travail dans cet espace. En savoir plus sur qui à cordova.apache.org. Je me suis concentré sur Windows Phone ici parce que le code est simple, facile à déboguer et simple à expliquer.

DocumentDB sur le portail

Dans cette prochaine section, je vais construire sur des travaux, dans laquelle j'ai créé un magasin de données de DocumentDB pour les données de température et de la ville. Pour trouver votre base de données DocumentDB (TemperatureDB), il suffit de cliquer sur l'élément de menu, parcourir, puis sélectionnez comptes DocumentDB. Vous pouvez également verrouiller votre base de données comme une tuile sur la page d'accueil en cliquant à droite, faisant le principal portail Azure à la page d'un tableau de bord. Vous pouvez trouver un bon résumé de l'utilisation de l'outillage portail d'interagir avec votre magasin de données de DocumentDB à bit.ly/112L4X1.

Une des fonctionnalités vraiment utiles sur le nouveau portail est la capacité d'interroger et de consulter les données réelles dans DocumentDB, vous permettant de voir les données de température et de la ville dans son format natif de JSON. Ceci simplifie considérablement votre capacité de facilement modifier le serveur Web de Node.js et de construire sur toutes les questions nécessaires.

Construire sur le serveur Web de Node.js

Une des premières choses que vous devrez effectuer lors de la création de votre serveur Web de Node.js est connecter à la Banque de données DocumentDB. Vous y trouverez les chaînes de connexion nécessaires sur le portail Azure. Pour obtenir les chaînes de connexion à partir du portail, sélectionnez le magasin de TemperatureDB DocumentDB, puis cliquez sur tous les paramètres, puis par touches.

A partir de là, vous aurez besoin de deux éléments d'information. Le premier est l'URI pour le magasin de données de DocumentDB. La seconde est la sécurité de l'information clé (appelée la clé primaire sur le portail), qui vous permet d'appliquer un accès sécurisé sur le serveur Web de Node.js. Vous verrez la connexion et les informations de la base de données dans le code suivant :

{
  "HOST"       : "https://temperaturedb.documents.azure.com:443/",
  "AUTH_KEY"   : "secret key from the portal",
  "DATABASE"   : "TemperatureDB",
  "COLLECTION" : "CityTempCollection"
}

Le serveur Web de Node.js lit les informations de configuration. Le champ de l'hôte dans votre fichier de configuration sera différent, car tous les URI sont uniques dans le monde. La clé d'autorisation est également unique.

Dans de précédents articles, la base de données de température a été nommé TemperatureDB. Chaque base de données peut avoir plusieurs collections, mais dans ce cas il y a juste un appelé de la collection CityTemperature. Une collection n'est rien de plus qu'une liste de documents. Dans ce modèle de données, un document unique est une ville avec des données de température des 12 derniers mois.

Que vous plongez dans les détails du code pour le serveur Web de Node.js, vous pouvez tirer parti de l'écosystème étendu de quelques Add-on­ies pour Node.js. Vous utiliserez deux bibliothèques (appelés nœud Packages) pour ce projet. Le premier paquet est pour la fonctionnalité DocumentDB. La commande pour installer le package DocumentDB n'est : documentdb d'installation du Musée. Le deuxième paquet est pour la lecture du fichier de configuration : NGP installer nconf. Ces paquets fournissent des fonctionnalités supplémentaires manquant dans l'installation par défaut de Node.js. Vous pouvez trouver un tutoriel plus large sur la construction sur une demande de Node.js pour DocumentDB dans la documentation d'Azur à bit.ly/1E7j5Wg.

Il y a sept sections dans le serveur Web de Node.js, comme le montre Figure 1. Section 1 couvre les connexions à certains des paquets installés, donc vous n'avez pas accès à eux plus tard dans le code. L'article 1 définit également le port par défaut à laquelle le client mobile se connectera. Lorsqu'il est déployé sur Azure, le numéro de port est géré par Azure, d'où le process.env.port construire.

Figure 1 le construit sur le serveur Web de Node.js

// +-----------------------------+
// |        Section 1            |
// +-----------------------------+
var http = require('http');
var port = process.env.port || 1337;
var DocumentDBClient = require('documentdb').DocumentClient;
var nconf = require('nconf');
// +-----------------------------+
// |        Section 2            |
// +-----------------------------+
// Tell nconf which config file to use
nconf.env();
nconf.file({ file: 'config.json' });
// Read the configuration data
var host = nconf.get("HOST");
var authKey = nconf.get("AUTH_KEY");
var databaseId = nconf.get("DATABASE");
var collectionId = nconf.get("COLLECTION");
// +-----------------------------+
// |        Section 3            |
// +-----------------------------+
var client = new DocumentDBClient(host, { masterKey: authKey });
// +-----------------------------+
// |        Section 4            |
// +-----------------------------+
http.createServer(function (req, res) {
  // Before you can query for Items in the document store, you need to ensure you
  // have a database with a collection, then use the collection
  // to read the documents.
  readOrCreateDatabase(function (database) {
    readOrCreateCollection(database, function (collection) {
      // Perform a query to retrieve data and display
      listItems(collection, function (items) {
        var userString = JSON.stringify(items);
        var headers = {
          'Content-Type': 'application/json',
          'Content-Length': userString.length
        };
        res.write(userString);
        res.end();
      });
    });
  });
}).listen(8124,'localhost');  // 8124 seemed to be the
                              // port number that worked
                              // from my development machine.
// +-----------------------------+
// |        Section 5            |
// +-----------------------------+
// If the database does not exist, then create it, or return the database object.
// Use queryDatabases to check if a database with this name already exists. If you
// can't find one, then go ahead and use createDatabase to create a new database
// with the supplied identifier (from the configuration file) on the endpoint
// specified (also from the configuration file).
var readOrCreateDatabase = function (callback) {
  client.queryDatabases('SELECT * FROM root r WHERE r.id="' + databaseId +
    '"').toArray(function (err, results) {
    console.log('readOrCreateDatabase');
    if (err) {
      // Some error occured, rethrow up
      throw (err);
    }
    if (!err && results.length === 0) {
      // No error occured, but there were no results returned,
      // indicating no database exists matching the query.           
      client.createDatabase({ id: databaseId }, function (err, createdDatabase) {
        console.log('client.createDatabase');
        callback(createdDatabase);
      });
    } else {
      // we found a database
      console.log('found a database');
      callback(results[0]);
    }
  });
};
// +-----------------------------+
// |        Section 6            |
// +-----------------------------+
// If the collection does not exist for the database provided, create it,
// or return the collection object. As with readOrCreateDatabase, this method
// first tried to find a collection with the supplied identifier. If one exists,
// it is returned and if one does not exist it is created for you.
var readOrCreateCollection = function (database, callback) {
  client.queryCollections(database._self, 'SELECT * FROM root r WHERE r.id="' +
    collectionId + '"').toArray(function (err, results) {
    console.log('readOrCreateCollection');
    if (err) {
      // Some error occured, rethrow up
      throw (err);
    }
    if (!err && results.length === 0) {
      // No error occured, but there were no results returned, indicating no
      // collection exists in the provided database matching the query.
      client.createCollection(database._self, { id: collectionId },
        function (err, createdCollection) {
        console.log('client.createCollection');
        callback(createdCollection);
      });
    } else {
      // Found a collection
      console.log('found a collection');
      callback(results[0]);
    }
  });
};
// +-----------------------------+
// |        Section 7            |
// +-----------------------------+
// Query the provided collection for all non-complete items.
// Use queryDocuments to look for all documents in the collection that are
// not yet complete, or where completed = false. It uses the DocumentDB query
// grammar, which is based on ANSI - SQL to demonstrate this familiar, yet
// powerful querying capability.
var listItems = function (collection, callback) {
  client.queryDocuments(collection._self, 'SELECT c.City,
    c.Temperatures FROM c where c.id="WACO- TX"').toArray(function (err, docs) {
    console.log('called listItems');
    if (err) {
      throw (err);
    }
    callback(docs);
  });
}

Section 2 lit dans le fichier config.json qui contient les informations de connexion, y compris la collection de bases de données et de document. Toujours, il est logique de prendre des littéraux de chaîne relatives à l'information de connexion et placez-les séparément dans un fichier de configuration.

L'article 3 est l'objet de connexion de client que vous utilisez pour interagir avec DocumentDB. La connexion est passée au constructeur pour DocumentDBClient.

Section 4 représente le code d'exécution une fois que le client mobile se connecte à l'application de serveur Web de Node.js. Le createServer est un noyau primitif pour des applications de Node.JS et implique un certain nombre de concepts autour de la boucle d'évènements et de traiter les demandes HTTP. Vous pouvez en savoir plus sur cette construction (bit.ly/1FcNq1E).

Cela représente le point d'entrée de haut niveau pour les clients qui se connectent au serveur Web de Node.js. Il est également chargé d'appeler les autres morceaux de Node.js code qui récupère des données JSON de DocumentDB. Une fois que les données sont récupérées, il emballez-le comme une charge de JSON-basé et remettre à un client mobile. Elle s'appuie sur les objets de requête et de réponse, qui sont des paramètres à la fonction createServer, (http.createServer (function (req, res)...).

Section 5 commence le traitement des requêtes DocumentDB. Votre magasin de données de DocumentDB peut contenir plusieurs bases de données. L'article 5 vise à affiner les données au niveau de l'URI DocumentDB, puis pointez sur une base de données spécifique. Dans ce cas, c'est TemperatureDB. Vous verrez également un code supplémentaire qui n'est pas utilisé directement, mais est-il purement à des fins éducatives. Vous noterez également certains code pour créer une base de données s'il n'existe pas. Une grande partie de la logique dans la Section 5 et au-delà est basée sur l'emballage du npm DocumentDB préalablement installé.

Section 6 représente une nouvelle étape dans le processus de récupération de données. Ici, le code est automatiquement appelé à la suite du code dans la Section 5. Section 6 encore rétrécit les données, forage vers le bas pour la collection de documents à l'aide de la base de données établie (température­DB) dans la Section 5. Vous remarquerez l'instruction select qui comprend une clause where clause pour la collection CityTemperature. Qui inclut du code pour créer une collection si elle n'existe pas.

Section 7 représente la requête finale exécutée avant que les données sont renvoyées au client mobile. Pour garder les choses simples, la requête est codé en dur pour retourner les données de température pour la ville de Waco, au Texas. Dans un scénario réaliste, le client mobile passerait dans la ville (basée sur l'entrée de l'utilisateur ou, éventuellement, emplacement de l'appareil). Le serveur Web de Node.js aurait ensuite analyser la ville passée en et ajoutez-la à Where clause figurant dans l'article 7.

Le serveur Web de Node.js est maintenant terminé et prêt à exécuter. Une fois que c' est vers le haut et en l'exécutant, il attendra indéfiniment des demandes du client de périphérique mobile. Vous devez exécuter le serveur Web de Node.js localement sur votre ordinateur de développement. À ce stade, il est logique d'utiliser Fiddler pour commencer à tester le serveur Web de Node.js. Fiddler vous permet d'émettre des requêtes HTTP (dans ce cas, GET) vers le service Web et d'afficher la réponse. Valider le comportement dans Fiddler peut vous aider à résoudre les problèmes avant de construire le client mobile.

Maintenant vous êtes prêt à construire sur le client mobile, ce qui implique deux composants architectes de base — le UI XAML et le Code CS -­Behind (où vit de la logique de programmation). Le code illustré dans Figure 2 représente le balisage de l'interface graphique sur le client mobile.

Figure 2 le balisage XAML pour Mobile Client principal écran histogramme

<Page
  x:Class="CityTempApp.MainPage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:CityTempApp"
  xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  xmlns:charting="using:WinRTXamlToolkit.Controls.DataVisualization.Charting"
  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Grid>
    <!-- WinRTXamlToolkit chart control -->
    <charting:Chart
      x:Name="BarChart"
      Title=""
      Margin="5,0">
      <charting:BarSeries
        Title="Rain (in)"
        IndependentValueBinding="{Binding Name}"
        DependentValueBinding="{Binding Value}"
        IsSelectionEnabled="True"/>
      </charting:Chart>
  </Grid>
</Page>

Remarquez qu'il intègre la WinRTXamlToolkit, que vous trouverez sur CodePlex à bit.ly/1PQdXwO. Cette trousse comprend un certain nombre de contrôles intéressants. Celui que vous utilisez est le contrôle chart. Graphique des données, simplement constituer une collection nom/valeur et l'associer au contrôle. Dans ce cas, vous allez construire une collection nom/valeur des données de précipitations pour chaque mois dans une ville donnée.

Avant de présenter la solution finale, il y a quelques bémols. Ne pouvait contester à l'aide d'une application native de Windows Phone, au profit d'une approche davantage axée sur le Web.

Le client mobile construit ici prend un certain nombre de raccourcis à venir avec la fonctionnalité minimale nue. Par exemple, le graphique à barres apparaîtra immédiatement une fois que vous exécutez le client Windows Phone. C'est parce qu'il y a une requête Web vers le serveur Web de Node.js de l'événement OnNavigatedTo. Elle exécute automatiquement une fois que le client Windows Phone commence. Vous pouvez le voir dans la Section 1 du code client mobile montre Figure 3.

Code de la figure 3 pour le Client Mobile

public sealed partial class MainPage : Page
{
  public MainPage()
  {
    this.InitializeComponent();
    this.NavigationCacheMode = NavigationCacheMode.Required;
  }
  // +-----------------------------+
  // |        Section 1            |
  // +-----------------------------+
  protected override void OnNavigatedTo(NavigationEventArgs e)
  {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
      "http://localhost:8124");
    request.BeginGetResponse(MyCallBack, request);
  }
  // +-----------------------------+
  // |        Section 2            |
  // +-----------------------------+
  async void MyCallBack(IAsyncResult result)
  {
    HttpWebRequest request = result.AsyncState as HttpWebRequest;
    if (request != null)
    {
      try
      {
        WebResponse response = request.EndGetResponse(result);
        Stream stream = response.GetResponseStream();
        StreamReader reader = new StreamReader(stream);
        JsonSerializer serializer = new JsonSerializer();
        // +-----------------------------+
        // |        Section 3            |
        // +-----------------------------+
        // Data structures coming back from Node
        List<CityTemp> cityTemp = (List<CityTemp>)serializer.Deserialize(
          reader, typeof(List<CityTemp>));
        // Data structure suitable for the chart control on the phone
        List<NameValueItem> items = new List<NameValueItem>();
        string[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
        for (int i = 11; i >= 0; i-- )
        {
          items.Add(new NameValueItem { Name = months[i], Value =
            cityTemp[0].Temperatures[i] });
        }
        // +-----------------------------+
        // |        Section 4            |
        // +-----------------------------+
        // Provide bar chart the data
        await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
          this.BarChart.Title = cityTemp[0].City + ", 2014";
          ((BarSeries)this.BarChart.Series[0]).ItemsSource = items;
        });
      }
      catch (WebException e)
      {
        return;
      }
    }
  }
}
// +-----------------------------+
// |        Section 5            |
// +-----------------------------+
// Data structures coming back from Node
public class CityTemp
{
  public string City { get; set;  }
  public List<double> Temperatures { get; set; }
}
// Data structure suitable for the chart control on the phone
public class NameValueItem
{
  public string Name { get; set; }
  public double Value { get; set; }
}

Également à la Section 1, vous remarquerez qu'il se connecte au serveur Web de Node.js exécutant sur l'hôte local. Évidemment, vous devez spécifier un point de terminaison différent si vous hébergez votre serveur Web de Node.js dans le cloud public, tels que les sites Web d'Azur. Après la publication de la demande Web, vous configurez le rappel à l'aide de la méthode BeginGetResponse. Les requêtes Web sont asynchrones, donc le code met en place un rappel (MyCallBack). Ceci mène à l'article 2, où les données sont récupérées, transformées et chargées dans un contrôle chart.

Le rappel à la Section 2 de la demande Web asynchrone décrites dans la Section 1 du processus la charge utile renvoyée par le service Web. Le code traite de la réponse Web, sérialiser les données, qui est au format JSON. L'article 3 est de transformer ces données JSON en deux structures de données distinct définis à l'article 5. Le but est de créer une liste de nom/valeur de tableaux ou de listes. La classe NameValueItem est la structure que du contrôle chart a besoin.

L'article 4 est sur l'utilisation de la thread de GUI d'assigner la liste nom/valeur au contrôle chart. Vous pouvez voir l'affectation de la collection source de points. L'attente cela. Dispatcher.RunAsync syntaxe s'appuie sur le thread de mise à jour des contrôles visuels. Le code ne fonctionnera pas correctement si vous essayez de mettre à jour l'interface graphique en utilisant le même thread que le traitement des données et de la demande Web.

Vous pouvez maintenant lancer le client mobile. Vous pourriez manquer certaines des données sur la température, bien que, si tous la barre de contrôles peuvent ne pas apparaître.

Jaquette en haut

Voilà qui termine la série en trois parties, où j'ai mis dehors pour montrer un scénario de l'ITO-to-end — due à l'ingestion de données de persistance des données à visualiser les données. J'ai commencé cette série en ingérant les données à l'aide d'un programme en C qui s'exécutent sous Ubuntu, comme un effort pour simuler le code que vous auriez besoin pour s'exécuter sous un dispositif Raspberry Pi. Vous pouvez insérer les données capturées par un capteur de température à Azure événement concentrateurs. Cependant, données stockées ici sont éphémères et vous avez besoin pour le déplacer dans un magasin plus permanent.

Qui vous a amené au second article, dans lequel un processus d'arrière-plan prend les données d'événement Hubs et il déplace à la fois de base de données de SQL Azure et de DocumentDB. Puis cette dernière tranche exposés ces données stockées en permanence aux appareils mobiles à l'aide d'un niveau intermédiaire en cours d'exécution Node.js.

Il existe de nombreuses extensions possibles et les améliorations qui peuvent être effectuées. Par exemple, un domaine, vous pourriez Explorer est la notion d'apprentissage automatique et analytique. La visualisation graphique à barres répond à la question fondamentale, « Qu'est-il arrivé? » Une question plus intéressante pourrait être, « Ce qui va se passer? » En d'autres termes, vous pouvez alors prévoir les précipitations futures ? La question ultime pourrait être, « Ce que devrais j'aujourd'hui basé sur prédictions futures? »


Bruno Terkaly est un ingénieur logiciel principal chez Microsoft dans le but de permet de développer des applications et services de pointe dans l'ensemble de périphériques. Il est chargé de conduire les nuages haut de la page et les opportunités mobiles à travers les Etats-Unis et au-delà du point de vue technologie habilitation. Il aide les partenaires apportent leurs applications sur le marché en fournissant des conseils architecturaux et technique approfondies engagement durant l'évaluation, de développement et de déploiement de l'ISV. Terkaly travaille également en étroite collaboration avec les nuages et les groupes de la technologie mobiles, fournissant des commentaires et qui influent sur la feuille de route.

Remercie les experts techniques Microsoft suivants pour avoir relu cet article : Leland Holmquest, David Magnotti et Nick Trogh
David Magnotti (Microsoft), Leland Holmquest (Microsoft), Nick Trogh (Microsoft)

David Magnotti est un Premier Field Engineer chez Microsoft, spécialisé dans le développement logiciel. Il prend principalement en charge des clients d'entreprise de Microsoft par le biais de la preuve des concepts de construction, entrepris des services consultatifs et animation d'ateliers. Il peut souvent être trouvées vidages débogage et analyse traces de performances de windows.

Leland Holmquest est une Solution Manager pour la plate-forme de gestion des connaissances pour les Services Microsoft, fan de longue date du MSDN Magazine et partisan de do'ers !

Nick Trogh est une technique chez Microsoft, où il aide les développeurs à réaliser leurs rêves de logiciels sur la plateforme Microsoft. Vous lui trouverez blogging, Twitter ou lors d'événements de développeur.