"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > إنشاء تطبيق دردشة ونشره باستخدام Jack.io وRedis.

إنشاء تطبيق دردشة ونشره باستخدام Jack.io وRedis.

تم النشر بتاريخ 2024-11-07
تصفح:476

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

في هذا البرنامج التعليمي، سنقوم ببناء تطبيق دردشة باستخدام مقابس الويب. تعتبر مآخذ الويب مفيدة حقًا عندما تريد إنشاء تطبيقات تتطلب نقل البيانات في الوقت الفعلي.

بحلول نهاية هذا البرنامج التعليمي، ستكون قادرًا على إعداد خادم المقبس الخاص بك، وإرسال واستقبال الرسائل في الوقت الفعلي، وتخزين البيانات في Redis، ونشر تطبيقك على العرض وتشغيل Google Cloud.

ماذا سوف نبني؟

سنقوم ببناء تطبيق الدردشة. باختصار، سنقوم فقط بإعداد الخادم. يمكنك استخدام إطار الواجهة الأمامية الخاص بك والمتابعة.

في تطبيق الدردشة هذا، ستكون هناك غرف ويمكن للمستخدمين الانضمام إلى غرفة وبدء الدردشة. ولتبسيط الأمور، سنفترض أن أسماء المستخدمين ليست فريدة. ومع ذلك، يمكن أن تحتوي كل غرفة على مستخدم واحد فقط باسم مستخدم محدد.

إعداد خادم مأخذ التوصيل.

أولاً نحتاج إلى تثبيت التبعيات المطلوبة.

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

سنستخدم وحدة http لإعداد خادم المقبس الخاص بنا. نظرًا لأن تطبيقنا سيتم تشغيله في المحطة، فسيتعين علينا السماح بجميع الأصول.

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 لتخزين رسائلنا بالإضافة إلى معلومات الغرفة والمستخدم. يمكنك استخدام upstash redis (مجاني). قم بإنشاء مثيل redis جديد في لوحة معلومات upstash الخاصة بك. بعد الإنشاء، ستتلقى عنوان URL الخاص بـ redis والذي يمكنك استخدامه للاتصال بمثيل redis الخاص بك.

قم بتثبيت أي عميل redis من اختيارك. سأستخدم إيوريديس.

npm i ioredis

بعد ذلك، سنقوم بتهيئة عميل redis الخاص بنا وتوصيله بخادم redis الخاص بنا باستخدام عنوان url للاتصال الذي حصلنا عليه.

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

التعامل مع الأحداث.

يمكن للمستخدمين إنشاء غرف أو الانضمام إلى الغرف الموجودة. يتم تحديد الغرف بواسطة معرفات فريدة للغرفة. كل عضو لديه اسم مستخدم فريد داخل الغرفة، وليس عالميًا.

يمكننا تتبع جميع الغرف النشطة في الخادم الخاص بنا، عن طريق تخزين معرفات غرفهم داخل مجموعة redis.

لأغراضنا، تكون أسماء المستخدمين فريدة داخل الغرفة فقط. لذلك، نقوم بتخزينها في مجموعة مع معرف الغرفة. وهذا يضمن أن يكون الجمع بين معرف الغرفة ومعرف العضو فريدًا عالميًا.

يمكننا إعداد حدث مأخذ التوصيل لإنشاء غرفة. عندما نقوم بإنشاء غرفة، نقوم أيضًا بإضافة العضو الذي طلب إنشائها إلى الغرفة.

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

لإضافة عضو جديد إلى غرفة موجودة، نحتاج أولاً إلى التحقق مما إذا كان العضو موجودًا بالفعل في تلك الغرفة.

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

أخيرًا، نقوم بإنشاء حدث الدردشة.

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

النشر.

يتطلب خادم المقبس اتصالات مستمرة، ولن يعمل في البيئات التي لا تحتوي على خادم. لذلك لا يمكنك نشر خادم المقبس الخاص بك في vercel.

يمكنك نشره في العديد من الأماكن مثل Render أو fly.io أو Google Cloud Run.

يجعل

النشر يصبح بسيطًا. إذا كان لديك ملف إرساء، فسيقوم تلقائيًا ببناء مشروعك من ملف الإرساء هذا. يحتوي Render على طبقة مجانية، ولكن ضع في اعتبارك أنه ستكون هناك بداية باردة في الطبقة المجانية.

هذا هو ملف الإرساء الخاص بي.

# 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 ## تثبيت pnpm. RUN --mount=type=cache,target=/root/.npm \ تثبيت npm -g pnpm@${PNPM_VERSION} # ------------ من القاعدة AS deps # قم بتنزيل التبعيات كخطوة منفصلة للاستفادة من التخزين المؤقت لـ Docker. # استخدم ذاكرة التخزين المؤقت المحملة على /root/.local/share/pnpm/store لتسريع عمليات الإنشاء اللاحقة. # ربط الرافعة المالية بـ package.json و pnpm-lock.yaml لتجنب الاضطرار إلى نسخها # في هذه الطبقة 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 --prod --frozen-lockfile # ----------- من Deps AS build ## تنزيل تبعيات التطوير. 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 --frozen-lockfile ينسخ . . RUN pnpm تشغيل البناء # ------------- من القاعدة كنهائي ENV NODE_ENV=الإنتاج عقدة المستخدم انسخ package.json . # انسخ تبعيات الإنتاج من مرحلة الإقلاع وأيضا # التطبيق المدمج من مرحلة البناء إلى الصورة. نسخ --from=deps /usr/src/app/node_modules ./node_modules نسخ --from=build /usr/src/app/dist ./dist فضح 3000 انتري بوينت ["pnpm"] CMD ["تشغيل"، "بدء"]

تشغيل جوجل السحابي

إذا كنت تريد بديلاً مجانيًا للعرض وتجنب عمليات التشغيل الباردة، فيجب عليك استخدام Google Cloud Run. إن خطوات النشر على التشغيل السحابي تقع خارج نطاق هذه المقالة، ولكن إليك قائمة قصيرة بالأشياء التي يتعين عليك القيام بها.
  1. أنشئ صورة عامل الإرساء الخاصة بك من ملف عامل الإرساء المتوفر أدناه.
  2. قم بإنشاء مستودع للقطع الأثرية باستخدام خدمة Google Artifact Registry.
  3. أعد تسمية صورة عامل الإرساء الخاصة بك إلى التنسيق -docker.pkg.dev//:
  4. ادفع صورتك إلى مستودع القطع الأثرية الخاص بك.
  5. انشر الصورة على Google Cloud Run. تأكد من تعيين الحد الأدنى من المثيلات النشطة إلى واحد، لتجنب البدء البارد.

هذا كل ما في هذا البرنامج التعليمي.

شكرًا على القراءة ❣️

بيان الافراج تم إعادة إنتاج هذه المقالة على: https://dev.to/sammaji/build-and-deploy-a-chat-application-using-socketio-and-redis-438f?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ Study_golang@163 .com لحذفه
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3