WebSocket

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

开启 WebSocket

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

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

  1. module.exports = {
  2. stickyCluster: true,
  3. // ...
  4. };

配置 WebSocket

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

  1. const websocket = require('think-websocket');
  2. module.exports = [
  3. // ...
  4. websocket(think.app),
  5. ];

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

  1. const socketio = require('think-websocket-socket.io');
  2. exports.websocket = {
  3. type: 'socketio',
  4. common: {
  5. // common config
  6. },
  7. socketio: {
  8. handle: socketio,
  9. allowOrigin: '127.0.0.1:8360', // 默认所有的域名都允许访问
  10. path: '/socket.io', // 默认 '/socket.io'
  11. adapter: null, // 默认无 adapter
  12. messages: [{
  13. open: '/websocket/open',
  14. addUser: '/websocket/addUser'
  15. }]
  16. }
  17. }

事件到 Action 的映射

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

  1. exports.websocket = {
  2. // ...
  3. socketio: {
  4. // ...
  5. messages: {
  6. open: '/websocket/open', // 建立连接时处理对应到 websocket Controller 下的 open Action
  7. close: '/websocket/close', // 关闭连接时处理的 Action
  8. addUser: '/websocket/addUser', // addUser 事件处理的 Action
  9. }
  10. }
  11. }

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

服务端 Action 处理

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

  1. module.exports = class extends think.Controller {
  2. constructor(...arg) {
  3. super(...arg);
  4. }
  5. openAction() {
  6. this.emit('opend', 'This client opened successfully!')
  7. this.broadcast('joined', 'There is a new client joined successfully!')
  8. }
  9. addUserAction() {
  10. console.log('获取客户端 addUser 事件发送的数据', this.wsData);
  11. console.log('获取当前 WebSocket 对象', this.websocket);
  12. console.log('判断当前请求是否是 WebSocket 请求', this.isWebsocket);
  13. }
  14. }

emit

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

  1. module.exports = class extends think.Controller {
  2. constructor(...arg) {
  3. super(...arg);
  4. }
  5. openAction() {
  6. this.emit('opend', 'This client opened successfully!')
  7. }
  8. }

broadcast

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

  1. module.exports = class extends think.Controller {
  2. constructor(...arg) {
  3. super(...arg);
  4. }
  5. openAction() {
  6. this.broadcast('joined', 'There is a new client joined successfully!')
  7. }
  8. }

客户端示例

客户端示例代码如下:

  1. <script src="http://lib.baomitu.com/socket.io/2.0.1/socket.io.js"></script>
  2. <script type="text/javascript">
  3. var socket = io('http://localhost:8360');
  4. $('.send').on('click', function(evt) {
  5. var username = $.trim($('.usernameInput').val());
  6. if(username) {
  7. socket.emit('addUser', username);
  8. }
  9. });
  10. socket.on('opend', function(data) {
  11. console.log('opend:', data);
  12. });
  13. socket.on('joined', function(data) {
  14. console.log('joined:', data);
  15. });
  16. </script>

socket.io

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

io 对象

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

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

设置 path

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

  1. exports.websocket = {
  2. // ...
  3. socketio: {
  4. // ...
  5. path: '/socket.io',
  6. }
  7. }

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

设置 allowOrigin

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

  1. exports.websocket = {
  2. // ...
  3. socketio: {
  4. // ...
  5. allowOrigin: '127.0.0.1:8360',
  6. }
  7. }

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

设置 adapter

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

  1. const redis = require('socket.io-redis');
  2. exports.websocket = {
  3. // ...
  4. socketio: {
  5. // ...
  6. adapter: redis({ host: 'localhost', port: 6379 }),
  7. }
  8. }

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

原文: https://thinkjs.org/zh-cn/doc/3.0/websocket.html