一个基于事件回调的业务思考

业务概述

某些并发比较高的业务,通常是基于回调事件异步处理的,比如电话系统。历史原因我们实现的方案并不是很优雅,导致出现一些问题,持续了很久。最近花了一些时间,来优化目前的问题,临近上线,特来记录一下。我们实现的是电话的呼入呼出,做一些排队、分配和通话结果等信息分析和记录。业务逻辑流程大致如下:

回调业务示意图

问题抓取和分析

对于记录的准确性方面的问题,我大致罗列如下:

  1. 时间类型字段错误,比如挂机时间小于呼入时间
  2. 业务字段缺失,比如该有的字段,没记录上
  3. 业务字段错误,比如本来是A,结果被更新成了B

每日分析了几批问题记录之后,总结了大致原因如下:

  1. 设计有缺陷,不够严谨,导致本身代码实现有问题
  2. 服务器回调顺序颠倒或并发太高时,业务层处理不够及时
  3. 服务器回调事件缺失
  4. 网络请求延迟

Tips: 至于如何抓问题记录,需要根据各自的业务,写脚本来跑,可以从服务器、数据库、业务日志上抓一些exception 或error日志或分析逻辑去汇总,每天早上定时发送昨天的异常记录分类和数量,用来监控业务准确性。

解决过程

问题的解决通常是个漫长的过程,通过每日的异常记录分析,根据不同的原因,制定了不同的修复方案:

  • 设计缺陷
    找负责的产品经理去沟通,历史原因不说了就,如何保证现有业务OK的情况下,尽可能的完善设计方案。再根据设计方案,重新实现部分功能。
  • 代码本身bug
    这个相对比较容易,加强业务日志,即可发现和验证。这里大部分来自并发请求引起的,通过锁、异步方案,基本得到解决。
  • 实现方案改进
    有些功能,可能由于当时时间紧,任务重,导致实现方案不够好,引发的后续不稳定情况较多。这里我单独说一个关于电话接听结果的解决过程。

需求方面:
产品设计初衷是希望一通电话经过不同的ivr菜单流转到系统坐席上面整个通话过程中,各个节点上有自己的状态,类似状态机这种:呼入-》ivr菜单-》转技能组-》坐席,客户可能在各个节点放弃,也可能造成排队等待,坐席繁忙,坐席呼叫失败等等一系列通过成功或失败的结果。希望一通记录能准确的反映出本次通话,失败点在哪儿,为何失败。听起来是不是没那么复杂,非常合理的需求。

原本的方案:
既然是基于事件通知的业务逻辑,那只要处理好服务器发送的通知不久好了么?服务器回过来什么事件,就在什么事件里记录通话状态,不断的滚动,最后在哪里断掉了,就是最新的结果。

理想很美好,实际情况是,如何保证各个事件的先后执行顺序?做过电话系统的都知道,一通电话根据你的业务交互复杂程度,会产生十几个甚至几十个事件出来。有些事件间隔时间非常短,相差在100-300ms之内,业务逻辑处理上,一旦延迟,很容易出现前面说到的,结果覆盖问题,时间覆盖问题,那就没办法保证准确了。也有说,请求带SN,并发请求时,只处理最新的SN请求,前面的抛弃,但是这样一来会引起部分逻辑缺失,最终影响判定。

最终方案:
在不断尝试和修正之后,我们采用了参照+日志+逻辑判定三方验证来保证准确性。具体实现方案:

既然每个记录必须有一个结果,我们应该先对结果进行分类。按什么方式去分类呢?这个非常重要,直接影响后面的实现方案。答案是准确性,这个也是我不断更换实现方案之后,得出的结论。这十几种结果,哪些是一次判定可以成功的类型,哪些是推断出来的类型,那些是没办法准确判定,也不好推断的,只能给默认的类型?搞清楚这个才是解决问题的根本。

分好类之后,其实做起来就相对容易一些了。

  1. 首先,既然基于事件处理的状态机,在完全信任服务器的情况下,每个主要事件节点都应该有自己的状态,整通会话下来,其实是一连串状态变更的记录,类似:1-》2-》3-》4-》5-》6。同时,需要定义结束状态标志,比如5是结束的状态,那么对于某条记录来说,走到5就已经结束了,后面的变更不再跟他产生关系。但是一通电话可能存在转接,甚至循环进菜单的问题,所以整体的状态变更还是需要更新。只是某一个通道结束的时候,把通道关联的记录变更日志记录下来做判定用。

  2. 其次,有了上面一步,完事大吉了么?不,远不够,上面的状态变更,能看到整个链条的变更情况,但是不能保证顺序一定是准的,这个完全看网络跟服务器心情。有些结果是必须要在流转过程中去判定的,这种必达型的事件,只能判定一次。即,有这个事件,一定是某个结果。梳理完这类事件,剩下的是逻辑推断结果,处理这类事件时,需要在满足一定条件约束下,才可以判定,比如说通道类型,挂机方等等。剩下的是默认类型,这种类型只能在最终入库的时候去校验。至于到底该算哪种类型,得看产品逻辑。

  3. 折腾完之后,重新测试,跟监控对比,发现数据准确性得到的极大的提高,从之前95%多提到到了99.99%,总体上达到满意效果。

总结思考

说了这么多,可能有人没太看明白,每个团队面临的问题不一定相同,当你面对一个复杂逻辑,当你怎么都理不顺,或者怎么做都做不到满意效果的时候,不妨停下来,冷静思考一下。这功能设计的是否真的合理?逻辑是是否真的严谨?可以实现?如果确实可以实现,只是难度上较大时,是否验证了多种方案?每种方案带来的利弊是否可以接收?必要时,多种方案结合是否可行?面对某些复杂功能时,必须做好切实可行的方案,有条不紊的去做设计和实现,完善的测试和线上监控,才能达到满意的效果。 否则,可能会给后面维护的人埋下大坑,不断的去修复修复再修复,N次修复之后,运气好的话形成个测试稳定版,运气不好的话,可能越补越漏。总之,处理问题时,我们应该做到:

  1. 想办法追本溯源,找出真正影响结果的因素,如监控日志、统计周报等
  2. 多和小伙伴沟通想法,一个人有时候会陷入自己的思维里出不来,尝试多种可行性办法,针对结果做对比,选出最合适的方案。
  3. 针对外部不确定性因素,(网络、逻辑错误、并发)尽可能的去兼容,减少出错次数。
  4. 新功能,一定要在设计上下功夫,磨刀不误砍柴工。千万不要开发到后期发现行不通,就惨了。

就酱紫~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值