"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Crie e implante um aplicativo de chat usando Socket.io e Redis.

Crie e implante um aplicativo de chat usando Socket.io e Redis.

Publicado em 2024-11-07
Navegar:892

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

Neste tutorial, construiremos um aplicativo de bate-papo usando web sockets. Os soquetes da Web são realmente úteis quando você deseja criar aplicativos que exigem transferência de dados em tempo real.

Ao final deste tutorial, você será capaz de configurar seu próprio servidor de soquete, enviar e receber mensagens em tempo real, armazenar dados no Redis e implantar seu aplicativo no render e no Google Cloud Run.

O que estaremos construindo?

Estaremos construindo um aplicativo de chat. Para ser breve, iremos apenas configurar o servidor. Você pode usar sua própria estrutura de front-end e acompanhar.

Neste aplicativo de chat, haverá salas e os usuários poderão entrar em uma sala e começar a conversar. Para manter tudo simples, assumiremos que os nomes de usuário não são exclusivos. Porém cada sala pode ter apenas um usuário com um nome de usuário específico.

Configure um servidor de soquete.

Primeiro precisamos instalar as dependências necessárias.

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

Estaremos usando o módulo http para configurar nosso servidor de soquete. Como nosso aplicativo estará rodando no terminal, teremos que permitir todas as origens.

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 o redis para armazenar nossas mensagens junto com as informações da sala e do usuário. Você pode usar upstash redis (gratuito). Crie uma nova instância redis em seu painel upstash. Após a criação, você receberá um URL redis que poderá usar para se conectar à sua instância redis.

Instale qualquer cliente redis de sua escolha. Usarei ioredis.

npm i ioredis

Em seguida, inicializaremos nosso cliente redis e o conectaremos ao nosso servidor redis usando o URL de conexão que obtivemos.

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

Tratamento de eventos.

Os usuários podem criar salas ou ingressar em salas existentes. Os quartos são identificados por IDs exclusivos. Cada membro tem um nome de usuário único dentro de uma sala, e não globalmente.

Podemos acompanhar todas as salas ativas em nosso servidor, armazenando seus IDs de sala dentro de um conjunto redis.

Para nosso propósito, os nomes de usuário são exclusivos apenas dentro de uma sala. Então, nós os armazenamos em um conjunto junto com o id da sala. Isso garante que a combinação do ID da sala com o ID do membro seja única globalmente.

Podemos configurar o evento de soquete para criar espaço. Quando criamos uma sala, também adicionamos na sala o membro que solicitou sua criação.

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 adicionar um novo membro a uma sala existente, primeiro precisamos verificar se o membro já existe naquela 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, criamos o 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)
  })
}

Implantação.

O servidor de soquete requer conexões persistentes e não funciona em ambientes sem servidor. Portanto, você não pode implantar seu servidor de soquete no vercel.

Você pode implantá-lo em vários lugares, como Render, fly.io ou Google Cloud Run.

Renderizar

Implantação em renderização simples. Se você tiver um dockerfile, ele criará automaticamente seu projeto a partir desse dockerfile. A renderização tem um nível gratuito, mas lembre-se de que haverá inicialização a frio no nível gratuito.

Aqui está meu arquivo 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"]
WORKDIR /usr/src/app ## instale o pnpm. EXECUTAR --mount=type=cache,target=/root/.npm \ instalação npm -g pnpm@${PNPM_VERSION} # ------------ DA base AS deps # Baixe as dependências como uma etapa separada para aproveitar as vantagens do cache do Docker. # Aproveite uma montagem de cache em /root/.local/share/pnpm/store para acelerar compilações subsequentes. # Aproveite montagens de ligação para package.json e pnpm-lock.yaml para evitar ter que copiá-los # nesta camada. EXECUTAR --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 \ instalação pnpm --prod --frozen-lockfile # ----------- DE deps AS build ## baixando dependências de desenvolvimento. EXECUTAR --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 \ instalação pnpm --frozen-lockfile CÓPIA. . EXECUTAR compilação de execução pnpm # ------------- DA base COMO final ENV NODE_ENV=produção Nó USUÁRIO COPIE pacote.json . # Copie as dependências de produção do estágio deps e também # o aplicativo construído desde o estágio de construção até a imagem. COPIAR --from=deps /usr/src/app/node_modules ./node_modules COPIAR --from=build /usr/src/app/dist ./dist EXPOSIÇÃO 3000 PONTO DE ENTRADA ["pnpm"] CMD ["executar", "iniciar"]

Google Cloud Run

Se você deseja uma alternativa gratuita para renderizar e evitar inicializações a frio, você deve usar o Google Cloud Run. As etapas para implantar na nuvem estão além do escopo deste artigo, mas aqui está uma pequena lista de coisas que você precisa fazer.
  1. Crie sua imagem do Docker a partir do arquivo docker fornecido abaixo.
  2. Crie um repositório de artefatos usando o serviço Google Artifact Registry.
  3. Renomeie sua imagem docker para o formato -docker.pkg.dev//:
  4. Envie sua imagem para seu repositório de artefatos.
  5. Implante a imagem no Google Cloud Run. Certifique-se de definir o mínimo de instâncias ativas como uma, para evitar inicializações a frio.

Este tutorial é isso.

Obrigado por ler ❣️

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/sammaji/build-and-deploy-a-chat-application-using-socketio-and-redis-438f?1 Se houver alguma violação, entre em contato com study_golang@163 .com para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3