Support de présentation utilisé pendant le Global Azure Bootcamp 2015 (GAB2015) à Lyon autour de NodeJS et SocketIO et les différentes possibilités de gestion de la scalabilité automatique dans le Cloud.
Outre le contexte particulier à Azure, le schéma présenté ici peut s'adapter à n'importe quel Cloud. D'ailleurs, la deuxième solution utilisée en production est totalement indépendante d'Azure puisque repose sur la fonctionnalité de Publisher&Subscriber
3. Microsoft Azure#GlobalAzure #ViseoSpirit
• Node
• Open source
– Créé en 2009
– Basé sur le moteur V8
• Application C
– Utilisée en tant que « gestionnaire de requêtes HTTPs » dans
l’exemple d’aujourd’hui
– « Event-driven »
– IO non bloquants
NodeJS
9. Microsoft Azure#GlobalAzure #ViseoSpirit
• Windows Server
• Serveur web IIS
• Scalabilité automatique
• Pas de root
• Pas d’accès RDP aux machines
Architecture Web App
9
10. Microsoft Azure#GlobalAzure #ViseoSpirit
• Pas de changement de l’application
• Préparation de l’environnement
– Importation d’un profil de sécurisation
– Exécuté une seule fois
• Selon votre préférence :
– Portail en ligne
– x-CLI
– API Rest
Déploiement
10
14. Microsoft Azure#GlobalAzure #ViseoSpirit
Quels problèmes pour la scalabilité ?
1 1
2 2 2
Les messages ne sont pas envoyés (broadcastés) aux différentes « instances socket.io »
Le stockage des participant est « local » (non partagé)
1
2
15. Microsoft Azure#GlobalAzure #ViseoSpirit
Architecture ciblée
15
Stockage des participants basé sur Azure
Table Storage (un stockage clé-valeur)
Azure Service Bus service utilisé
comme connecteur inter-scokets
(compatible protocole AMQP)
Point d’entrée de l’application Web App
Agit comme un Load Balancer
Une instance
16. Microsoft Azure#GlobalAzure #ViseoSpirit
1ère étape : Adapter SocketIO - Service Bus
require(‘azure’);
module.exports = adapter;
function adapter(opts) {
...
var azureServiceBusSender = azure.createServiceBusService();
var azureServiceBusReceiver = azure.createServiceBusService();
function AzureServiceBus(namespace) {
azureServiceBusReceiver.createTopicIfNotExists("default", function(err) {
azureServiceBusReceiver.createSubscription("default",getId(), function(err) {
azureServiceBusReceiver.receiveSubscriptionMessage("default",getId(),this.onmessage.bind(this));
}
})
}
AzureServiceBus.prototype.__proto__ = Adapter.prototype;
AzureServiceBus.prototype.onmessage = function(err, receivedMessage) {
azureServiceBusReceiver.receiveSubscriptionMessage(opts.topic, this.subscriptionId, this.onmessage.bind(this));
args.push(true);
this.broadcast.apply(this, args);
};
16
20. Microsoft Azure#GlobalAzure #ViseoSpirit
Nouvelle architecture ciblée
20
Stockage des participants basé sur
Azure Table Storage (un stockage clé-
valeur)
Azure Redis Cache (via ses
capacités pub/sub)
service utilisé comme connecteur
pour socket.IO
Point d’entrée de l’application Web App
Agit comme un Load Balancer
Une instance
21. Microsoft Azure#GlobalAzure #ViseoSpirit
1ère étape : Adapter SocketIO - Redis
var redis = require(‘redis’).createClient;
var redisAdapter = require('socket.io-redis');
module.exports = function (app, io) {
...
// Publisher
var redisClientPub = redis(
process.env.REDIS_PORT,
process.env.REDIS_HOST,
{auth_pass: process.env.REDIS_AUTH_PASS}
);
// Subscriber
var redisClientSub = redis(
process.env.REDIS_PORT,
process.env.REDIS_HOST,
{auth_pass: process.env.REDIS_AUTH_PASS, detect_buffers: true}
);
io.adapter(redisAdapter({
pubClient: redisClientPub,
subClient: redisClientSub
}));
21
22. Microsoft Azure#GlobalAzure #ViseoSpirit
2ème étape : Stockage – Azure Table Storage
var azureStorage = require('azure-storage');
module.exports = function () {
// Service Repository
var ParticipantTableStorage = function () {
var azureTableService = azureStorage.createTableService();
var entGen = azureStorage.TableUtilities.entityGenerator;
var tableName = 'participants';
var partitionKey = "default";
var prefix = process.env.WEBSITE_INSTANCE_ID || 'default';
// If this is the first time the application is launched, let's create the table
azureTableService.createTableIfNotExists(tableName);
return {
22
23. Microsoft Azure#GlobalAzure #ViseoSpirit
// Retrieve participants count from Store
// We retrieve all participants to show you how to get a users' list
getCount: function(room, callback) {
var query = new azureStorage.TableQuery()
.where('PartitionKey eq ?', partitionKey + room);
azureTableService.queryEntities(tableName, query, null, function(err, result, response){
callback(undefined, result.entries.length);
}.bind(this))
},
// Add a new participant to the store
add: function(participant) {
var entity = {
PartitionKey: entGen.String(partitionKey + participant.room),
RowKey: entGen.String(prefix + participant.id),
username: entGen.String(participant.username),
avatar: entGen.String(participant.avatar),
room: entGen.String(participant.room)
};
azureTableService.insertEntity(tableName, entity)
},
23
24. Microsoft Azure#GlobalAzure #ViseoSpirit
// Remove a participant from the store
remove: function(participant) {
var entity = {
PartitionKey: entGen.String(partitionKey + participant.room),
RowKey: entGen.String(prefix + participant.id)
};
azureTableService.deleteEntity(tableName, entity);
}
};
return ParticipantTableStorage;
};
24
27. Microsoft Azure#GlobalAzure #ViseoSpirit
• Devrait être une règle de base
• Créez des interfaces communes
– Règles d’équipes
– TypeScript
• Injection de dépendances
– Stockage de données
– Système de fichiers
– Gestionnaire d’événement
– … toutes les I/Os
I/O abstraction
27