当我们调用服务失败的时候,有很多策略。
比如:
- FailFast
快速失败
- FailOver
尝试下一次调用
等等其他各种策略。
不同的失败策略方式只是处理失败的方式不同而已。
这个主要放在客户端,当调用失败的时候,重新进行尝试即可。
/**
* 失败策略
* @author binbin.hou
* @since 0.1.1
*/
public interface FailStrategy {
/**
* 失败策略
* @param context 远程调用上下文
* @return 最终的结果值
* @since 0.1.1
*/
Object fail(final RemoteInvokeContext context);
}
主要展示下这个策略,其他的类似。
这里是默认失败后重试两次,因为 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,可能会出现多次随机到同一台服务器的情况。
还有就是如果调用一次出现异常,应该把这个异常的服务器,暂时移除。
等待后期心跳重新连接。