这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
在做rest、dubbo邓远程调用时,出现客户端报错socket timeout超时情况无法避免,但是最终是服务端执行成功了还是未执行成功?当设计高可用时,都是假定调用前服务端已经挂掉,这个假设真的成立吗?基于这两个问题,分析一下超时问题对业务的影响。
基于服务端挂掉导致的超时问题,有三个时间点进行分析,调用的请求到达前挂掉、调用的请求到达后服务端在处理时挂掉、调用的请求到达后服务端处理完成后挂掉。
调用的请求到达前挂掉
这种情形是一种常见的情况,比如client与server网络不通,此时进行调用client会报错socket timeout,对数据没有任何影响,解决方式就是重试,如果是web应用,直接给用户返回调用超时请重试即可。
调用的请求到达后服务端在处理时挂掉
这种情况需要看服务端处理到哪个阶段进行分析,与服务单业务息息相关,所以进行以下分类讨论:
服务端是MySQL
当服务端是MySQL时,先来看MySQL执行流程:
- 查询缓存
- 词法分析语法分析
- 查询优化器
- 执行计划
- 存储引擎执行 以上部分均为查询等操作,没有持久化的写操作,如果服务端在此期间挂掉,客户端收到超时进行重试也没有关系。
- 写redo log,prepare状态
- 更新page cache。如果pacge cache不存在该条记录需要先进行一次读IO读取数据到page cache
- 写bin log,redo log进行commit(如果写bin log后 crash,没有commit还是有不一致情况,如何保证原子性?) 以上6-8是MySQL的Innodb引擎的写操作,当在进行第6步如果此时MySQL重启,client端收到socket timeout,client进行报错“请重试”,没有问题。当在第7步MySQL重启,重启后可以根据redo log恢复已经commit的数据,不能恢复prepare状态的数据,因此这条数据是丢失的。也可以通过重试的方式解决。如果第八步发生MySQL重启,如果写binlog后重启,则主从数据会存在不一致,因为vinlog写入与redo log commit两个操作没办法保证原子性的写入。
服务端是Dubbo provider
比如服务端是一个扣减库存服务,在调用时provider重启,最终数据结果取决于provider中的逻辑。通常provider中是操作数据库进行扣减库存,然后写订单之类。这种业务的出现超时时也可以返回"请重试",但需要数据库事物来保证数据一致性。
调用请求到达服务端处理完成后挂掉
这个场景就是调用provider执行完成,在tcp返回时服务端重启,导致tcp响应无法发出,client报错超时。这种情况没办法用简单重试的方式解决。而是需要client在进行写入时,先判断写入的数据存不存在,不存在再进行写入,存在就进行查询,所以还要将数据定义一个主键来判断数据的唯一性。如果不进行数据唯一性校验,会报错主键冲突的问题。