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 URLserviceUrls = {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');}}// 切换服务 URLchangeService(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 {# 定义一个限制连接的 zonelimit_conn_zone $binary_remote_addr zone=ws_limit:10m;server {location /websocket/ {limit_conn ws_limit 10; # 每个客户端的最大连接数为 10proxy_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,还是需要在应用层处理。