放弃云数据库!基于NestJS的WebSocket实时通信方案实践

背景

在开发3D骰子联机小程序时,多人游戏功能需要实时同步房间的操作状态(如房主开盅动作)。初期采用小程序云开发数据库的watch监听机制实现状态同步,虽能完成基础功能,但存在致命体验短板------云函数响应延迟高达1-2秒,与骰子游戏强实时的特性产生明显冲突。

于是为追求更丝滑的实时交互的用户体验,决定选择通过 WebSocket 重构通信层,下文将详解一下通过 nest 实现与小程序实时通信的方案。

实现

首先我们通过 nest 的脚手架来初始化一下项目

arduino 复制代码
nest new websocket-demo

运行一下项目,启动成功

接着我们安装对应的所需的websocket的依赖

css 复制代码
npm i --save @nestjs/websockets @nestjs/platform-ws ws

最开始用的 socket.io 依赖实现,结果后面发现小程序不支持 socket.io ,需要下载一些兼容库来结合使用,于是就换了方案。

接下来我们需要为应用注入 WebSocket 适配器,在main.ts中配置一下 主要作用

  • 将传统的 HTTP 服务扩展为 同时支持 HTTP + WebSocket 双协议
  • 通过适配器模式,让 WebSocket 的底层实现(如 wssocket.io 库)与框架的路由、中间件等系统无缝对接。
js 复制代码
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { WsAdapter } from '@nestjs/platform-ws';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useWebSocketAdapter(new WsAdapter(app));
  await app.listen(3000);
}
bootstrap();

接下来需要配置一下 WebSocket 网关

js 复制代码
import { SubscribeMessage, WebSocketGateway, WebSocketServer, OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';
import { Server, WebSocket } from 'ws';

@WebSocketGateway(3001) // 指定ws端口号,不配置则默认与http服务相同端口
export class WsGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer()
  server: Server;

  // 初始化后执行
  afterInit(server: Server) {
    console.log('WebSocket server initialized');
  }

  // 处理新连接
  handleConnection(client: WebSocket, ...args: any[]) {
    console.log('Client connected:');
  }

  // 处理断开连接
  handleDisconnect(client: WebSocket) {
    console.log(`Client disconnected: ${client}`);
  }

  // 消息处理示例
  @SubscribeMessage('message') // 这里的message对应的是前端发送ws请求的里的event值
  handleMessage(client: WebSocket, payload: any): string {
    console.log('handleMessage message:', payload);
    client.send(JSON.stringify({ event: 'message', data: `我收到了你发的消息${payload}` })); // 回发消息
    return 'OK';
  }
}

还需要在 app.module.ts 中引入 socket 网关

js 复制代码
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { WsGateway } from './ws.gateway';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, WsGateway], // 这里
})
export class AppModule {}

我们重新跑一下后端服务

然后在小程序中的app.vue中的 onLaunch 写连接本地 WebSocket 服务的代码

js 复制代码
const conSocket = wx.connectSocket({
  url: 'ws://localhost:3001',
  success: () => console.log('Connecting...'),
})

// 监听事件
conSocket.onOpen(() => {
  console.log('Connected!');
});

conSocket.onMessage((res: any) => {
  const { data } = res
  console.log('onMessage-msg', data);
});

conSocket.onError((err) => {
  console.error('Error:', err);
});

conSocket.onClose(() => {
  console.log('Connection closed');
});

小程序项目运行起来

发现连接成功了!!!

接下来我来试一下双向通信,在连接打开的时候,我们给服务端发送点内容

js 复制代码
conSocket.onOpen(() => {
    console.log('Connected!');
    conSocket.send({
      data: JSON.stringify({
        event: 'message',
        data: {
          msg: 'Hello, server!'
        }
      }),
      success: () => console.log('Message sent')
    })
  });

可以看到服务器与小程序之间的双向通信没问题,并且响应很快,非常符合我们的联机功能。

✨ 总结

  • 旧方案:云数据库watch监听

    • 延迟1-2秒,体验割裂
    • 被动轮询,资源消耗大
  • 新方案:NestJS+WebSocket

    • 延迟<100ms,毫秒级响应
    • 主动推送,节省资源

对于前端来说,用户体验极其重要,我自己在开发过程中,都会让自己从用户的角度,来体验流程,这也有助于自己去思考,去进行一些性能优化。

相关推荐
米尔的可达鸭7 小时前
UDP 通信深度实验:从 Windows FastPath 到 ICMP 端口不可达的完整排查
windows·websocket·网络协议·rust·udp·wireshark·tcpdump
鲁Q同志7 小时前
微信小程序【uni-file-picker文件预览】
微信小程序·小程序
2601_963869957 小时前
【计算机毕业设计】基于微信小程序的拼车服务系统设计与实现
微信小程序·小程序·课程设计
dota李天王1 天前
简单的 socket 实现
前端·javascript·websocket
她说可以呀1 天前
WebSocket 和 SSE 简单使用及区别
网络·websocket·网络协议
weixin_462901971 天前
TCP+WebSocket双向转发服务器 完整部署运维手册
运维·websocket·tcp/ip
虚惊一场1 天前
撤销为什么只需要 pop:用事件重放实现离线台球计分
微信小程序·uni-app
虚惊一场1 天前
从一个小程序到一座工具箱:uni-app 分包与 Flask 微服务实践
微信小程序·uni-app
完美火龙篇 四月的友1 天前
SpringBoot 即时聊天 IM 完整实现(HTTP会话管理 \+ WebSocket实时推送 \+ 离线消息)
spring boot·websocket·http
beichenxingzhui1 天前
茶饮品牌“新品口感盲测问答”:适合微信小程序与互动页的工具 Top 3
微信小程序·小程序