如何使用Socket.IO实现微应用之间的通讯?

为什么?

我微应用加载器都自己写了,通讯怎么可能用乾坤的东西?

​​​​​​​Socket.IO是什么?

1. Socket.IO 是一个库,可以在客户端和服务器之间实现 低延迟双向 和 基于事件的 通信。

2. Socket.IO 不是 WebSocket实现。尽管 Socket.IO 确实在可能的情况下使用 WebSocket 进行传输,但它为每个数据包添加了额外的元数据。这就是为什么 WebSocket 客户端将无法成功连接到 Socket.IO 服务器,而 Socket.IO 客户端也将无法连接到普通 WebSocket 服务器。

3. Socket.IO 并不打算在移动应用程序的后台服务中使用。Socket.IO 库保持与服务器的开放 TCP 连接,这可能会导致用户消耗大量电池。请为此用例使用FCM等专用消息传递平台。

Socket.IO有什么特点?

HTTP 长轮询回退

如果无法建立 WebSocket 连接,连接将回退到 HTTP 长轮询。

这个特性是人们在十多年前创建项目时使用 Socket.IO 的原因(!),因为浏览器对 WebSockets 的支持仍处于起步阶段。

即使现在大多数浏览器都支持 WebSockets(超过97%),它仍然是一个很棒的功能,因为我们仍然会收到来自用户的报告,这些用户无法建立 WebSocket 连接,因为他们使用了一些错误配置的代理。

自动重新连接

在某些特定情况下,服务器和客户端之间的 WebSocket 连接可能会中断,而双方都不知道链接的断开状态。

这就是为什么 Socket.IO 包含一个心跳机制,它会定期检查连接的状态。

当客户端最终断开连接时,它会以指数回退延迟自动重新连接,以免使服务器不堪重负。

数据包缓冲

当客户端断开连接时,数据包会自动缓冲,并在重新连接时发送。

更多信息在这里.

收到后的回调

Socket.IO 提供了一种方便的方式来发送事件和接收响应:

发件人

socket.emit("hello", "world", (response) => {
  console.log(response); // "got it"
});

接收者

socket.on("hello", (arg, callback) => {
  console.log(arg); // "world"
  callback("got it!");
});

您还可以添加超时:

socket.timeout(5000).emit("hello", "world", (err, response) => {
  if (err) {
    // 另一方未在给定延迟内确认事件
  } else {
    console.log(response); // "got it"
  }
});

广播

在服务器端,您可以向所有连接的客户端客户端的子集发送事件:

// 到所有连接的客户端
io.emit("hello");

// 致“news”房间中的所有连接客户端
io.to("news").emit("hello");

这在扩展到多个节点时也有效。

多路复用

命名空间允许您在单个共享连接上拆分应用程序的逻辑。例如,如果您想创建一个只有授权用户才能加入的“管理员”频道,这可能很有用。

io.on("connection", (socket) => {
  // 普通用户
});

io.of("/admin").on("connection", (socket) => {
  // 管理员用户
});

那如果我用它来实现微应用通讯会怎么样?稍加改造封成组件,组件逻辑如下配合代码看就清晰了

 

展开看代码:

<SocketEmitter>:

  interface.d.ts

declare module 'socket.io-client' {
  io: any;
  export default io;
}

  eventEmitter.ts

/**
 * 自定义事件
 * author hjj
 */

type EventItemType = { [key: string]: { listener: (data?: any) => void; timer: number }[] };

class EventEmitter {
  events: EventItemType;
  constructor() {
    this.events = {};
  }
  getEvents() {
    return this.events;
  }
  once(eventName: string, listener: (...args: any) => void) {
    return this.on(eventName, listener, 0);
  }
  on(eventName: string, listener: (...args: any) => void, timer = -1) {
    let listeners = this.getListeners(eventName);
    listeners.push({
      listener,
      timer,
    });
  }
  emit(eventName: string, ...args: any) {
    return this.trigger(eventName, args);
  }
  remove(eventName: string) {
    this.events[eventName] && delete this.events[eventName];
  }
  off(eventName: string, listener: (...args: any) => void) {
    let listeners = this.getListeners(eventName);
    let index = listeners.findIndex((item) => item.listener === listener);
    index !== -1 && listeners.splice(index, 1);
  }
  clear() {
    this.events = {};
  }
  trigger(eventName: string, args: any) {
    let listeners = this.getListeners(eventName);
    for (let i = 0; i < listeners.length; i++) {
      let listener = listeners[i];
      if (listener) {
        listener.listener.apply(this, args || []);
        listener.timer === 0 && listeners.splice(i, 1);
        listeners.length === 0 && delete this.events[eventName];
        listener.timer !== -1 && listener.timer--;
      }
    }
  }
  getListeners(eventName: string) {
    return this.events[eventName] || (this.events[eventName] = []);
  }
}

export default EventEmitter;

  index.ts

import EventEmitter from './eventEmitter';
import logger from '../logger'; //日志打印组件就是console.log一样
import cache from '../cache'; //缓存组件这里用来获取session里的token
import io from 'socket.io-client';
//继承自定义事件类型
class SocketEmitter extends EventEmitter {
  sokect: any;
  sokectEvents: string[];
  subActions: { [key: string]: (data?: any) => void };
  constructor() {
    super();
    this.sokectEvents = [];
    this.subActions = {};
  }
  connect(url: string, options: any) {
    if (this.sokect && this.sokect.connected) {
      return;
    }
    const token = cache.getCache('token', 'session');
    logger.debug('第一次尝试连接socket');
    this.sokect = io(url, { path: '/socket.io', query: { token }, transports: ['websocket'], ...options });
    this.sokect.on('connect', function () {
      logger.debug('socket连接成功');
    });
    this.sokect.on('disconnect', function () {
      logger.debug('socket断开连接');
    });
  }
  disconnect() {
    this.clear();
    this.sokect && this.sokect.disconnect();
    this.sokect = null;
  }

  /**
   * 抽象socket监听方法,这几个方法就是解决了多个listener会和服务创建多条连接,如图那样,调这些方法会汇总处理后只与服务创建一条连接。
   * @param {*} param0
   */
  subscribe(eventName: string, listener: (...args: any) => void) {
    this.on(eventName, listener);
    if (this.sokectEvents.includes(eventName) || !this.sokect) {
      return;
    }
    this.sokectEvents.push(eventName);
    this.subActions[eventName] = this.subAction(eventName);
    this.sokect && this.sokect.on(eventName, this.subActions[eventName]);
    logger.debug(`socket.io listener event -> ${eventName}`);
  }

  subAction(eventName: string) {
    return (data: any) => {
      let json;
      try {
        json = JSON.parse(data);
      } catch (e) {
        json = data;
      }
      this.emit(eventName, json);
    };
  }

  /**
   * 抽象socket监听方法
   * @param {*} param0
   */
  unsubscribe(eventName: string, listener: (...args: any) => void) {
    this.off(eventName, listener);
    this.sokect && this.sokect.off(eventName, this.subActions[eventName]);
    delete this.subActions[eventName];
    this.sokectEvents = this.sokectEvents.filter((v) => v !== eventName);
    logger.debug(`socket.io unlistener event -> ${eventName}`);
  }
}
//保证全局单例
const _global = window as any;
const event = new SocketEmitter();

const LMEvent: SocketEmitter = (function () {
  if (_global._LM_EVENT_) {
    return _global._LM_EVENT_;
  } else {
    _global._LM_EVENT_ = event;
    return event;
  }
})();

export default LMEvent;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值