„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Erstellen und implementieren Sie eine Chat-Anwendung mit Socket.io und Redis.

Erstellen und implementieren Sie eine Chat-Anwendung mit Socket.io und Redis.

Veröffentlicht am 07.11.2024
Durchsuche:701

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

In diesem Tutorial erstellen wir eine Chat-Anwendung mithilfe von Web-Sockets. Web-Sockets sind wirklich nützlich, wenn Sie Anwendungen erstellen möchten, die eine Echtzeitübertragung von Daten erfordern.

Am Ende dieses Tutorials werden Sie in der Lage sein, Ihren eigenen Socket-Server einzurichten, Nachrichten in Echtzeit zu senden und zu empfangen, Daten in Redis zu speichern und Ihre Anwendung auf Render und Google Cloud Run bereitzustellen.

Was werden wir bauen?

Wir werden eine Chat-Anwendung erstellen. Um es kurz zu machen: Wir werden nur den Server einrichten. Sie können Ihr eigenes Front-End-Framework verwenden und mitmachen.

In dieser Chat-Anwendung gibt es Räume und Benutzer können einem Raum beitreten und mit dem Chatten beginnen. Der Einfachheit halber gehen wir davon aus, dass die Benutzernamen nicht eindeutig sind. Allerdings kann jeder Raum nur einen Benutzer mit einem bestimmten Benutzernamen haben.

Richten Sie einen Socket-Server ein.

Zuerst müssen wir die erforderlichen Abhängigkeiten installieren.

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

Wir werden das http-Modul verwenden, um unseren Socket-Server einzurichten. Da unsere App im Terminal ausgeführt wird, müssen wir alle Ursprünge zulassen.

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

Redis einrichten.

Wir werden Redis verwenden, um unsere Nachrichten zusammen mit Raum- und Benutzerinformationen zu speichern. Sie können Upstash Redis (kostenlos) verwenden. Erstellen Sie eine neue Redis-Instanz in Ihrem Upstash-Dashboard. Nach der Erstellung erhalten Sie eine Redis-URL, mit der Sie eine Verbindung zu Ihrer Redis-Instanz herstellen können.

Installieren Sie einen beliebigen Redis-Client Ihrer Wahl. Ich werde ioredis verwenden.

npm i ioredis

Als nächstes werden wir unseren Redis-Client initialisieren und ihn über die Verbindungs-URL, die wir erhalten haben, mit unserem Redis-Server verbinden.

/** /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)

Umgang mit Ereignissen.

Benutzer können Räume erstellen oder bestehenden Räumen beitreten. Räume werden durch eindeutige Raum-IDs identifiziert. Jedes Mitglied hat einen Benutzernamen, der innerhalb eines Raums und nicht global eindeutig ist.

Wir können alle aktiven Räume auf unserem Server verfolgen, indem wir ihre Raum-IDs in einem Redis-Set speichern.

Für unseren Zweck sind Benutzernamen nur innerhalb eines Raums eindeutig. Deshalb bewahren wir sie zusammen mit der Raum-ID in einem Set auf. Dadurch wird sichergestellt, dass die Kombination aus Raum-ID und Mitglieds-ID weltweit eindeutig ist.

Wir können ein Socket-Ereignis einrichten, um Raum zu schaffen. Wenn wir einen Raum erstellen, fügen wir dem Raum auch das Mitglied hinzu, das die Erstellung angefordert hat.

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

Um ein neues Mitglied zu einem bestehenden Raum hinzuzufügen, müssen wir zunächst prüfen, ob das Mitglied bereits in diesem Raum existiert.

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

Zuletzt erstellen wir das Chat-Event.

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

Einsatz.

Socket-Server erfordert dauerhafte Verbindungen, er funktioniert nicht in serverlosen Umgebungen. Sie können Ihren Socket-Server also nicht in Vercel bereitstellen.

Sie können es an vielen Orten wie Render, fly.io oder Google Cloud Run bereitstellen.

Machen

Bereitstellung beim Rendern einfach. Wenn Sie über eine Docker-Datei verfügen, wird Ihr Projekt automatisch aus dieser Docker-Datei erstellt. Render hat eine kostenlose Stufe, aber bedenken Sie, dass es in der kostenlosen Stufe einen Kaltstart gibt.

Hier ist meine Docker-Datei.

# 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"]
ARBEITSVERZEICHNIS /usr/src/app ## pnpm installieren. RUN --mount=type=cache,target=/root/.npm \ npm install -g pnpm@${PNPM_VERSION} # ------------ VON der Basis AS abh # Laden Sie Abhängigkeiten als separaten Schritt herunter, um das Caching von Docker zu nutzen. # Nutzen Sie einen Cache-Mount in /root/.local/share/pnpm/store, um nachfolgende Builds zu beschleunigen. # Nutzen Sie Bind-Mounts für package.json und pnpm-lock.yaml, um sie nicht kopieren zu müssen # in diese Ebene. 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 # ----------- VON abhängig AS Build ## Entwicklungsabhängigkeiten werden heruntergeladen. 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 KOPIEREN. . RUN pnpm run build # ------------- VON der Basis AS endgültig ENV NODE_ENV=Produktion USER-Knoten KOPIEREN Sie package.json . # Kopieren Sie die Produktionsabhängigkeiten aus der Deps-Phase und auch # die erstellte Anwendung von der Build-Phase in das Image. COPY --from=deps /usr/src/app/node_modules ./node_modules COPY --from=build /usr/src/app/dist ./dist 3000 aussetzen ENTRYPOINT

CMD ["run", "start"]

Google Cloud Run

    Wenn Sie eine kostenlose Alternative zum Rendern und Vermeiden von Kaltstarts wünschen, sollten Sie Google Cloud Run verwenden. Die Schritte zur Bereitstellung in der Cloud gehen über den Rahmen dieses Artikels hinaus, aber hier ist eine kurze Liste der Dinge, die Sie tun müssen.
  1. Erstellen Sie Ihr Docker-Image aus der unten bereitgestellten Docker-Datei.
  2. Erstellen Sie ein Artefakt-Repository mit dem Google Artifact Registry-Dienst.
  3. Benennen Sie Ihr Docker-Image in das Format -docker.pkg.dev//: um
  4. Übertragen Sie Ihr Bild in Ihr Artefakt-Repository.
  5. Stellen Sie das Image in Google Cloud Run bereit. Stellen Sie sicher, dass die Mindestanzahl aktiver Instanzen auf eins festgelegt ist, um Kaltstarts zu vermeiden.

Das ist alles für dieses Tutorial.

Danke fürs Lesen ❣️

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

Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/sammaji/build-and-deploy-a-chat-application-using-socketio-and-redis-438f?1 Bei Verstößen wenden Sie sich bitte an Study_golang@163 .com, um es zu löschen
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3