Java-Interview-Advanced/docs/distributed-system/interface-idempotence.md

85 lines
4.3 KiB
Markdown
Raw Normal View History

2020-04-25 11:55:58 +00:00
接口幂等性实现起来非常的简单,不打算带着大家手写代码
1数据库唯一索引
2基于Redis实现一套幂等性防重框架
对于插入类的操作,一般都是建议大家要在数据库表中设计一些唯一索引
你如果有一个订单被支付了此时就要通知wms创建一个对应发货单也是数据库里的一个表仓库里的人会看到这个发货单此时他就会根据发货单的信息从仓库里进行拣货打包封装交给物流公司
发货单
id order_id 订单金额 发货地址 xxxx
对order_id就可以建立一个唯一索引你插入发货单的时候同一个order_id最多只能对应一个发货单不可能说同样的一个order_id对应了多个发货单
订单服务 -> wms服务出现了重试导致第二次请求再次让人家创建这个订单的发货单create语句order_id触发了唯一索引约束
扣减库存、累加积分,更新,很难通过数据库唯一索引来保证
基于Redis实现一套接口的防重框架
你得做一个类似spring mvc里的拦截器这样的东西在这个拦截器里他会拦截所有的请求对所有的请求都会提取请求对应的参数GET请求、POST请求、PUT请求有些参数是跟在URL地址里的?xx=xx&xx=xx
POST、PUT可能是请求体里的可能是一个JSON格式
把参数拼接在一起作为key去redis中判断一下是否存在这个key之前附加这些参数的请求是否发起过如果没有的话此时就可以把这些参数+接口名称作为一个key存储到redis中去
然后呢,把请求放行,去执行这个请求
如果说人家重试再次发起一个这个请求此时就可以判断出来参数组成的key在redis中已经存在了此时就不让执行这个请求了认为是重复调用了
考虑很多问题,幂等不幂等,通用框架,需要一个公司所有的接口都按照指定的参数来传递,还有很多业务语义的问题
第一次发起一个请求直接把请求key放入redis但是他执行的过程中失败了而且还阻塞了一段时间此时人家再次重试发起第二次请求这个时候按照上述的框架逻辑就会把请求拦截下来了
到底是不是要对所有接口都开启这么一个东西呢?
每个接口如果执行成功了之后我可以设置一个每个接口调用的时候执行成功之后做一个后拦截器如果成功了就把请求对应的参数拼接为key放入redis中
有没有可能是第一次请求发送过来在执行过程中时间长了比如需要1.3秒才执行完毕此时人家发现超过1s了直接重试第二次请求过来了也在正常的执行
第一次请求1.3秒之后执行成功了,第二次请求也执行成功了
只要一个服务希望对自己的接口开启幂等性防重功能就把你开发好的拦截器对应的jar包通过maven引入一个依赖就可以了
中大型互联网公司里也没做一个统一的防重幂等框架,其实一般都是各个服务对自己核心的接口,如果要保证幂等性的话,每个服务根据自己的业务逻辑来实现,而且仅仅是对少数核心接口做幂等性保障
核心接口,库存服务,扣减库存接口
定制化的去针对接口开发幂等性的机制比如说一旦库存扣减成功之后就立马要写一条数据到redis里去order_id_11356_stock_deduct写入redis中如果写入成功就说明之前这个订单的库存扣减没人执行过
但是如果此时有一些重试的请求过来了调用了你的库存扣减接口他同时也进行了库存的扣减但是他用同样的一个keyorder_id_11356_stock_deduct写入redis中此时会发现已经有人写过keykey已经存在了
此时你就应该直接对刚才的库存扣减逻辑做一个反向的回滚逻辑update product_stock set stock = stock - 100update product_stock set stock = stock + 100反向逻辑回滚掉自己避免说重复扣减库存
核心接口幂等性都是自己保证的人家可能会重试调用你的接口对于create类的操作用唯一索引来保证对update类的操作建议在核心接口里基于自己的业务逻辑配合上redis来保证幂等性