服务的健壮性跟系统设计有很大关系,前期设计时考虑多一些处理逻辑,可以避免后期出现问题带来的损失以及修复问题的成本。
在前期讨论设计方案时测试同学也需要参与,而不只是埋头设计用例和测试,开发同学可能因为思维局限或者思考疲劳,不一定能想到异常处理,测试同学可以利用自身优势多给一些建议。
如果等测试评审时再跟开发同学强调这些设计,或者是到测试中才发现这类问题,可能就有点迟了,因为越到后期改动代码的影响范围越大,改动的风险也越高。
业务中有一个功能,用户提交申请后要将数据回调给某个第二方系统或者是第三方系统,这时候你要考虑,如果请求失败了怎么办。
重要事件的处理要记录请求情况,特别是异步请求,虽然不影响主流程,但是如果失败了又没有记录就不方便回溯,想重新发起请求也没有入口。
不管是通过HTTP通信还是通过MQ进行通信,都是可以记录请求状态的:前一步成功后将状态置为待推送,推送前置为推送中(为什么先置为推送中,因为有可能结果更快回来),推送后再置为成功/失败,然后针对待推送、推送失败的记录要做定时任务来进行重试。
假设原本交互逻辑是A直接请求B系统,后来新增了C系统作为中间处理系统,A不直接请求B了,或者是原本A请求B的方式由MQ改成了HTTP,接到这样的需求要第一时间想到做开关,即某个配置确定了业务是走方式1还是方式2的逻辑。
对旧功能的大逻辑修改最好做特性开关,新功能要能兼容开关的两种方式都能正常运行。
上线后,当有需求时再开启使用,功能下架时也可以直接将开关关闭,最重要的是,如果上线后出现问题不需要回滚代码,直接将开关关闭即可。
这样出现问题后改动的成本和影响范围是最小的,修复问题花费的时间也是最少的。
如果外部请求进来网关时,网关要先请求内部系统A,然后处理完再返回最终结果,比如外部发起注册请求,而网关要先请求信用系统判断用户资质,这时候要考虑请求信用系统失败的处理。
如果将请求失败和信用系统返回失败一样处理,返回拒绝的结果给外部系统,那是不合理的,因为这属于内部系统的错误,并不是业务处理的结果。这时候应该要返回请求失败给外部系统,让外部系统自己重新发起请求进行重试。
从系统的角度说,请求A系统时失败,相当于并没有进行处理,那么返回失败十分合理;从业务的角度说,虽然是我们自己的服务挂了,但是我们不能就这样把用户拒绝,我们都希望做到收益最大化,那么最好的方式当然是能把用户留住,而不是直接就把用户拒绝。
为了更灵活地方便产品同学配置维度,而不用每次都通过改代码上线来实现,降低业务逻辑实现难度,到配置系统获取配置结果并根据结果进行相应处理是非常常见的实现方式。
但是多请求一套系统就多增加一个不确定性,特别是对配置来说,需要考虑请求失败和返回的配置无法使用、有缺漏的情况如何处理,至少不应该阻塞正常流程,比如可以在请求配置失败时使用默认配置的方式降低风险。
其实这几个都是“兜底”的设计方式,遇到一个功能不要只想到正常场景,要思考可能出现的异常场景,最重要的是出现这些异常场景的处理方式,尽可能覆盖异常情况,才能做到胸有成竹地上线。