网站首页 > 技术文章 正文
前言
WebSocket协议允许客户端和服务端持久化连接,这种可以持续连接的特性使得WebScoket特别适用于游戏或者聊天室的场景,同样也适用于订单提交完以后监听状态变化进行页面刷新的场景。
在Midway中提供了对于ws模块的支持和封装,能够快速创建WebSocket服务。
服务端实现
我们采用Midway.js来实现服务端能力,首先创建一个项目:
npm init midway@latest -y
然后在项目中安装WebSocket的依赖包:
npm i @midwayjs/ws@3 --save
npm i @types/ws --save-dev
在/src/configuration.ts中开启WebSocket组件:
// src/configuration.ts
import { Configuration } from '@midwayjs/core';
import * as ws from '@midwayjs/ws';
@Configuration({
imports: [ws],
// ...
})
export class MainConfiguration {
async onReady() {
// ...
}
}
然后我们开始写接口,首先在项目目录创建socket目录:
├── package.json
├── src
│ ├── configuration.ts ## 入口配置文件
│ ├── interface.ts
│ └── socket ## ws 服务的文件
│ └── hello.controller.ts
├── test
├── bootstrap.js ## 服务启动入口
└── tsconfig.json
通过 @WSController 装饰器定义 WebSocket 服务:
import { WSController } from '@midwayjs/core';
@WSController()
export class HelloSocketController {
// ...
}
当有客户端连接时,会触发 connection 事件,我们在代码中可以使用 @OnWSConnection() 装饰器来修饰一个方法,当每个客户端第一次连接服务时,将自动调用该方法。
import { WSController, OnWSConnection, Inject } from '@midwayjs/core';
import { Context } from '@midwayjs/ws';
import * as http from 'http';
@WSController()
export class HelloSocketController {
@Inject()
ctx: Context;
@OnWSConnection()
async onConnectionMethod(socket: Context, request: http.IncomingMessage) {
console.log(`namespace / got a connection ${this.ctx.readyState}`);
}
}
WebSocket 是通过事件的监听方式来获取数据。Midway 提供了 @OnWSMessage() 装饰器来格式化接收到的事件,每次客户端发送事件,被修饰的方法都将被执行。
import { WSController, OnWSMessage, Inject } from '@midwayjs/core';
import { Context } from '@midwayjs/ws';
@WSController()
export class HelloSocketController {
@Inject()
ctx: Context;
@OnWSMessage('message')
async gotMessage(data) {
return { name: 'harry', result: parseInt(data) + 5 };
}
}
我们可以通过 @WSBroadCast 装饰器将消息发送到所有连接的客户端上。
import { WSController, OnWSConnection, Inject } from '@midwayjs/core';
import { Context } from '@midwayjs/ws';
@WSController()
export class HelloSocketController {
@Inject()
ctx: Context;
@OnWSMessage('message')
@WSBroadCast()
async gotMyMessage(data) {
return { name: 'harry', result: parseInt(data) + 5 };
}
@OnWSDisConnection()
async disconnect(id: number) {
console.log('disconnect ' + id);
}
}
至此,基础的WebSocket接口就开发好了。
- 当客户端连接时,我们会打印namespace / got a connection;
- 当客户端发送消息时,我们会返回给客户端基于入参的编号附加5的结果;
- 当客户端断开连接时,我们会打印disconnect;
最后我们在服务端开启一个WebSocket服务:
/src/config/config.default.ts
// src/config/config.default
export default {
// ...
webSocket: {
port: 3000,
},
}
接下来我们前端来调用测试下。
前端实现
前端使用react,我们建一个新页面,在useEffect中开启ws服务:
useEffect(async () => {
const ws = new WebSocket(`ws://localhost:9999`);
ws.onopen = () => {
console.log('连接成功');
ws.send(1);
};
ws.onmessage = (e) => {
console.log('服务端响应:', e);
};
ws.onclose = (e) => {
console.log('关闭连接,服务端响应:', e)
}
}, []);
这样我们在前端就可以打印出服务端返回的结果,同时服务端获取到了日志信息,前后端的WebSocket链路打通了。
前端:
开启了webSocket服务:
后端:
订单刷新
接下来我们模拟一个场景:当前端提交完订单后到了订单详情页,需要实时获取订单状态,如果订单申请通过,则展示成功信息,这里就不连接数据库了,直接在服务端维护一个对象来模拟:
import {
WSController,
Inject,
OnWSConnection,
OnWSMessage,
OnWSDisConnection,
} from '@midwayjs/core';
import { Context } from '@midwayjs/ws';
import * as http from 'http';
const orderInfo = {
status: 'pending',
id: 1,
// ...orderInfo
};
@WSController()
export class HelloSocketController {
@Inject()
ctx: Context;
// 客户端第一次连接
@OnWSConnection()
async onConnectionMethod(socket: Context, request: http.IncomingMessage) {
this.ctx.logger.info(`namespace / got a connection ${this.ctx.readyState}`);
setTimeout(() => {
orderInfo['status'] = 'success';
}, 3000);
}
// 收到客户端消息
@OnWSMessage('message')
async gotMessage() {
if (orderInfo.status === 'success') {
return { result: true };
}
return { result: false };
}
// 客户端断开连接
@OnWSDisConnection()
async disconnect(id: number) {
console.log('disconnect ' + id);
}
}
- 在后端mock一个orderInfo;
- 当WebSocket连接后,延迟三秒改变orderInfo的状态,模拟一个算法审核的时间;
- 三秒后响应给前端新状态;
前端配合改造:
useEffect(async () => {
const ws = new WebSocket(`ws://localhost:9999`);
ws.onopen = () => {
console.log('连接成功');
timer.current = setInterval(() => {
ws.send(1);
}, 1000);
};
ws.onmessage = (e) => {
console.log('服务端响应:', e);
if (JSON.parse(e.data).result) {
setOrderPass(true);
}
};
ws.onclose = (e) => {
console.log('关闭连接,服务端响应:', e);
};
}, []);
return <>订单状态:{orderPass ? '通过' : '申请中'}</>;
改造后,前端页面刷新,三秒后订单状态异步更改为通过,达到了页面监听实时刷新的效果,这样的效果是不是比传统Http定时轮询性能更优呢?
结尾
本文通过Midway0~1打通前后端ws链路,并在最后通过实际业务场景展示了ws的优势以及与传统轮询方案的差异。
Midway.js打通WebSocket前后端监听通道
原文链接:https://juejin.cn/post/7298913075544162338
猜你喜欢
- 2025-05-28 改善施乐5575系列,打印输出纸张前端、后端有粘碳粉现象
- 2025-05-28 3D打印,演绎制造新传奇
- 2025-05-28 世界最大规模3D打印混凝土步行桥落户上海科普公园
- 2025-05-28 【实战篇】数字化打印——打印部署管理接口开发
- 2025-05-28 前端实用技术分享—用Vue实现打印指定区域
- 2025-05-28 行业案例:高效打印,智能办公
- 2024-09-25 上海首座3D打印书屋亮相!可容纳15人,有地暖有天窗
- 2024-09-25 “JVM” 上的AOP:Java Agent 实战
- 2024-09-25 Python短文,关于print函数的基础用法(一)
- 2024-09-25 电子签章处理文件和打印基于ABP框架的前端项目Vue&Element
你 发表评论:
欢迎- 500℃几个Oracle空值处理函数 oracle处理null值的函数
- 494℃Oracle分析函数之Lag和Lead()使用
- 493℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 481℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 472℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 468℃【数据统计分析】详解Oracle分组函数之CUBE
- 453℃Oracle有哪些常见的函数? oracle中常用的函数
- 448℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 最近发表
-
- Spring Boot跨域难题终结者:3种方案,从此告别CORS噩梦!
- 京东大佬问我,SpringBoot为什么会出现跨域问题?如何解决?
- 在 Spring Boot3 中轻松解决接口跨域访问问题
- 最常见五种跨域解决方案(常见跨域及其解决方案)
- Java Web开发中优雅应对跨域问题(java跨域问题解决办法)
- Spring Boot解决跨域最全指南:从入门到放弃?不,到根治!
- Spring Boot跨域问题终极解决方案:3种方案彻底告别CORS错误
- Spring Cloud 轻松解决跨域,别再乱用了
- Github 太狠了,居然把 "master" 干掉了
- IntelliJ IDEA 调试 Java 8,实在太香了
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端富文本编辑器 (47)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)