في هذا البرنامج التعليمي، سنقوم ببناء تطبيق دردشة باستخدام مقابس الويب. تعتبر مآخذ الويب مفيدة حقًا عندما تريد إنشاء تطبيقات تتطلب نقل البيانات في الوقت الفعلي.
بحلول نهاية هذا البرنامج التعليمي، ستكون قادرًا على إعداد خادم المقبس الخاص بك، وإرسال واستقبال الرسائل في الوقت الفعلي، وتخزين البيانات في 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. إن خطوات النشر على التشغيل السحابي تقع خارج نطاق هذه المقالة، ولكن إليك قائمة قصيرة بالأشياء التي يتعين عليك القيام بها.
أنشئ صورة عامل الإرساء الخاصة بك من ملف عامل الإرساء المتوفر أدناه.
هذا كل ما في هذا البرنامج التعليمي.
شكرًا على القراءة ❣️
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3