当前位置: 首页 > Web前端 > HTML

得物客服一站式客服工作台卡住优化之路

时间:2023-03-28 14:39:52 HTML

一、背景一站式客服工作台包括在线、电话、工单、工具四大功能模块。很多常用的模块,比如工单明细,订单明细,都是以iframe的形式嵌套的,在系统加载过程中会耗费时间。另外,在线消息通信模块强烈依赖tinode的第三方SDK,很多方法都是直接调用tinode提供的api,也继承了tinode很多不合理的方式。使用tinode至今,由于迭代资源的投入,一直没有对tinode源码进行优化和改进。当消息通信方式改为Afterbroadcast时,sessionfreeze的问题就暴露出来了。通过阅读tinode源码的消息链接模块,发现还有很大的优化空间。本文是针对消息链接的具体优化实现。二、发现问题1、消息数据处理过程存在缺陷。看了tinode的第三方sdk源码,发现客服在“接收”和“发送”消息的环节有很大的优化空间。原来的逻辑中,从发送消息到快速渲染页面到tinode响应返回结果再刷新渲染页面,而客服收到消息后,会刷新整条消息,反序列化,排序,去重,状态处理等。需要多次循环,加??上通信方式改为广播方式,大数据量的循环任务对性能是一个严峻的挑战。客服“接收”和“发送”消息链接概览(一)客服“接收”和“发送”消息链接概览(二)图中红色区域有很多for循环,是时间最多的——消费场景。就是获取用户和客服的通讯记录(原tinode中提供的方法,topic.message()会执行n次),反序列化,会话状态处理,排序,去重会遍历所有的聊天消息.序列化是最耗时的场景。如果客服之前和用户的聊天信息比较多,那么遍历的次数就会越多,时间也就越长。此外,JavaScript是单线程的。如果遍历次数过多,会形成阻塞,导致客服在快速切换会话时,循环还没有结束,页面渲染还没有完成,就会出现卡顿现象。3、优化思路当每个用户从客户端进入客服工作台的线路时,会生成一个会话id(sessionId),每个sessionid下的每条人工消息都会有一个消息id(msgid),有很多轮客服与用户沟通的消息。为了减少“旧代码”中降低性能的循环次数,核心任务是避免遍历聊天消息数据(因为消息太多),遵循不遍历聊天消息不遍历的原则,并重写原有逻辑中的“去重”和“排序”逻辑。这时候上面说的sessionid和messageid就起到了很重要的作用。1.去重在这个优化方案中,全局维护了一个msgidCacheMapsMap数据结构。这个数据结构有两个维度,sessionId和msgid,用来保存当前session中每条消息的msgid(sessionId)。在消息对话中,人工客服发送的消息会经历从虚拟消息到真实消息两个阶段(这里的虚拟消息是指在人工会话中,客服发送消息到网关后,为了快速在聊天区显示消息,通过之前的消息seq+0.002生成一个虚拟seq(virtualSeq,等待网关返回真实seq,然后用真实seq替换virtualSeq),虚拟消息阶段会保存msgid在Map中,对于系统推送的消息,没有msgid,不需要经过这个过程,直接放入sessionpool中,在真实消息(tinode返回seq)阶段,根据msgid到msgidCacheMapsMap数据结构中,如果这个msgid存在,说明是重复数据,可以用seq代替。2.排序本次的优化方案是采用二分查找插入排序*的方式,全局维护一个seqCacheMapsMap数据结构。这个数据结构有点类似于上面的去重,同样有两个维度,sessionId和seq,用于二分查找插入排序的方法,以seq(realseq)和virtualSeq(virtualseq)作为查找的依据,每次一个message进来,根据二分法快速找到当前seq可以插入的位置,虚拟message阶段,直接插入,realmessage阶段(msgidCacheMaps存在这个msgid),直接替换掉,但是这个时候出现问题,因为在人工对话的过程中,客服给用户发送的每条消息都会经过网关对敏感词的校验,如果没有触发敏感词,就会将消息发送给客服端展示给用户,如果触发敏感词,包含敏感词的消息会被网关拦截,消息不会到达用户端。这时候网关是不会返回seq的,那么没有返回seq怎么办呢?即在tinode返回阶段,会将之前的virtualSeq替换为之前的messageseq+0.002,保证其位置在聊天区中有序有序的显示*。“去重”和“排序”概述3.缓存恢复(结束会话销毁)上面去重和排序中提到,为了减少遍历次数,两个数据仓库(msgidCacheMapsMap数据结构,seqCacheMapsMap数据结构)是globalmaintained),但是每个客服每天的会话量是100+,加上每个session中客服和用户的来回消息量在40+左右,存储的数据量还是比较大的,容易导致内存溢出,那么什么时候删除比较合适呢?根据业务情况,最终的选择是销毁全局挂载的hashmap,在会话结束、会话转移、推送下线时释放内存。数据“存储”和“删除”概述4、消息状态这里的消息状态是指:已读、未读、已接收、发送、发送失败……等。在客服与用户沟通过程中,客服端和用户端显示的消息状态是实时更新的。客服给用户发消息。当用户阅读消息时,会返回info协议(推送消息通知)告诉h5端消息已阅读,然后h5端响应消息。原来消息状态更新的处理方式:客服给用户发送消息后,遍历该用户在当前会话中的所有历史消息,进行所有的重置操作。如果此时用户与客服之间通信的消息较多,会导致大量遍历,导致性能严重消耗等问题。优化方案:先过滤掉历史消息和非客服发来的消息,通过二分法找到消息,然后直接更改状态。收到用户发送的消息后,在messagePools(当前用户的所有session)中更新客服发送消息的状态为倒序读取,因为既然所有用户都发送了消息,那么就说明消息已发送by客服已经读过了,没必要按照老逻辑遍历每条消息设置状态,很浪费性能。除了正在发送和发送失败的消息,都呈现为已读。具体实现:客户端推送一长串note事件告诉H5,H5端记录已读消息的seq,更新小于等于seq的客服发送的消息数据状态,即:recv(received)=>read(读取)发送消息:目前发送消息只会执行两次,第一次会快速显示消息到通讯页面,然后发送消息(wss),当收到ack后,会进行第二次消息状态更新,只需要通过msgid查找需要更新的消息即可更新,不再需要使用tinode提供的topic.message方法遍历接收到的消息全量:客服只会在接收到用户消息后触发一次消息更新,不需要遍历当前用户的全量数据来更新新的状态,同时会返回ack5,硒敏感词拦截处理IM聊天页面用户进线后,将对用户与代理客服之间发送的消息进行敏感词监控(仅监控和禁止发送)。原方案:代理客服编辑好消息后,点击发送,调用后台敏感词接口,不触发敏感词验证后才发送。如果网络有波动,界面返回慢,就会让客服有种发信息的感觉,必须点击才能退出的情况。优化方案:通过网关拦截。客服发送消息时,会直接渲染到聊天区。网关会检查发送的消息是否触发了敏感词。如果触发了敏感词,网关会返回一个状态告诉h5,然后h5会根据返回的结果改变状态来提示客服。敏感词逻辑概览4、优化前后数据对比优化链接技术方案于2月28日发布上线,故以2月28日为时间截止点,拉取优化前后数据对比,如下图.1、优化前,如上图,统计了2022年2月1日到2022年2月28日总进线的两个数据指标:平均第一响应时间:8.40秒平均响应时间:19.9秒2.优化后As上图统计了2022年3月1日到2022年3月9日总入线的两个数据指标:平均第一响应时间:6.82秒,比优化前减少1.58秒平均响应时间:18.22秒,即比优化前缩短了1.68秒。5.小结一般来说,IM产品的用户量和活跃度通常都比较大,容易在一些特殊的时间点造成流量高峰。因此,在技术上需要能够应对突发流量。同时,IM一般包括实时性、可靠性、一致性和安全性这四个特性。IM优化还有很长的路要走。在保证业务稳定的情况下,我们还将围绕四大特点继续努力,使符合得物的IMSDK越来越完善,形成行业消息通信的标杆。文/宇博关注得物科技,做最时尚的科技人!