从零开始手写 dubbo rpc 框架-11-fail 失败策略

fail 失败策略

说明

当我们调用服务失败的时候,有很多策略。

比如:

  • FailFast

快速失败

  • FailOver

尝试下一次调用

等等其他各种策略。

实现思路

不同的失败策略方式只是处理失败的方式不同而已。

这个主要放在客户端,当调用失败的时候,重新进行尝试即可。

代码实现

失败策略接口

/**
 * 失败策略
 * @author binbin.hou
 * @since 0.1.1
 */
public interface FailStrategy {

    /**
     * 失败策略
     * @param context 远程调用上下文
     * @return 最终的结果值
     * @since 0.1.1
     */
    Object fail(final RemoteInvokeContext context);

}

FailOver 策略

主要展示下这个策略,其他的类似。

这里是默认失败后重试两次,因为 rpc 和 mq 有些不同。

一般调用都有超时的限制,不适合重试太多次。

/**
 * 如果调用遇到异常,则进行尝试其他 server 端进行调用。
 * (1)最大重试次数=2  不能太多次
 * (2)重试的时候如何标识重试次数还剩多少次?
 * (3)如何在失败的时候获取重试相关上下文?
 *
 * @author binbin.hou
 * @since 0.1.1
 */
@ThreadSafe
class FailOverStrategy implements FailStrategy {

    @Override
    public Object fail(final RemoteInvokeContext context) {
        try {
            final Class returnType = context.request().returnType();
            final RpcResponse rpcResponse = context.rpcResponse();
            return RpcResponses.getResult(rpcResponse, returnType);
        } catch (Exception e) {
            Throwable throwable = e.getCause();
            if(throwable instanceof RpcTimeoutException) {
                throw new RpcRuntimeException();
            }

            // 进行失败重试。
            int retryTimes = context.retryTimes();
            if(retryTimes > 0) {
                // 进行重试
                retryTimes--;
                context.retryTimes(retryTimes);
                return context.remoteInvokeService()
                        .remoteInvoke(context);
            } else {
                throw e;
            }
        }
    }

}

测试代码

注册中心

启动

服务端

  • server1

正常服务启动

  • server2

启动异常服务,实现如下:

public class CalculatorServiceErrorImpl implements CalculatorService {

    public CalculateResponse sum(CalculateRequest request) {
        throw new RuntimeException("服务端异常");
    }

}

客户端

  • 测试代码
public static void main(String[] args) {
    // 服务配置信息
    ReferenceConfig<CalculatorService> config = ClientBs.newInstance();
    config.serviceId(ServiceIdConst.CALC);
    config.serviceInterface(CalculatorService.class);
    // 自动发现服务
    config.subscribe(true);
    config.registerCenter(ServiceIdConst.REGISTER_CENTER);

    CalculatorService calculatorService = config.reference();
    CalculateRequest request = new CalculateRequest();
    request.setOne(10);
    request.setTwo(20);

    // 循环 1 次,验证 fail-over
    CalculateResponse response = calculatorService.sum(request);
    System.out.println(response);
}
  • 测试日志

这里重试了2次,可以通过日志 [Client] start call remote with request 观察到。

[INFO] [2019-11-01 22:29:52.848] [main] [c.g.h.r.c.p.i.RemoteInvokeServiceImpl.remoteInvoke] - [Client] start call channel id: 502b73fffec4485c-00001ea0-00000001-d5e67f0043cf621c-70d8b9e8
[INFO] [2019-11-01 22:29:52.853] [main] [c.g.h.r.c.p.i.RemoteInvokeServiceImpl.remoteInvoke] - [Client] start call remote with request: DefaultRpcRequest{seqId='8c71aa3a1dfd4d0d8d6b7f5c82a3c119', createTime=1572618592829, serviceId='calc', methodName='sum', paramTypeNames=[com.github.houbb.rpc.server.facade.model.CalculateRequest], paramValues=[CalculateRequest{one=10, two=20}], returnType=class com.github.houbb.rpc.server.facade.model.CalculateResponse}
[INFO] [2019-11-01 22:29:52.854] [main] [c.g.h.r.c.i.i.DefaultInvokeService.addRequest] - [Client] start add request for seqId: 8c71aa3a1dfd4d0d8d6b7f5c82a3c119, timeoutMills: 60000
[INFO] [2019-11-01 22:29:52.856] [main] [c.g.h.r.c.i.i.DefaultInvokeService.getResponse] - [Client] seq 8c71aa3a1dfd4d0d8d6b7f5c82a3c119 对应结果为空,进入等待
[INFO] [2019-11-01 22:29:52.869] [nioEventLoopGroup-4-1] [c.g.h.r.c.i.i.DefaultInvokeService.addResponse] - [Client] 获取结果信息,seqId: 8c71aa3a1dfd4d0d8d6b7f5c82a3c119, rpcResponse: DefaultRpcResponse{seqId='8c71aa3a1dfd4d0d8d6b7f5c82a3c119', error=com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException, result=null}
[INFO] [2019-11-01 22:29:52.870] [nioEventLoopGroup-4-1] [c.g.h.r.c.i.i.DefaultInvokeService.addResponse] - [Client] seqId:8c71aa3a1dfd4d0d8d6b7f5c82a3c119 信息已经放入,通知所有等待方
[INFO] [2019-11-01 22:29:52.870] [nioEventLoopGroup-4-1] [c.g.h.r.c.i.i.DefaultInvokeService.addResponse] - [Client] seqId:8c71aa3a1dfd4d0d8d6b7f5c82a3c119 remove from request map
[INFO] [2019-11-01 22:29:52.871] [nioEventLoopGroup-4-1] [c.g.h.r.c.h.RpcClientHandler.channelRead0] - [Client] response is :DefaultRpcResponse{seqId='8c71aa3a1dfd4d0d8d6b7f5c82a3c119', error=com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException, result=null}
[INFO] [2019-11-01 22:29:52.871] [main] [c.g.h.r.c.i.i.DefaultInvokeService.getResponse] - [Client] seq 8c71aa3a1dfd4d0d8d6b7f5c82a3c119 对应结果已经获取: DefaultRpcResponse{seqId='8c71aa3a1dfd4d0d8d6b7f5c82a3c119', error=com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException, result=null}
[INFO] [2019-11-01 22:29:52.873] [main] [c.g.h.r.c.p.i.RemoteInvokeServiceImpl.remoteInvoke] - [Client] start call channel id: 502b73fffec4485c-00001ea0-00000001-d5e67f0043cf621c-70d8b9e8
[INFO] [2019-11-01 22:29:52.875] [main] [c.g.h.r.c.p.i.RemoteInvokeServiceImpl.remoteInvoke] - [Client] start call remote with request: DefaultRpcRequest{seqId='048d0b5a5db24233abffd80bed6513c4', createTime=1572618592829, serviceId='calc', methodName='sum', paramTypeNames=[com.github.houbb.rpc.server.facade.model.CalculateRequest], paramValues=[CalculateRequest{one=10, two=20}], returnType=class com.github.houbb.rpc.server.facade.model.CalculateResponse}
[INFO] [2019-11-01 22:29:52.875] [main] [c.g.h.r.c.i.i.DefaultInvokeService.addRequest] - [Client] start add request for seqId: 048d0b5a5db24233abffd80bed6513c4, timeoutMills: 60000
[INFO] [2019-11-01 22:29:52.876] [main] [c.g.h.r.c.i.i.DefaultInvokeService.getResponse] - [Client] seq 048d0b5a5db24233abffd80bed6513c4 对应结果为空,进入等待
[INFO] [2019-11-01 22:29:52.884] [nioEventLoopGroup-4-1] [c.g.h.r.c.i.i.DefaultInvokeService.addResponse] - [Client] 获取结果信息,seqId: 048d0b5a5db24233abffd80bed6513c4, rpcResponse: DefaultRpcResponse{seqId='048d0b5a5db24233abffd80bed6513c4', error=com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException, result=null}
[INFO] [2019-11-01 22:29:52.885] [nioEventLoopGroup-4-1] [c.g.h.r.c.i.i.DefaultInvokeService.addResponse] - [Client] seqId:048d0b5a5db24233abffd80bed6513c4 信息已经放入,通知所有等待方
[INFO] [2019-11-01 22:29:52.885] [nioEventLoopGroup-4-1] [c.g.h.r.c.i.i.DefaultInvokeService.addResponse] - [Client] seqId:048d0b5a5db24233abffd80bed6513c4 remove from request map
[INFO] [2019-11-01 22:29:52.886] [nioEventLoopGroup-4-1] [c.g.h.r.c.h.RpcClientHandler.channelRead0] - [Client] response is :DefaultRpcResponse{seqId='048d0b5a5db24233abffd80bed6513c4', error=com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException, result=null}
[INFO] [2019-11-01 22:29:52.886] [main] [c.g.h.r.c.i.i.DefaultInvokeService.getResponse] - [Client] seq 048d0b5a5db24233abffd80bed6513c4 对应结果已经获取: DefaultRpcResponse{seqId='048d0b5a5db24233abffd80bed6513c4', error=com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException, result=null}
[INFO] [2019-11-01 22:29:52.886] [main] [c.g.h.r.c.p.i.RemoteInvokeServiceImpl.remoteInvoke] - [Client] start call channel id: 502b73fffec4485c-00001ea0-00000001-d5e67f0043cf621c-70d8b9e8
[INFO] [2019-11-01 22:29:52.887] [main] [c.g.h.r.c.p.i.RemoteInvokeServiceImpl.remoteInvoke] - [Client] start call remote with request: DefaultRpcRequest{seqId='8c6002afa9c4471a8d2f6c46f2efd8e0', createTime=1572618592829, serviceId='calc', methodName='sum', paramTypeNames=[com.github.houbb.rpc.server.facade.model.CalculateRequest], paramValues=[CalculateRequest{one=10, two=20}], returnType=class com.github.houbb.rpc.server.facade.model.CalculateResponse}
[INFO] [2019-11-01 22:29:52.888] [main] [c.g.h.r.c.i.i.DefaultInvokeService.addRequest] - [Client] start add request for seqId: 8c6002afa9c4471a8d2f6c46f2efd8e0, timeoutMills: 60000
[INFO] [2019-11-01 22:29:52.888] [main] [c.g.h.r.c.i.i.DefaultInvokeService.getResponse] - [Client] seq 8c6002afa9c4471a8d2f6c46f2efd8e0 对应结果为空,进入等待
[INFO] [2019-11-01 22:29:52.896] [nioEventLoopGroup-4-1] [c.g.h.r.c.i.i.DefaultInvokeService.addResponse] - [Client] 获取结果信息,seqId: 8c6002afa9c4471a8d2f6c46f2efd8e0, rpcResponse: DefaultRpcResponse{seqId='8c6002afa9c4471a8d2f6c46f2efd8e0', error=com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException, result=null}
[INFO] [2019-11-01 22:29:52.897] [nioEventLoopGroup-4-1] [c.g.h.r.c.i.i.DefaultInvokeService.addResponse] - [Client] seqId:8c6002afa9c4471a8d2f6c46f2efd8e0 信息已经放入,通知所有等待方
[INFO] [2019-11-01 22:29:52.897] [nioEventLoopGroup-4-1] [c.g.h.r.c.i.i.DefaultInvokeService.addResponse] - [Client] seqId:8c6002afa9c4471a8d2f6c46f2efd8e0 remove from request map
[INFO] [2019-11-01 22:29:52.897] [nioEventLoopGroup-4-1] [c.g.h.r.c.h.RpcClientHandler.channelRead0] - [Client] response is :DefaultRpcResponse{seqId='8c6002afa9c4471a8d2f6c46f2efd8e0', error=com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException, result=null}
[INFO] [2019-11-01 22:29:52.898] [main] [c.g.h.r.c.i.i.DefaultInvokeService.getResponse] - [Client] seq 8c6002afa9c4471a8d2f6c46f2efd8e0 对应结果已经获取: DefaultRpcResponse{seqId='8c6002afa9c4471a8d2f6c46f2efd8e0', error=com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException, result=null}
Exception in thread "main" com.github.houbb.rpc.common.exception.RpcRuntimeException: com.github.houbb.rpc.common.exception.RpcRuntimeException: java.lang.reflect.InvocationTargetException

可以改进的地方

失败的服务端处理

因为我这边默认实现是 random,可能会出现多次随机到同一台服务器的情况。

还有就是如果调用一次出现异常,应该把这个异常的服务器,暂时移除。

等待后期心跳重新连接。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值