WebSocket 基础与应用系列 转转客服IM的WebSocket集群部署方案 消息发送流程 断线重连

实践:

服务端设置保持连接的最长时间。

github.com\gorilla\websocket@v1.5.0\server.go

    // HandshakeTimeout specifies the duration for the handshake to complete.
    HandshakeTimeout time.Duration
 
         14:15:57 recv: 14:15:56
         14:16:30 recv: 14:16:28
         14:17:19 recv: 14:17:18
         14:18:19 read: websocket: close 1006 (abnormal closure): unexpected EOF

        HandshakeTimeout specifies the duration for the handshake to complete.
设为60s,14:17:19收到消息,60内无消息传递,关闭连接。

        一次会话的时间间隔
 
 

小结:

1、

在nginx配置中,将ws服务的负载均衡策略设置为ip_hash,保证用户在IP不变的情况,一直分配在一个固定服务器上。

用户与ws服务建立连接后,获取当前机器hostname,将用户uid与机器hostname关系存储在redis中。

每个ws服务都维护了一个独立的consumer,采用广播消费模式,仅消费属于自己tag的消息,tag即为当前机器hostname。

2、

用户A在向用户C发送消息时,用户A将消息发送给ws-1服务器,ws-1服务接收消息后,从redis中获取用户C的连接信息;

ws-1服务器拿到用户C和ws-2的关系后,设置mq消息tag="ws-2",直接将用户消息通过mq发送出去

ws-2服务器的consumer接收到对应mq消息后,即可通过ws连接将消息推送用户C 至此,我们就完成了一条消息的发送与接收。

3、

断线重连

  1. 即时清理。用户与ws服务建立连接时,当前ws服务查询redis中存储的用户连接信息。当连接信息与当前ws服务不一致时,当前ws服务更新redis存储信息、并发出广播mq消息,通知其他ws服务关闭与当前用户的连接通道。

  2. 定时清理。除此之外,还需要前端同学配合,定时向连接的ws服务发送心跳消息,ws服务定时检测用户连接的心跳情况,关闭废弃的用户连接。

 

 转转客服IM系统:高效沟通背后的技术挑战和解决方案 https://mp.weixin.qq.com/s/50v5cOWT79huMTJmBQhGiw

转转客服IM系统:高效沟通背后的技术挑战和解决方案

 

  • 前言

  • 实时性

  • 可靠性

  • 心跳机制

  • 消息协议

  • 结语

 

前言

在当今互联网时代,高效的用户服务是提升用户体验的关键。转转自研的客服IM系统作为用户与客服沟通的桥梁,承担着传递信息、解决问题的关键角色。然而,消息数据的流转并非一帆风顺,本文将深入探讨IM系统在消息传递过程中遇到的问题和挑战,以及相应的技术解决方案。

如图是IM系统中一条消息的流转链路:

图片IM消息流转

相较于普通web系统,IM系统的消息数据流转链路更长、更复杂。从客户端到服务端,再从服务端到另一个客户端,任何一个环节的故障都可能导致消息延迟、丢失、乱序或重复,从而影响用户体验。

网络波动和客户端设备性能的不稳定性是影响IM系统性能的主要因素,这些因素可能导致消息的实时性、可靠性和完整性受到威胁。

实时性

首当其冲的就是消息延迟问题:当一条消息发出后,我们的系统需要确保这条消息最快被接收人感知并获取到,并且保证资源消耗较少。这里关键的几个点是:最快触达,且耗费资源少。

方案1:长短轮询

在PC web早期,大部分应用都是采用“一问一答”的请求响应模式来获取数据,IM系统采用客户端轮询的方式,定期、高频轮询获取服务端的新消息。这种方式开发成本较低、容易实现,但是高频轮询很多请求是无用请求,客户端浪费流量和电量,服务端资源压力很大。

后来基于短轮询进化出长轮询模式,相较于前者,后者在请求时获取到新数据时不会立即返回,而是在服务端保持连接等待一段时间,如果等待期间有新消息就立即返回响应,长轮询仅仅解决了客户端的无用消耗,但是服务端资源高负载情况依然未能解决。

方案2:WebSocket

随着HTML5的出现,全双工的WebSocket成为解决这一问题的关键。基于WebSocket实现的IM服务,客户端和服务端只需要完成一次握手,就可以创建持久的长连接,并进行随时的双向数据传输。

 长短轮询WebSocket
浏览器支持情况 ✅所有浏览器都支持 大部分主流浏览器支持
服务器负载 高负载 ✅取决于实际消息量
客户端延迟 非实时;取决于轮询频率 ✅实时
客户端资源消耗 ✅低
实现复杂度 ✅低

经过比较转转客服IM系统采用了WebSocket协议,具体使用方式见WebSocket使用。当服务端接收到新消息时,可以通过建立的WebSocket连接,直接进行推送,保证了消息到达的实时性。

可靠性

如图是一条消息发送的核心步骤,整个过程中可以分为两个部分,消息由客户端发送到服务端(第1、2步),服务端将消息推送至另一个客户端(第3步),发送过程中任何一步出现问题都会导致消息发送失败。

图片消息发送示例
如何保证消息触达?

我们参考使用了TCP/IP协议中的ACK机制来实现防丢逻辑。ACK机制是TCP/IP协议三次握手重要的一环,用于确认对方发送信息无误。ACK响应机制如下:

  1. 发送者发送消息时会携带一个消息标识符(此处使用发送方id和消息发送时间戳)、并在本地维护一个“等待ACK消息列表”
  2. 接收者收到消息后对消息进行存储得到消息id
  3. 随后再将该标识回传给发送方(ACK消息)
  4. 发送方收到ACK消息后将消息从“等待ACK消息列表”删除
  5. 当发送方没有在约定时间内收到ACK消息时,就需要执行失败消息处理逻辑:自动重发、客户端标记发送失败等

服务端实现与客户端稍有不同,服务端需要要维护全量用户的消息,使用定时任务检查等待ACK消息列表效率比较低,此处通过mq的延迟消息来实现:

当消息发出时同时发送一个延迟mq,延迟消息被消费时对应的消息仍在等待ACK列表中,则表示消息未能在规定时间内被确认,需要进行重试发送。

如图为完整的ACK实现机制:

图片ACK机制

另外客户端也会在页面刷新、WebSocket重连时触发http接口重新拉取当前会话的所有消息进行渲染,保证消息不丢失。

数据重复

消息重推解决了消息丢失的问题,但是因为ACK消息本身就可能会丢失从而导致消息重复,因此我们需要保证推送消息和重推消息有相同且唯一的消息id,接收方可以根据该消息id进行数据去重。

发送方:客户端使用发送人id消息发送时间戳作为唯一的ACK标识,传递给服务端。

服务端:使用雪花算法接收到的消息生成消息id,将ACK标识消息id建立映射关系;服务端再将消息id推送至发送方和接收方。

一个完整的消息发送流程如图所示:

图片IM消息流转

心跳机制

IM系统中接收和发送消息都是使用长连接实现,但是如果连接断开,那发送和接收数据就会出现问题。在客服业务中,人工客服席位有限,系统需要可靠的机制保证人工客服资源有效利用。

为此我们在应用层设计心跳消息,使用该机制更新用户当前状态、保证会话有序退出。

心跳机制设计如下:

客户端

  • 设置定时器,用户建立连接后,定时发送(30s)心跳消息给服务端

服务端

  • 接收心跳消息,更新心跳时间
  • 设置定时任务,定时扫描在线用户上次心跳时间,执行以下逻辑
    • 上次心跳时间超出30s,将其标记为离线状态,关闭连接,等待用户重连
    • 上次心跳时间超出2分钟则认为用户已经彻底离开,执行会话关闭逻辑释放人工客服资源

应用层心跳消息仅用于保活和状态更新,因此数据结构设计十分精简,不携带额外信息。

消息协议

在IM系统中消息格式的设计也十分重要,良好的数据格式可以准确传递消息内容并具有极高的可读性。我们根据消息类型和发送流向将消息数据格式大致分为如下几部分:

  • 消息类型:用于描述消息的用途、流向,如心跳消息、用户/客服消息、系统消息等
  • 客服id:接收或者发送消息的客服标识
  • 用户id:接收或者发送消息的用户标识
  • 消息内容:实际的消息,与消息类型相关
    • 消息格式:用于描述用户/客服消息格式,如文本、图片、视频、订单卡片、优惠券等
    • 消息文本:消息的展示内容
图片不同类型的消息

结语

转转客服IM系统通过引入WebSocket协议、ACK机制、消息重推和数据去重等策略,有效应保障了消息传递过程中的实时性、可靠性和完整性。这些技术的应用,不仅提升了用户与客服之间的沟通效率,也为转转平台提供了更加稳定、高效的服务支持。

在未来的发展中,我们将继续优化和完善,以应对不断变化的需求和用户期望,为用户提供更加优质的服务体验。

 

关于作者

李帅,转转履约中台研发工程师,主要负责客服工单、IM等系统研发

 

想了解更多转转公司的业务实践,欢迎点击关注下方公众号:

 

 

 

 

转转客服IM的WebSocket集群部署方案 https://mp.weixin.qq.com/s/oR1svDjbWa8c-qx1rKn80w

转转客服IM的WebSocket集群部署方案

李帅 转转技术 2022-05-18 08:30 发表于北京
  • 一、背景

  • 二、WebSocket集群

    • 2.1 WebSocket协议

    • 2.2 WebSocket服务的集群式部署

    • 2.3 消息发送流程

    • 2.4 断线重连

  • 三、总结

一、背景

转转作为国内头部的二手闲置交易平台,拥有上亿的用户。用户在使用转转app遇到问题时,一般可以通过在线客服、热线电话等方式联系转转客服并解决问题。

客服IM系统是转转自研的在线客服系统,是用户和转转客服沟通的重要工具,主要包括机器人客服、人工客服、会话分配、技能组管理等功能。在这套系统中,我们使用了很多开源框架和中间件,今天讲一下WebSocket在客服IM系统中的应用。

二、WebSocket集群

2.1 WebSocket协议

IM是实时通信(Instant Messaging)的简称,实时是IM系统的基本要求。在客服系统中,用户和客服实时收发消息,是最基础、最重要的功能。

WebSocket(以下简称ws)是一种在单个TCP连接上进行全双工通信的协议,允许服务端主动向客户端推送数据。能够以较小的开销,实现更强的实时性。因此客服IM系统采用了ws协议,实现了服务端同时接收与发送数据的能力。

2.2 WebSocket服务的集群式部署

在实际生产环境中,我们不可能使用单台服务器做ws服务,一旦出现问题就是毁灭性的,所以我们会使用集群式部署ws服务。

ws协议是全双工通信协议,可以双向通信的,部署多台机器后,如图所示,不同用户会分别和不同的机器连接,A如何向C发送消息呢?

图片

  • 首先,我们在nginx配置中,将ws服务的负载均衡策略设置为ip_hash,保证用户在IP不变的情况,一直分配在一个固定服务器上。
  • 用户与ws服务建立连接后,获取当前机器hostname,将用户uid与机器hostname关系存储在redis中。
  • 每个ws服务都维护了一个独立的consumer,采用广播消费模式,仅消费属于自己tag的消息,tag即为当前机器hostname。

图片

2.3 消息发送流程

  1. 用户A在向用户C发送消息时,用户A将消息发送给ws-1服务器,ws-1服务接收消息后,从redis中获取用户C的连接信息;
  2. ws-1服务器拿到用户C和ws-2的关系后,设置mq消息tag="ws-2",直接将用户消息通过mq发送出去
  3. ws-2服务器的consumer接收到对应mq消息后,即可通过ws连接将消息推送用户C 至此,我们就完成了一条消息的发送与接收。

2.4 断线重连

网络环境错综复杂,难免会有网络掉线、网络环境切换的情况,都会导致用户和ws服务的连接发生变化。

一个比较典型的场景就是C端用户网络在4G和wifi之间进行切换,会导致ip变化,从而可能到导致用户和另外的ws服务再次建立连接。这种情况可能会导致消息重复发送、以及额外的资源消耗,所以要尽量避免。处理方式如下:

  1. 即时清理。用户与ws服务建立连接时,当前ws服务查询redis中存储的用户连接信息。当连接信息与当前ws服务不一致时,当前ws服务更新redis存储信息、并发出广播mq消息,通知其他ws服务关闭与当前用户的连接通道。

  2. 定时清理。除此之外,还需要前端同学配合,定时向连接的ws服务发送心跳消息,ws服务定时检测用户连接的心跳情况,关闭废弃的用户连接。

三、总结

以上就是我们在客服IM系统中使用WebSocket协议实现的消息收发的主要流程。要实现一个完整的IM系统,不仅要保证消息的实时性,消息的一致性、顺序性也很重要,还有网络异常等情况下的重连、用户心跳检测等,这些功能需要客户端同学一起协作才能完成。

WebSocket 基础与应用系列(一)—— 抓个 WebSocket 的包 https://mp.weixin.qq.com/s/f96Da8kCluNwv7cxW39gzg

posted @ 2021-09-22 20:59  papering  阅读(866)  评论(0编辑  收藏  举报