Write an rpc framework from 0.5 to 1-6: call an abnormal node to automatically retry

Write an rpc framework from 0.5 to 1-6: call an abnormal node to automatically retry

The eureka client sends a heartbeat to the registry every 30s to renew its life. When the registry does not receive the client's signal for a long time, it will think it is down and bring it to a group chat.

In addition, other services also update the local cache at a certain frequency, so it is often not so timely to find out that a small partner has gone offline. The consequence is that a request will be sent to a node that no longer exists, and the connection will be abnormal as a result.

In this regard, we can add a retry mechanism from the framework level, and there are similar mechanisms in spring, but since we are writing our own framework, we need to implement one ourselves.

In the first chapter, we have implemented remote calls through dynamic agents, so start directly from here, and determine whether you need to retry by judging the caught exception.

@Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        if ("toString".equals(method.getName()) && (args == null || args.length == 0)) {
            return rpcServiceInfo.toString();//When debugging, it is always called by ide and throws an exception, which is very annoying
        }
        RpcRequest rpcRequest = new RpcRequest(rpcServiceInfo.getAppName(), rpcServiceInfo.getServiceName(), method.getName());
        if (args != null && args.length> 0) {
            rpcRequest.setOrderedParameter(Arrays.stream(args).map(JsonUtil::toJson).collect(Collectors.toList()));
        }
        int n = 3;//Retry at most 3 times, it is better to change to configurable
        int i = 0;
        RpcClient client = null;
        while (i++ <n) {
            try {
                client = getRpcClient();
                String res = client.invoke(rpcRequest);
                return JsonUtil.fromJson(res, TypeFactory.defaultInstance().constructType(method.getGenericReturnType()));
            } catch (Exception e) {
                if (client == null) {
                    throw e;
                }
                boolean rediscover = needRediscover(e) && i <n;
                log.error("invoke {}/{} {} {} error={} msg={} rediscover={}",
                        i, n, rpcRequest.getKey(), client.getNodeInfo(), e.getClass().getName(), e.getMessage(), rediscover);
                if (rediscover) {
                    try {
                        NodeInfo nodeInfo = rpcClientManager.selectNode(rpcServiceInfo, client.getNodeInfo());
                        client.reconnect(nodeInfo);
                        continue;
                    } catch (RpcNotFoundException e1) {
                        e.addSuppressed(e1);
                    }
                }
                throw e;
            }
        }
        throw new RuntimeException("invoke error");
    }

   /**
     * Determine whether you need to change the instance according to the abnormal type
     */
    private boolean needRediscover(Throwable e) {
        while (e != null) {
            if (e instanceof HttpStatusException) {
               //My custom exception type, if the service is unavailable here (the program is normal but no longer provides service)
                if (((HttpStatusException) e).getStatus() == NOT_AVAILABLE) {
                    return true;
                }
            } else if (e instanceof ConnectException) {
               //The connection is abnormal, it must be gone
                return true;
            }
            e = e.getCause();
        }
        return false;
    }

With the retry mechanism, there is no fear that some guys will suddenly drop the chain. Of course, if they all drop the chain, then there is no fun.