
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
大部分的软件编程开发项目都是在曲折中前进的,经常我们就会遇到一些编程中的异常或者是错误情况发生,下面我们就简单来了解一下,接口设计中流程异常问题与解决方法。
这里的流程异常不是说代码没有正确实现业务逻辑——那属于功能异常,不属于鲁棒性考虑的范围。这里说的流程异常是指在正常执行流中出现了不可控的异常。
想想我们过去开发的接口,有没有出现过以下情况:
读取磁盘中的文件——有没有考虑读取失败会怎样?
写入磁盘文件——有没有考虑写入失败会怎样(如目录不存在)?
读取系统时间——有没有考虑如果系统时间错误会怎样?
计算某个比率(如中奖率)——有没有考虑除数是0的情况(如压根没人抽奖)?
调某个外部接口——有没有考虑接口调用失败(如超时)的情况?
更重要的,当流程中的某一步失败了,其他步该如何处理(以及已经产生的数据如何处理)?
以上异常有两个特征:
大部分是不可控的(无法通过程序自身避免问题发生);
只要系统运行时间足够长,就一定会发生(除非系统自身没有涉及到这些方面,如压根没有涉及到远程调用);
健壮的程序要能够正确地处理这些异常,保证数据的一致性。这里有两层含义:
程序要处理(而不是忽略)这些异常;
程序能正确地处理这些异常,让程序在发生异常时的行为符合预期;
作为开发人员我们不能有”幸运儿“思想:我的系统不会发生这些问题。但这不代表我们的程序一定能够消化掉这些异常并让流程继续进行下去——有时候让流程终止才是正确的方式,但由于程序没有处理这些异常(或者处理不当)导致流程继续进行,进而导致数据一致性问题(比如在储值卡充值场景中,调支付接口失败,但程序没有判断该异常,仍然往下执行,给用户卡账充了钱)。
处理这些异常的方式主要有以下几种:
终止执行流。比如储值卡消费场景,如果储值卡扣款接口调失败了,则要终止执行流,防止出现扣款失败但订单状态变成已支付的数据一致性问题(实际上储值卡消费的异常场景远比这里说的复杂,后面我会在单独的文章中分析该场景);
预处理。比如写文件的场景,可以先判断一下目录是否存在,不存在则先创建目录然后再写文件;计算比率时可先判断分母(如抽奖次数)是否为0,如果为0则比率直接为0,不再执行除法运算。
重试。这在远程调用时用得比较多,当接口超时时,一段时间后(如1秒)重试一次,还不行则终止执行流。但需要注意,一般接口超时往往意味着对方系统负载高(或者网络拥塞),大量的重试会加重对方系统负担,终崩溃掉;另外重试也会导致本次请求长时间占用本服务器资源,如果对方系统长时间无法恢复,本系统则会产生大量的请求进程(大家都在那重试),终引发雪崩。如果决定引入重试机制,则需要合理设置超时时间(比如2秒。时间越长请求占用资源越久,越容易导致雪崩),重试次数也不能太多,可能还要结合熔断和限流一起使用。
异步补偿。对于执行流中的非核心节点出现的异常(主要是远程调用失败的场景),我们可以先做异常登记,然后执行流继续往下执行。而后我们通过异步任务去重试这些异常节点。比如用户消费返券的场景,在支付回调的处理流程中会调券接口给用户发券,如果该接口调用失败(超时),我们除了可采用重试机制,还可以在数据库中(或消息队列中)写一条失败待重试的记录,由异步处理程序稍后重试。
相比同步重试机制,异步重试不会导致本次请求占用太久服务器资源,本次请求的后续流程仍然能够快速执行完成;另外异步重试的时间间隔可以更长(如10秒一次,或者随着重试次数而增加时间间隔),这样对被调用系统的压力也更小。
不过异步重试也是有限制条件的。先相关节点可以异步化,后续节点不需要依赖该节点的输出结果;其次业务对该节点的时效性具有较宽的容忍度(如消费返券的场景,即使延迟几秒钟发券也无所谓)。
【免责声明】本文系本网编辑部分转载,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请在30日内与管理员联系,我们会予以更改或删除相关文章,以保证您的权益!更多内容请加danei0707学习了解。欢迎关注“达内在线”参与分销,赚更多好礼。