"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Cree e implemente una aplicación de chat utilizando Socket.io y Redis.

Cree e implemente una aplicación de chat utilizando Socket.io y Redis.

Publicado el 2024-11-07
Navegar:683

Build and deploy a chat application using Socket.io and Redis.

En este tutorial, crearemos una aplicación de chat utilizando sockets web. Los sockets web son realmente útiles cuando desea crear aplicaciones que requieren transferencia de datos en tiempo real.

Al final de este tutorial, podrá configurar su propio servidor de socket, enviar y recibir mensajes en tiempo real, almacenar datos en Redis e implementar su aplicación en render y Google Cloud Run.

¿Qué estaremos construyendo?

Crearemos una aplicación de chat. Para ser breve, solo configuraremos el servidor. Puede utilizar su propio marco de interfaz de usuario y seguirlo.

En esta aplicación de chat, habrá salas y los usuarios podrán unirse a una sala y comenzar a chatear. Para simplificar todo, asumiremos que los nombres de usuario no son únicos. Sin embargo, cada sala solo puede tener un usuario con un nombre de usuario específico.

Configurar un servidor de socket.

Primero necesitamos instalar las dependencias requeridas.

npm i express cors socket.io -D @types/node

Usaremos el módulo http para configurar nuestro servidor de socket. Dado que nuestra aplicación se ejecutará en la terminal, tendremos que permitir todos los orígenes.

import express from "express";
import cors from "cors"
import { Server } from "socket.io";
import { createServer } from "http"

const app = express();
const server = createServer(app);

// create a socket server.
const io = new Server(server, {
  cors: {
    origin: "*",
    credentials: true,
  }
});

// listen to connections errors
io.engine.on("connection_error", console.log)

app.use(cors())

const PORT = 3000;
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Configurando Redis.

Usaremos redis para almacenar nuestros mensajes junto con la información de la sala y del usuario. Puedes usar upstash redis (gratis). Cree una nueva instancia de Redis en su panel de control superior. Después de la creación, recibirá una URL de Redis que podrá utilizar para conectarse a su instancia de Redis.

Instala cualquier cliente redis de tu elección. Usaré ioredis.

npm i ioredis

A continuación, inicializaremos nuestro cliente Redis y lo conectaremos a nuestro servidor Redis usando la URL de conexión que obtuvimos.

/** /src/index.ts */
import { Redis } from "ioredis"

if (!process.env.REDIS_URL) throw new Error("REDIS_URL env variable is not set");
const redis = new Redis(process.env.REDIS_URL);

// listen to connection events.
redis.on("connect", () => console.log("Redis connected"))
redis.on("error", console.log)

Manejo de eventos.

Los usuarios pueden crear salas o unirse a salas existentes. Las habitaciones se identifican mediante identificadores de habitación únicos. Cada miembro tiene un nombre de usuario que es único dentro de una sala y no a nivel mundial.

Podemos realizar un seguimiento de todas las salas activas en nuestro servidor, almacenando sus identificadores de sala dentro de un conjunto de redis.

Para nuestro propósito, los nombres de usuario solo son únicos dentro de una sala. Entonces, los almacenamos en un conjunto junto con la identificación de la habitación. Esto garantiza que la combinación de la identificación de la sala junto con la identificación del miembro sea única a nivel mundial.

Podemos configurar un evento de socket para crear espacio. Cuando creamos una sala, también agregamos a la sala el miembro que solicitó su creación.

io.on("connection", () => {
    // ...
    socket.on("create:room", async (message) => {
        console.log("create:room", message)

        const doesRoomExist = await redis.sismember("rooms", message.roomId)
        if (doesRoomExist === 1) return socket.emit("error", { message: "Room already exist."})

        const roomStatus = await redis.sadd("rooms", message.roomId)
        const memStatus = await redis.sadd("members", message.roomId   "::"   message.username)

        if (roomStatus === 0 || memStatus === 0) return socket.emit("error", { message: "Room creation failed." })

        socket.join(message.roomId)
        io.sockets.in(message.roomId).emit("create:room:success", message)
        io.sockets.in(message.roomId).emit("add:member:success", message)
  })
}

Para agregar un nuevo miembro a una sala existente, primero debemos verificar si el miembro ya existe en esa sala.

io.on("connection", () => {
    // ...
    socket.on("add:member", async (message) => {
        console.log("add:member", message)

        const doesRoomExist = await redis.sismember("rooms", message.roomId)
        if (doesRoomExist === 0) return socket.emit("error", { message: "Room does not exist." })

        const doesMemExist = await redis.sismember("members", message.roomId   "::"   message.username)
        if (doesMemExist === 1) return socket.emit("error", { message: "Username already exists, please choose another username." })

        const memStatus = await redis.sadd("members", message.roomId   "::"   message.username)
        if (memStatus === 0) return socket.emit("error", { message: "User creation failed." })

        socket.join(message.roomId)
        io.sockets.in(message.roomId).emit("add:member:success", message)
  })

    socket.on("remove:member", async (message) => {
        console.log("remove:member", message)

        const doesRoomExist = await redis.sismember("rooms", message.roomId)
        if (doesRoomExist === 0) return socket.emit("error", { message: "Room does not exist." })

        await redis.srem("members", message.roomId   "::"   message.username)

        socket.leave(message.roomId)
        io.sockets.in(message.roomId).emit("remove:member:success", message)
      })
}

Finalmente, creamos el evento de chat.

io.on("connection", () => {
    socket.on("create:chat", (message) => {
    console.log("create:chat", message)
    redis.lpush("chat::"   message.roomId, message.username   "::"   message.message)
    io.sockets.in(message.roomId).emit("create:chat:success", message)
  })
}

Despliegue.

El servidor de socket requiere conexiones persistentes, no funcionará en entornos sin servidor. Por lo tanto, no puede implementar su servidor de socket en vercel.

Puedes implementarlo en muchos lugares como Render, fly.io o Google Cloud Run.

Prestar

Implementación en renderizado simple. Si tiene un archivo acoplable, creará automáticamente su proyecto a partir de ese archivo acoplable. Render tiene un nivel gratuito, pero tenga en cuenta que habrá un inicio en frío en el nivel gratuito.

Aquí está mi archivo Docker.

# syntax=docker/dockerfile:1
ARG NODE_VERSION=20.13.1
ARG PNPM_VERSION=9.4.0

FROM node:${NODE_VERSION}-bookworm AS base

## set shell to bash
SHELL [ "/usr/bin/bash", "-c" ]
WORKDIR /usr/src/app

## install pnpm.
RUN --mount=type=cache,target=/root/.npm \
    npm install -g pnpm@${PNPM_VERSION}

# ------------
FROM base AS deps
# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.local/share/pnpm/store to speed up subsequent builds.
# Leverage bind mounts to package.json and pnpm-lock.yaml to avoid having to copy them
# into this layer.
RUN --mount=type=bind,source=package.json,target=package.json \
    --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
    --mount=type=cache,target=/root/.local/share/pnpm/store \
    pnpm install --prod --frozen-lockfile

# -----------
FROM deps AS build
## downloading dev dependencies.
RUN --mount=type=bind,source=package.json,target=package.json \
    --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
    --mount=type=cache,target=/root/.local/share/pnpm/store \
    pnpm install --frozen-lockfile

COPY . .
RUN pnpm run build

# -------------
FROM base AS final
ENV NODE_ENV=production
USER node
COPY package.json .

# Copy the production dependencies from the deps stage and also
# the built application from the build stage into the image.
COPY --from=deps /usr/src/app/node_modules ./node_modules
COPY --from=build /usr/src/app/dist ./dist

EXPOSE 3000
ENTRYPOINT [ "pnpm" ]
CMD ["run", "start"]
DIRTRABAJO /usr/src/app ## instalar pnpm. EJECUTAR --mount=tipo=cache,target=/root/.npm \ instalación npm -g pnpm@${PNPM_VERSION} # ------------ DESDE los departamentos AS de la base # Descargue las dependencias como un paso independiente para aprovechar el almacenamiento en caché de Docker. # Aproveche un montaje de caché en /root/.local/share/pnpm/store para acelerar las compilaciones posteriores. # Aprovechar los montajes de enlace en package.json y pnpm-lock.yaml para evitar tener que copiarlos # en esta capa. EJECUTAR --mount=tipo=bind,fuente=paquete.json,target=paquete.json \ --mount=tipo=bind,fuente=pnpm-lock.yaml,target=pnpm-lock.yaml \ --mount=tipo=caché,target=/root/.local/share/pnpm/store \ instalación pnpm --prod --frozen-lockfile # ----------- DESDE los departamentos AS construir ## descargando dependencias de desarrollo. EJECUTAR --mount=tipo=bind,fuente=paquete.json,target=paquete.json \ --mount=tipo=bind,fuente=pnpm-lock.yaml,target=pnpm-lock.yaml \ --mount=tipo=caché,target=/root/.local/share/pnpm/store \ instalación pnpm --frozen-lockfile COPIAR . . EJECUTAR pnpm ejecutar compilación # ------------- DE base AS final ENV NODE_ENV=producción nodo USUARIO COPIAR paquete.json . # Copie las dependencias de producción de la etapa de departamentos y también # la aplicación construida desde la etapa de construcción hasta la imagen. COPIAR --from=deps /usr/src/app/node_modules ./node_modules COPIAR --from=build /usr/src/app/dist ./dist EXPONER 3000 PUNTO DE ENTRADA

CMD ["ejecutar", "iniciar"]

Ejecutar en la nube de Google

    Si deseas una alternativa gratuita para renderizar y evitar inicios en frío, debes usar Google Cloud Run. Los pasos para implementar en la nube están fuera del alcance de este artículo, pero aquí hay una breve lista de las cosas que debe hacer.
  1. Construya la imagen de la ventana acoplable a partir del archivo acoplable que se proporciona a continuación.
  2. Crear un repositorio de artefactos utilizando el servicio Google Artifact Registry.
  3. Cambie el nombre de la imagen de la ventana acoplable al formato -docker.pkg.dev//:
  4. Envía tu imagen a tu repositorio de artefactos.
  5. Implemente la imagen en Google Cloud Run. Asegúrese de establecer el mínimo de instancias activas en una para evitar arranques en frío.

Eso es todo por este tutorial.

Gracias por leer ❣️

Build and deploy a chat application using Socket.io and Redis.

Declaración de liberación Este artículo se reproduce en: https://dev.to/sammaji/build-and-deploy-a-chat-application-using-socketio-and-redis-438f?1 Si hay alguna infracción, comuníquese con Study_golang@163 .com para eliminarlo
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3