Cá nhânĐọc khoảng 2 phút
Game Lô Tô trực tuyến
Game lô tô nhiều người chơi thời gian thực, đóng gói bằng Docker và deploy lên server Armbian cá nhân.
- TypeScript
- Next.js
- Tailwind CSS
- Docker
Game lô tô (bingo) nhiều người chơi, viết bằng Next.js và đóng gói trong container để chạy trên server Armbian cá nhân của tôi.
Vì sao lại là lô tô#
Tôi cần một bài toán thời gian thực có trạng thái dùng chung đủ nhỏ để hoàn thành, nhưng đủ thật để gặp các vấn đề đồng bộ: nhiều người xem cùng một bộ số, người vào muộn phải thấy đúng trạng thái hiện tại, và không ai được bỏ lỡ một lần gọi số.
Trạng thái nằm ở server#
Sai lầm dễ mắc nhất là để mỗi client tự sinh số rồi đồng bộ với nhau. Ở đây, server là nguồn chân lý duy nhất; client chỉ nhận và hiển thị.
interface Room {
id: string;
/* Toàn bộ số đã gọi, theo thứ tự. Client vào muộn phát lại từ mảng này. */
called: number[];
remaining: number[];
startedAt: number;
}
export function callNext(room: Room): number | null {
if (room.remaining.length === 0) return null;
/* Rút ngẫu nhiên bằng cách hoán vị phần tử cuối: O(1), không lệch phân phối. */
const index = Math.floor(Math.random() * room.remaining.length);
const [value] = room.remaining.splice(index, 1);
room.called.push(value);
return value;
}Đóng gói và deploy#
Toàn bộ game chạy trong một container duy nhất, dùng standalone output của Next.js để image Docker giữ ở mức nhỏ:
FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
RUN corepack enable pnpm && pnpm install --frozen-lockfile && pnpm build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# standalone gói sẵn chỉ những dependency thực sự được dùng
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]Kết quả#
- Trạng thái phòng nhất quán cho mọi người chơi, kể cả khi vào giữa ván
- Image Docker gọn, chạy được trên board ARM cấu hình thấp
- Không cần database: trạng thái phòng sống trong bộ nhớ theo vòng đời ván chơi