Dubbo-Retry超时重试导致数据重复问题
使用dubbo的重试机制造成了不小的困恼,就希望dubbo能在数据写入操作不进行重试,读取操作进行重试,是否可有相应的配置实现?发现了一篇文章,也是处理重试方面的问题的。转载记录下
原文地址:https://kute.github.io/2018/08/06/Dubbo-Retry%E8%B6%85%E6%97%B6%E9%87%8D%E8%AF%95%E9%98%B2%E6%AD%A2%E6%95%B0%E6%8D%AE%E9%87%8D%E5%A4%8D%E5%B0%8F%E8%AE%B0.html
dubbo 服务调用过程中配置了重试,对于 非幂等性接口 ,由于 网络 或者 服务端处理速度较慢,发生超时,重试导致 接口被多次调用进行业务逻辑处理,发生脏数据等问题
1、在配置了重试机制的基础上,修改 单个方法的 “重试” 的配置 + (调用方异常捕获 或者 配置降级策略)
dubbo默认未提供 方法 级别 的注解(xml配置是有的),只有 @Service @Reference,重试次数是对 整个service 中的所有方法生效,通过修改某些对于 幂等性 要求较高的“方法”级别的重试配置(如 取消重试,减少重试),避免因重试带来的脏数据问题
问题 1:
对于某些关键服务调用若配置超时不重试,可能引起 数据丢失问题,需要添加 降级
处理: 如:扔到队列,异步消费,消费时进行数据一致性校验,如 数据 是否真正入库配置 降级措施
1.1、服务调用方 添加 parameters 参数,设置 重试次数
如下 ICityService接口服务的方法 findCity 的重试次数 设置为 2,而 此服务的重试次数默认为5。 (类似的对于超时时间 都可以 这么设置:parameters = {“#myMethod#.#property#”, “#propertyValue#”})
单独设置 非幂等方法的容错模式为:failfast(快速失败),只调用一次,调用超时则立即失败,然后调用方(consumer) 进行异常捕获,提供降级逻辑。
服务降级 是在 业务调用方 实际调用失败后(或者 强制直接走降级)执行 降级策略,以保障服务可用性。
服务降级 可以 配置 在整个 Service 上,也可以单独为 Service 的 某个方法 配置降级策略
基本方法:配置 <dubbo:reference /> 或者 @Reference 的mock 属性, 示例如下
其中降级处理类如下:
这里 对于 findCity 方法的降级处理类 直接用了 ICityServiceMock ,如果我们只是对部分方法 有降级的需求,那么可以提供一个 模板类(适配器),降级处理类继承模板类,然后只实现必要的方法
try{…}catch(..){…} 或者 自定义 切面 或者 filter 处理异常 对于 异常,参见:其他
客户端:每次进行rpc调用前,生成唯一ID(UUID),传递到服务端
服务端:首先判断 以 UUID 为key在redis中是否存在,不存在 则可以执行正常逻辑;若存在,则认为是重试(重复调用)
客户端:
对有调用duubo rpc的方法添加切面,以注解声明的接口类以及调用的方法名 为 key,值为UUID.randomUUID(),存于 RpcContext 发送到 服务端。
服务端:通过在对dubbo方法添加切面,判断 redis中是否 存在 以 此UUID为key的缓存,若存在则判定为 重复调用,直接返回,否则 存于redis并设置过期时间
伪码如下:
客户端:
服务端:
代码简单实现:
为客户端提供的注解:
切面:
客户端使用注解:
服务端提供注解:
切面:
服务端使用:
1、provider(服务提供方)设置tps以及tps.interval :控制请求频率
2、dubbo异常处理(ExceptionFilter) 顺序
如果是checked异常则直接抛出
如果是unchecked异常但是在接口上有声明,也会直接抛出
如果异常类和接口类在同一jar包里,直接抛出
如果是JDK自带的异常,直接抛出
如果是Dubbo的异常,直接抛出
其余的都包装成RuntimeException然后抛出(避免异常在Client出不能反序列化问题)
对于 非幂等 接口,如果可以 借助 数据库唯一索引 保证接口幂等,但是 还是存在 接口调用资源浪费。