websocket的封装,面对后端为服务架构
// websocket.js
import Vue from 'vue';
class WebSocketService {
constructor() {
this.socket = null;
this.state = Vue.observable({
isConnected: false,
currentUrl: '',
retries: 0,
maxRetries: 5,
reconnectInterval: 3000
});
this.timeoutId = null;
this.onMessageCallback = null; // 用户自定义的 onmessage 处理函数
this.onCloseCallback = null; // 用户自定义的 onclose 处理函数
}
// 配置不同微服务的 WebSocket URL
serviceUrls = {
serviceA: 'ws://serviceA-url',
serviceB: 'ws://serviceB-url',
serviceC: 'ws://serviceC-url'
};
// 初始化 WebSocket 连接
init(serviceName, onMessageCallback, onCloseCallback) {
const url = this.serviceUrls[serviceName];
if (!url) {
console.error(`WebSocket URL for ${serviceName} is required`);
return;
}
if (this.state.isConnected) {
console.log('WebSocket already connected');
return;
}
this.state.currentUrl = url;
this.socket = new WebSocket(url);
// 设置连接超时(10秒)
this.timeoutId = setTimeout(() => {
if (!this.state.isConnected) {
this.close();
console.warn('WebSocket connection timed out');
}
}, 10000);
// 保存用户传递的回调函数
this.onMessageCallback = onMessageCallback;
this.onCloseCallback = onCloseCallback;
// 监听 WebSocket 事件
this.socket.onopen = () => {
clearTimeout(this.timeoutId);
this.state.isConnected = true;
this.state.retries = 0;
console.log('WebSocket connection opened');
};
this.socket.onmessage = (event) => {
if (this.onMessageCallback) {
this.onMessageCallback(event); // 调用自定义的消息处理函数
} else {
console.log('Message received:', event.data);
}
};
this.socket.onerror = (error) => {
console.error('WebSocket error:', error);
this.reconnect(serviceName);
};
this.socket.onclose = () => {
this.state.isConnected = false;
console.log('WebSocket connection closed');
if (this.onCloseCallback) {
this.onCloseCallback(); // 调用自定义的关闭处理函数
}
this.reconnect(serviceName);
};
}
// 发送消息
sendMessage(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(message);
} else {
console.warn('WebSocket is not open');
}
}
// 切换服务 URL
changeService(serviceName, onMessageCallback, onCloseCallback) {
if (this.socket) {
this.close();
}
this.init(serviceName, onMessageCallback, onCloseCallback);
}
// 关闭 WebSocket 连接
close() {
if (this.socket) {
this.socket.close();
}
clearTimeout(this.timeoutId);
}
// 重连机制
reconnect(serviceName) {
if (this.state.retries < this.state.maxRetries) {
this.state.retries += 1;
console.log(`Attempting to reconnect... (${this.state.retries}/${this.state.maxRetries})`);
setTimeout(() => this.init(serviceName, this.onMessageCallback, this.onCloseCallback), this.state.reconnectInterval);
} else {
console.error('Max retries reached, giving up on connection');
}
}
get isConnected() {
return this.state.isConnected;
}
get currentUrl() {
return this.state.currentUrl;
}
}
// 创建单例
const webSocketService = new WebSocketService();
export default {
install(Vue) {
Vue.prototype.$webSocket = webSocketService;
}
};
使用示例
在组件中调用 init
方法时,可以传入自定义的 onmessage
和 onclose
回调:
export default {
mounted() {
// 连接到 serviceA 的 WebSocket,并设置自定义的消息和关闭处理函数
this.$webSocket.init('serviceA', this.handleMessage, this.handleClose);
},
methods: {
handleMessage(event) {
console.log('Custom message handler:', event.data);
// 在这里处理接收到的消息
},
handleClose() {
console.log('Custom close handler: WebSocket connection closed');
// 在这里处理连接关闭后的操作
},
sendMessage() {
this.$webSocket.sendMessage('Hello WebSocket');
},
changeToServiceB() {
// 切换到 serviceB 的 WebSocket,并传递新的回调
this.$webSocket.changeService('serviceB', this.handleMessage, this.handleClose);
}
},
beforeDestroy() {
this.$webSocket.close();
}
};
这种方式使得前端在处理 onmessage
和 onclose
时有更大的灵活性,可以根据实际需求自定义事件处理逻辑,而不是在插件内部固定处理。
使用场景
在 WebSocket 使用完之后就立即关闭,还是在页面离开时才关闭,取决于应用的具体需求。以下是两种情况的分析:
1. 使用完即关闭
- 适用场景:WebSocket 仅在特定交互中需要,例如某个功能的实时数据流,在用户完成操作后就不再需要继续保持连接。
- 优点:节省资源,因为 WebSocket 连接在不使用时即被关闭,避免了长时间空闲占用服务器连接。
- 缺点:如果用户频繁进入和离开此功能模块,可能会多次重新连接,导致性能开销增大。
适合的应用场景:像股票行情、天气更新等功能,在用户切换页面或完成实时数据查询时,不再需要保持实时连接,使用完后立即关闭更为合理。
2. 页面离开时关闭
- 适用场景:需要在整个页面会话中保持实时连接的数据,例如消息通知、实时聊天、后台数据监控等。
- 优点:可以避免重复连接开销,用户在同一页面会话中始终保持实时数据的推送,提升用户体验。
- 缺点:在页面空闲时仍然会占用连接资源,可能会影响性能,尤其是在大规模连接时。
适合的应用场景:例如聊天应用、实时监控平台等,页面保持实时更新更重要,用户在页面切换或关闭页面时才关闭连接更为合理。
综合建议
可以根据业务需求选择合适的方式,或结合两者,例如:
- 按模块保持连接:在模块页面生命周期内保持连接,用户离开模块或页面时关闭 WebSocket。
- 自动超时关闭:在 WebSocket 长时间未使用时,自动关闭以节省资源,并在需要时重新连接。
这种方式兼顾了资源使用和性能,且不影响用户体验。
ngixn做处理
Nginx 可以一定程度上控制 WebSocket 连接的生命周期,但它的控制范围较为有限。主要可以通过设置超时、连接数限制等方式来管理 WebSocket,但无法直接在前端实现“使用完即关闭”这样的逻辑。以下是 Nginx 在 WebSocket 控制方面的可行方式:
1. 超时设置
Nginx 提供超时配置,可以设置 WebSocket 在空闲一定时间后自动断开。常用的超时设置有:
proxy_read_timeout
:指定 Nginx 在等待后端响应的最大时间。如果后端在指定时间内没有数据传输,Nginx 将关闭连接。proxy_send_timeout
:指定 Nginx 向后端发送请求的超时时间,类似地,如果超时则关闭连接。proxy_connect_timeout
:用于连接后端服务器的超时时间,如果连接超时则关闭连接。
location /websocket/ {
proxy_pass http://backend_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
# 设置超时
proxy_read_timeout 60s; # 60秒内无数据传输则关闭
proxy_send_timeout 60s;
proxy_connect_timeout 10s;
}
这种超时机制适合控制空闲连接,但如果连接始终有数据传输,这些超时设置不会主动关闭 WebSocket。
2. 限制 WebSocket 连接数
可以通过 Nginx 限制每个客户端的最大连接数,避免同一个用户产生过多的 WebSocket 连接:
http {
# 定义一个限制连接的 zone
limit_conn_zone $binary_remote_addr zone=ws_limit:10m;
server {
location /websocket/ {
limit_conn ws_limit 10; # 每个客户端的最大连接数为 10
proxy_pass http://backend_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
}
这可以有效防止单个用户产生大量连接,导致服务器资源浪费。
3. Nginx 配合应用层的自动断开策略
Nginx 还可以结合应用层设置,比如:
- 当连接空闲或达到指定的业务条件时,前端可以通过应用逻辑通知后端关闭 WebSocket,Nginx 通过
proxy_read_timeout
触发连接超时来响应关闭。 - 或者应用层后端可以根据业务需求实现连接管理,Nginx 负责保持连接代理。应用主动断开后,Nginx 也会相应地释放 WebSocket。
总结
Nginx 可以通过超时和连接数限制等措施控制 WebSocket 的资源使用,但它的控制相对被动。如果需要业务逻辑来判断关闭 WebSocket,还是需要在应用层处理。