En este artículo, le mostraremos cómo crear una aplicación de chat en tiempo real utilizando las siguientes tecnologías:
- Redis
- Node.js + Express.js
- Socket.IO
- Heroku
Redis
Redis es un almacén de estructura de datos en memoria de código abierto (licencia BSD), que se utiliza como base de datos, caché y agente de mensajes. Admite estructuras de datos como cadenas, hashes, listas, conjuntos, conjuntos ordenados con consultas de rango, mapas de bits, hiperloglogs e índices geoespaciales con consultas de radio.
En este tutorial de aplicación, nos conectaremos a uno de los clústeres mediante el alojamiento de ScaleGrid para Redis™*.
Nodo.js
Una plataforma basada en el tiempo de ejecución de JavaScript de Chrome para crear fácilmente aplicaciones de red rápidas y escalables. Node.js utiliza un modelo de E/S sin bloqueo y controlado por eventos que lo hace liviano y eficiente, por lo tanto, perfecto para aplicaciones en tiempo real con uso intensivo de datos que se ejecutan en dispositivos distribuidos.
Express.js
Un marco Node.js. Node.js es una plataforma que permite usar JavaScript fuera de los navegadores web para crear aplicaciones web y de red. Esto significa que puede crear el servidor y el código del lado del servidor para una aplicación como la mayoría de los otros lenguajes web, pero usando JavaScript.
Socket.IO
Una biblioteca de JavaScript para aplicaciones web en tiempo real que permite la comunicación bidireccional en tiempo real entre clientes y servidores web. Socket.IO tiene dos componentes:una biblioteca del lado del cliente que se ejecuta en el navegador y una biblioteca del lado del servidor para Node.js. Ambos componentes tienen API casi idénticas.
Héroe
Una plataforma en la nube que permite a las empresas crear, entregar, monitorear y escalar aplicaciones. La plataforma Heroku es la forma más rápida de pasar de la idea a la URL, evitando todos esos dolores de cabeza de infraestructura.
Este artículo asume que ya tiene Redis, Node.js y Heroku Toolbelt instalados en su máquina.
Configuración
Cree una carpeta y asígnele un nombre. Puede crearlo en cualquier parte de su máquina ya que Node.js no necesita un servidor especial como Apache/nginx.
Paso 1
Inicialice un archivo package.json ejecutando npm init
.
{ "name": "node-socket-redis-chat-scalegrid", "version": "0.0.1", "description": "A realtime chat application using Redis, Node.js and Socket.IO", "dependencies": { "body-parser": "^1.15.2", "express": "^4.10.2", "redis": "^2.6.3", "socket.io": "^1.7.1" }, "main": "index.js", "scripts": { "start": "node index.js" }, "engines": { "node": "4.1.1" } }
Paso 2
Instalar las siguientes dependencias:
- expressjs
- socketio
- redis
…y algunos otros métodos de utilidad:
- analizador de cuerpo
ejecutando el siguiente comando:
npm install --save expressjs socket.io redis body-parser
Paso 3
Cree una carpeta pública para almacenar nuestros archivos CSS y JS:
/public/css/main.css /public/js/main.js
Paso 4:
Cree una carpeta de vistas para almacenar nuestro archivo HTML principal:
/views/index.html
Paso 5:
Crear un creds.json
archivo que contendrá las credenciales para conectarse a nuestro Redis™ Cluster. Debe seguir el siguiente formato:
{ "user": "", "password": "", "host": "", "port": 6379 }
Paso 6:
Cree el index.js
archivo que alojará nuestro código Node.js y servirá como punto de partida para Heroku.
Paso 7:
Agregue un .gitignore
archivo para que la carpeta node_modules no se registre en Heroku:
node_modules
Después de completar el paso 7, deberías tener la siguiente estructura:
. ├── creds.json ├── index.js ├── package.json ├── public │ ├── css │ │ └── main.css │ └── js │ └── main.js └── views └── index.html
Paso 8
Ahora que todo está configurado, podemos comenzar a escribir nuestro código de back-end. En primer lugar, debemos incorporar todos nuestros módulos. Por lo tanto, abra el archivo index.js y pegue lo siguiente:
var express = require('express'); var bodyParser = require('body-parser'); var app = express(); var http = require('http').Server(app); var io = require('socket.io')(http); var fs = require('fs'); var creds = ''; var redis = require('redis'); var client = ''; var port = process.env.PORT || 8080; // Express Middleware for serving static // files and parsing the request body app.use(express.static('public')); app.use(bodyParser.urlencoded({ extended: true })); // Start the Server http.listen(port, function() { console.log('Server Started. Listening on *:' + port); }); // Store people in chatroom var chatters = []; // Store messages in chatroom var chat_messages = [];
Antes de que podamos comenzar a escribir cualquier código, necesitamos un clúster que ejecute Redis. Afortunadamente, ScaleGrid for Redis™ proporciona una solución de alojamiento de alto rendimiento, con un solo clic y completamente administrada.
Si aún no es miembro, puede registrarse para una prueba gratuita de 30 días aquí.
De lo contrario, inicie sesión en su tablero y cree un nuevo clúster de Redis™ en la sección de Redis™:
Una vez que se complete la creación del clúster, tome nota de la información anterior y agréguela a los campos relevantes de creds.json
archivo.
Ahora que tenemos configuradas nuestras credenciales, estamos listos para crear nuestro cliente Redis en Node que se conectará a nuestro clúster y comenzará a almacenar pares clave-valor.
Agregue el siguiente código al archivo index.js:
// Read credentials from JSON fs.readFile('creds.json', 'utf-8', function(err, data) { if(err) throw err; creds = JSON.parse(data); client = redis.createClient('redis://' + creds.user + ':' + creds.password + '@' + creds.host + ':' + creds.port); // Redis Client Ready client.once('ready', function() { // Flush Redis DB // client.flushdb(); // Initialize Chatters client.get('chat_users', function(err, reply) { if (reply) { chatters = JSON.parse(reply); } }); // Initialize Messages client.get('chat_app_messages', function(err, reply) { if (reply) { chat_messages = JSON.parse(reply); } }); }); });
El código anterior hace dos cosas:
- Lee las credenciales de
creds.json
y crea un cliente de Redis que se utiliza para realizar operaciones de clave-valor - Una vez que el cliente está listo, completamos los
chatters
y loschat_messages
por lo que cualquier miembro nuevo que se una podrá ver el historial de chat.
Ahora vamos a escribir un par de API para manejar la aplicación de chat. Necesitamos las siguientes API:
- Unirse a la sala [POST]
- Salir de la habitación [POST]
- Enviar mensaje [POST]
- Obtener mensajes [GET]
- Obtener miembros [GET]
Empecemos con la API Join Room. Esto se llama cuando cualquier usuario nuevo inicia la aplicación por primera vez e intenta unirse a la sala de chat:
// API - Join Chat app.post('/join', function(req, res) { var username = req.body.username; if (chatters.indexOf(username) === -1) { chatters.push(username); client.set('chat_users', JSON.stringify(chatters)); res.send({ 'chatters': chatters, 'status': 'OK' }); } else { res.send({ 'status': 'FAILED' }); } });
Aquí tenemos la API para salir de la sala de chat:
// API - Leave Chat app.post('/leave', function(req, res) { var username = req.body.username; chatters.splice(chatters.indexOf(username), 1); client.set('chat_users', JSON.stringify(chatters)); res.send({ 'status': 'OK' }); });
Enviando y almacenando el mensaje:
// API - Send + Store Message app.post('/send_message', function(req, res) { var username = req.body.username; var message = req.body.message; chat_messages.push({ 'sender': username, 'message': message }); client.set('chat_app_messages', JSON.stringify(chat_messages)); res.send({ 'status': 'OK' }); });
Obtener todos los mensajes en la sala:
// API - Get Messages app.get('/get_messages', function(req, res) { res.send(chat_messages); });
Obtener todos los miembros:
// API - Get Chatters app.get('/get_chatters', function(req, res) { res.send(chatters); });
Una vez que tengamos todas las API configuradas, debemos escribir el código Socket.IO para emitir eventos cuando se actualicen ciertas propiedades como las siguientes:
- Recuento de habitaciones
- Mensajes
// Socket Connection // UI Stuff io.on('connection', function(socket) { // Fire 'send' event for updating Message list in UI socket.on('message', function(data) { io.emit('send', data); }); // Fire 'count_chatters' for updating Chatter Count in UI socket.on('update_chatter_count', function(data) { io.emit('count_chatters', data); }); });
Estos eventos luego son recogidos en el front-end por la biblioteca Socket.IO, que a su vez actualiza la interfaz de usuario.
Paso 9
Ahora, necesitamos crear nuestra interfaz de usuario que permita a los usuarios iniciar sesión y chatear.
Abre el index.html
archivo y agregue el siguiente código:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Node.js + Socket.io + Redis Chat | ScaleGrid</title> <link rel="stylesheet" href="css/main.css"> </head> <body> <div class="container"> <h1>Node.js + Socket.io + Redis Chat | ScaleGrid</h1> <div class="join-chat"> <label for="username">Username:</label> <input type="text" id="username" name="username" /> <input type="button" id="join-chat" value="Join Chat" /> </div><br /> <div class="chat-info"></div><br /> <div class="chat"> <div class="messages"></div> <textarea name="message" id="message" cols="90" rows="5" placeholder="Enter your message..."></textarea><br /><br /> <input type="button" id="send-message" data-username="" value="Send Message"> <input type="button" id="leave-chat" data-username="" value="Leave Chat"> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.6.0/socket.io.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <script src="js/main.js"></script> </body> </html>
Paso 10
Para que nuestro HTML funcione, necesitamos agregar algunos eventos AJAX de JavaScript que manejarán las diversas operaciones, como unirse a una sala, salir, enviar un mensaje, etc.
El siguiente código obtiene la cantidad de personas que chatean para que podamos actualizar la interfaz de usuario sobre la cantidad total de personas en la sala:
$.get('/get_chatters', function(response) { $('.chat-info').text("There are currently " + response.length + " people in the chat room"); chatter_count = response.length; //update chatter count });
Este código permite a los usuarios unirse a la sala de chat. Recuerde, los nombres de usuario son únicos y no se pueden duplicar:
$('#join-chat').click(function() { var username = $.trim($('#username').val()); $.ajax({ url: '/join', type: 'POST', data: { username: username }, success: function(response) { if (response.status == 'OK') { //username doesn't already exists socket.emit('update_chatter_count', { 'action': 'increase' }); $('.chat').show(); $('#leave-chat').data('username', username); $('#send-message').data('username', username); $.get('/get_messages', function(response) { if (response.length > 0) { var message_count = response.length; var html = ''; for (var x = 0; x < message_count; x++) { html += "<div class='msg'><div class='user'>" + response[x]['sender'] + "</div><div class='txt'>" + response[x]['message'] + "</div></div>"; } $('.messages').html(html); } }); $('.join-chat').hide(); //hide the container for joining the chat room. } else if (response.status == 'FAILED') { //username already exists alert("Sorry but the username already exists, please choose another one"); $('#username').val('').focus(); } } }); });
Este es el código para permitir que los usuarios abandonen la sala de chat:
$('#leave-chat').click(function() { var username = $(this).data('username'); $.ajax({ url: '/leave', type: 'POST', dataType: 'json', data: { username: username }, success: function(response) { if (response.status == 'OK') { socket.emit('message', { 'username': username, 'message': username + " has left the chat room.." }); socket.emit('update_chatter_count', { 'action': 'decrease' }); $('.chat').hide(); $('.join-chat').show(); $('#username').val(''); alert('You have successfully left the chat room'); } } }); });
Este es el código que se ejecuta cada vez que alguien envía un mensaje:
$('#send-message').click(function() { var username = $(this).data('username'); var message = $.trim($('#message').val()); $.ajax({ url: '/send_message', type: 'POST', dataType: 'json', data: { 'username': username, 'message': message }, success: function(response) { if (response.status == 'OK') { socket.emit('message', { 'username': username, 'message': message }); $('#message').val(''); } } }); });
El siguiente es el código de Socket.IO que escucha los eventos del backend y actualiza la interfaz de usuario. Por ejemplo, agregar nuevos mensajes al área de mensajes, actualizar el conteo de conversaciones, etc.:
socket.on('send', function(data) { var username = data.username; var message = data.message; var html = "<div class='msg'><div class='user'>" + username + "</div><div class='txt'>" + message + "</div></div>"; $('.messages').append(html); }); socket.on('count_chatters', function(data) { if (data.action == 'increase') { chatter_count++; } else { chatter_count--; } $('.chat-info').text("There are currently " + chatter_count + " people in the chat room"); });
¡Y listo! Inicie el servidor usando npm start
y abra múltiples ventanas del navegador para simular múltiples usuarios.
Una demostración de la aplicación está disponible aquí: https://node-socket-redis-chat.herokuapp.com/
Para implementar esta aplicación en Heroku, consulte sus documentos: https://devcenter.heroku.com/categories/deployment
El código fuente completo también está disponible en GitHub para que lo bifurques y trabajes en:https://github.com/Scalegrid/code-samples/tree/sg-redis- node-socket-chat/redis-node-socket-chat
Como siempre, si construyes algo asombroso, envíanos un tweet al respecto @scalegridio.
Si necesita ayuda con la administración y el alojamiento de Redis™, permítanos simplificarle las cosas con nuestros servicios profesionales.