无论是游戏场景还是密集运算场景,双工通信已经算是一个老相识了;我们这次仅仅用ws举例,说明不同的连接之间如果想传输信息、交互,该如何选择方案。
假设有A、B两个连接,如果我们想实现一个「先发起A连接,实现客户端z到服务端x的连接,然后由x发起和服务端y的通信,最终把y的信息传输到z(连接B)」,其一是使用阻塞方法,在z连接到x时(连接A,我们也可以说是A线程),先通过动态数据结构(例如py中的queue、go中的channel map等)阻塞当前的连接线程,等y把信息传输到z时(连接B,即B线程),访问上述共享变量,把数据set到queue里边,然后A线程以FIFO的形式接收到数据,并解除阻塞状态;其二是直接使用B线程控制A线程websocket的handler,构建一个全局的hash map,key是websocket_id(连接唯一uuid),value是websocket的handler,B线程直接通过map[id].send(xxx)的形式把y的信息传输到z;两种方式各有利弊,选择哪种方案主要取决于我们的应用需求、可用资源、对性能的需求,以及代码的可维护性。
对于第一种方法——阻塞方式,使用队列或者通道这样的动态数据结构来在连接之间传递信息。这种方式的优点包括:
- 易于理解:使用队列或通道进行通信的概念相对简单直观,易于理解和实现。
- 线程安全:队列和通道通常内建线程安全特性,可以避免多线程编程中的数据冲突问题。
- 可以控制流量:可以利用阻塞队列控制处理速率,防止数据过快消费导致压力。
然而,这种方式也存在一些缺点:
- 阻塞:如果队列为空,那么线程就会阻塞,等待新的数据到来。这可能导致系统资源的浪费,特别是在高并发的环境中。
- 可能存在的延迟:在队列中传递数据可能会引入额外的延迟,特别是在高并发的环境中。
对于第二种方法——使用全局哈希映射,它的优点包括:
- 实时性:这种方法能够实现实时的信息传输,减少延迟。
- 直接操作:直接通过 WebSocket ID 操作 WebSocket 连接,较之间接地操作队列或通道,这种方式更为直接。
然而,这种方式的缺点包括:
- 线程安全问题:对于全局变量的操作,特别是在多线程环境中,如果没有正确的同步措施,可能会导致线程安全问题。
- 复杂性:相比于使用队列或通道,这种方法需要更多的代码逻辑来维护和操作这个全局映射。
- 扩展性问题:如果你需要增加更多的 WebSocket 连接,这种方式可能会带来更高的维护成本。
- 可能存在的内存问题:如果你的应用程序有大量的 WebSocket 连接,并且这些连接的生命周期相对较长,那么使用全局哈希映射可能会占用大量的内存。
基于上述方法,其实还有类似mqtt这种高度集成的、以发布订阅为主旨的服务组件,它类似消息队列、持久化存储等组件服务,需要另起一个服务中台,所有的消息接收与转发全部由它搞定,就不需要每个消息发起/接收端自己去逐个定义了,所以它适用于物联网多终端的场景。