feat: 在线双人五子棋(Socket.IO + Vue3 + Canvas)
- 服务端权威状态机:落子校验、四方向五连判胜、悔棋请求、认输、离线判负、交换黑白再来一局 - 断线重连:resumeToken 凭证 + localStorage,刷新/锁屏自动回到原座位 - 移动端 Canvas 棋盘:DPR 适配、触摸容差吸附、最后一手标记、胜利连子高亮 - Docker 多阶段构建,非 root + 只读根文件系统 - k8s 清单:单副本 Recreate(内存态房态)、Ingress WebSocket 超时与粘性会话注释
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# 本机镜像导入集群的两种方式(无镜像仓库场景):
|
||||
#
|
||||
# 方式 A:集群就是执行 docker build 的这台机器(单节点或每台节点都构建)
|
||||
# docker save wuziqi:0.1.0 -o wuziqi-0.1.0.tar
|
||||
# ctr -n k8s.io images import wuziqi-0.1.0.tar
|
||||
#
|
||||
# 方式 B:集群在别的机器上
|
||||
# docker save wuziqi:0.1.0 -o wuziqi-0.1.0.tar
|
||||
# scp wuziqi-0.1.0.tar <node>:~/
|
||||
# 在【每台可能被调度到的节点】上执行:ctr -n k8s.io images import ~/wuziqi-0.1.0.tar
|
||||
#
|
||||
# 导入后配合 imagePullPolicy: IfNotPresent 即可直接启动,无需任何 registry。
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: wuziqi
|
||||
namespace: wuziqi
|
||||
labels:
|
||||
app: wuziqi
|
||||
spec:
|
||||
# 【必须保持为 1】
|
||||
# 房间与棋局状态存放在进程内存(RoomManager),多副本会各自持有一份状态:
|
||||
# 两名玩家被 Ingress 分流到不同 Pod 时,第二个玩家会看到"房间不存在"。
|
||||
# 若确实需要多副本,必须引入 Redis + Socket.IO Redis Adapter,并把房间状态外置。
|
||||
replicas: 1
|
||||
strategy:
|
||||
# Recreate:先终止旧 Pod 再创建新 Pod。
|
||||
# 默认的 RollingUpdate 会让新旧两个 Pod 短暂并存,此时新 Pod 内存里没有旧 Pod 的房间,
|
||||
# 进行中的对局会"房间丢失"。
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: wuziqi
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: wuziqi
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
fsGroup: 1000
|
||||
# 内存态服务,收到 SIGTERM 后推完最后一次状态即可退出
|
||||
terminationGracePeriodSeconds: 15
|
||||
containers:
|
||||
- name: wuziqi
|
||||
image: wuziqi:0.1.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 3000
|
||||
env:
|
||||
- name: PORT
|
||||
value: "3000"
|
||||
- name: NODE_ENV
|
||||
value: production
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 20m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
# 进程不需要写文件系统,静态资源与构建产物都是只读的
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
@@ -0,0 +1,55 @@
|
||||
# 部署前需要改两处:
|
||||
# 1. spec.ingressClassName —— 用 `kubectl get ingressclass` 确认集群里实际的 class 名
|
||||
# 2. spec.rules[].host 与 spec.tls[].hosts —— 换成你自己的域名
|
||||
#
|
||||
# 证书二选一:
|
||||
# A) cert-manager 自动签发:在 annotations 里加
|
||||
# cert-manager.io/cluster-issuer: <你的 ClusterIssuer 名>
|
||||
# B) 手动导入已有证书:
|
||||
# kubectl -n wuziqi create secret tls wuziqi-tls \
|
||||
# --cert=fullchain.pem --key=privkey.pem
|
||||
# 没有域名时删掉整段 spec.tls,并去掉 annotations 里的 force-ssl-redirect(若有),
|
||||
# 改为纯 HTTP 访问;Socket.IO 在 HTTP 下也能工作,但手机网络上不如 wss 稳定。
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: wuziqi
|
||||
namespace: wuziqi
|
||||
labels:
|
||||
app: wuziqi
|
||||
annotations:
|
||||
# —— Socket.IO 长连接的关键三项 ——
|
||||
# 默认 proxy-read-timeout 只有 60s,长连接会被 Ingress 直接掐断,
|
||||
# 表现为手机每隔一分钟掉线一次。心跳间隔 25s,这里放宽到 1 小时。
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
# 关闭缓冲,避免事件被攒着不发导致落子延迟
|
||||
nginx.ingress.kubernetes.io/proxy-buffering: "off"
|
||||
# 显式声明 WebSocket Upgrade 的目标 Service
|
||||
nginx.ingress.kubernetes.io/websocket-services: "wuziqi"
|
||||
|
||||
# —— 会话亲和性 ——
|
||||
# 客户端 transports 为 [websocket, polling],WebSocket 不通时会回退到长轮询。
|
||||
# 长轮询的握手与后续请求必须落到同一 Pod,否则会话丢失。
|
||||
# replicas=1 时这几项是空操作,但一旦调大副本数就是必需的。
|
||||
nginx.ingress.kubernetes.io/affinity: "cookie"
|
||||
nginx.ingress.kubernetes.io/session-cookie-name: "wuziqi-affinity"
|
||||
nginx.ingress.kubernetes.io/session-cookie-max-age: "3600"
|
||||
nginx.ingress.kubernetes.io/session-cookie-samesite: "Lax"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- gomoku.example.com
|
||||
secretName: wuziqi-tls
|
||||
rules:
|
||||
- host: gomoku.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: wuziqi
|
||||
port:
|
||||
number: 80
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: wuziqi
|
||||
labels:
|
||||
app: wuziqi
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: wuziqi
|
||||
namespace: wuziqi
|
||||
labels:
|
||||
app: wuziqi
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: wuziqi
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: http
|
||||
Reference in New Issue
Block a user