Bootstrap

websocket的使用

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 方法时,可以传入自定义的 onmessageonclose 回调:

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();
  }
};

这种方式使得前端在处理 onmessageonclose 时有更大的灵活性,可以根据实际需求自定义事件处理逻辑,而不是在插件内部固定处理。

使用场景

在 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,还是需要在应用层处理。

;