WebSocket

对于 WebSocket 目前 ThinkJS 支持了 socket.iows 对其进行了一些简单的包装。

开启 WebSocket

在集群环境中,WebSocket 要求使用粘性会话,来确保给定客户端请求命中相同的 worker,否则其握手机制将无法正常工作。 为了实现这一点,需要开启 stickyCluster 配置。

为了保证性能,stickyCluster 功能默认是关闭的,项目如果需要开启,可以修改配置文件 src/config/config.js

module.exports = {
  stickyCluster: true,
  // ...
};

与此同时,ThinkJS将websocket封装为think-websocket,但是需要在项目目录下执行

$ npm install -s think-websocket

来安装模块

配置 WebSocket

WebSocket 是以 extend 的形式集成到 ThinkJS 的,首先要配置 src/config/extend.js:

const websocket = require('think-websocket');

module.exports = [
  // ...
  websocket(think.app),
];

WebSocket 的各个实现是以 adapter 的形式存在的,以 socket.io 为例(使用 think-websocket-socket.io 进行了封装),在 src/config/adapter.js 中配置如下:

const socketio = require('think-websocket-socket.io');
exports.websocket = {
  type: 'socketio',
  common: {
    // common config
  },
  socketio: {
    handle: socketio,
    allowOrigin: '127.0.0.1:8360',  // 默认所有的域名都允许访问
    path: '/socket.io',             // 默认 '/socket.io'
    adapter: null,                  // 默认无 adapter
    messages: {
      open: '/websocket/open',
      addUser: '/websocket/addUser'
    }
  }
}

事件到 Action 的映射

socket.io 为例,ThinkJS 遵循了 socket.io 服务端和客户端之间通过事件来交互的机制,这样服务端需要将事件名映射到对应的 Action,才能响应具体的事件。事件的映射关系配置在 messages 字段,具体如下:

exports.websocket = {
  // ...
  socketio: {
    // ...
    messages: {
      open: '/websocket/open',       // 建立连接时处理对应到 websocket Controller 下的 open Action
      close: '/websocket/close',     // 关闭连接时处理的 Action
      addUser: '/websocket/addUser', // addUser 事件处理的 Action
    }
  }
}

其中 openclose 事件名固定,表示建立连接和断开连接的事件,其他事件均为自定义,项目里可以根据需要添加。

服务端 Action 处理

通过配置事件到 Action 的映射后,就可以在对应的 Action 作相应的处理。如:

module.exports = class extends think.Controller {

  constructor(...arg) {
    super(...arg);
  }

  openAction() {
    this.emit('opend', 'This client opened successfully!')
    this.broadcast('joined', 'There is a new client joined successfully!')
  }

  addUserAction() {
    console.log('获取客户端 addUser 事件发送的数据', this.wsData);
    console.log('获取当前 WebSocket 对象', this.websocket);
    console.log('判断当前请求是否是 WebSocket 请求', this.isWebsocket);
  }
}

emit

Action 里可以通过 this.emit 方法给当前 socket 发送事件,如:

module.exports = class extends think.Controller {

  constructor(...arg) {
    super(...arg);
  }

  openAction() {
    this.emit('opend', 'This client opened successfully!')
  }
}

broadcast

Action 里可以通过 this.broadcast 方法给所有的 socket 广播事件,如:

module.exports = class extends think.Controller {

  constructor(...arg) {
    super(...arg);
  }

  openAction() {
    this.broadcast('joined', 'There is a new client joined successfully!')
  }
}

客户端示例

客户端示例代码如下:

<script src="http://lib.baomitu.com/socket.io/2.0.1/socket.io.js"></script>
<script type="text/javascript">
  var socket = io('http://localhost:8360');

  $('.send').on('click', function(evt) {
    var username = $.trim($('.usernameInput').val());
    if(username) {
      socket.emit('addUser', username);
    }
  });

  socket.on('opend', function(data) {
    console.log('opend:', data);
  });

  socket.on('joined', function(data) {
    console.log('joined:', data);
  });
</script>

socket.io

socket.io 对 WebSocket 前后端都有封装,使用起来非常方便。

io 对象

在 Action 里可以通过 this.ctx.app.websocket 来获取 io 对象,该对象为 socket.io 的一个实例。

io 对象包含的方法参见文档 https://socket.io/docs/server-api/#server

设置 path

设置被 socket.io 处理的路径,默认为 /socket.io。如果需要修改,可以修改 src/config/adapter.js 的配置:

exports.websocket = {
  // ...
  socketio: {
    // ...
    path: '/socket.io',
  }
}

path 的详细配置参见文档 https://socket.io/docs/server-api/#server-path-value,需要注意的是:如果服务端修改了处理的路径后,客户端也要作对应的修改。

设置 allowOrigin

默认情况下 socket.io 允许所有域名的访问。如果需要修改,可以修改 src/config/adapter.js 的配置:

exports.websocket = {
  // ...
  socketio: {
    // ...
    allowOrigin: '127.0.0.1:8360',
  }
}

allowOrigin 的详细配置参见文档 https://socket.io/docs/server-api/#server-origins-value,需要注意的是:如果服务端修改了处理的路径后,客户端也要作对应的修改。

设置 adapter

使用多节点来部署 WebSocket 时,多节点之间可以借助 Redis 进行通信,这时可以设置 adapter 来实现。

const redis = require('socket.io-redis');
exports.websocket = {
  // ...
  socketio: {
    // ...
    adapter: redis({ host: 'localhost', port: 6379 }),
  }
}

adapter 的详细配置参见文档 https://socket.io/docs/server-api/#server-adapter-value

如发现文档中的错误,请点击这里修改本文档,修改完成后请 pull request,我们会尽快合并、更新。