前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从0.5到1写个rpc框架 - 6:调用异常节点自动重试

从0.5到1写个rpc框架 - 6:调用异常节点自动重试

作者头像
acupt
发布2019-08-26 11:49:43
5620
发布2019-08-26 11:49:43
举报

eureka client每隔30s向注册中心发送心跳来给自己续命,当注册中心长时间没收到client的信号,就会认为它挂掉了,把它提出群聊。

再加上其它服务也按照一定频率更新本地缓存,因此往往不会那么及时地发现曾经的小伙伴已经下线了。导致的后果就是,会向不再存在的节点发送请求,结果连接异常。

对此,我们可以从框架层面加入一个重试机制,spring里面类似的机制也有,但既然在写自己的框架,那就自己实现一个。

在第一章里已经实现了通过动态代理执行远程调用,那么直接从这里入手,通过判断捕获的异常来判断是否需要重试。

@Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        if ("toString".equals(method.getName()) && (args == null || args.length == 0)) {
            return rpcServiceInfo.toString();//debug时老是被ide调用然后抛异常,很烦
        }
        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; // 最多重试3次,改成可配置的更好
        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");
    }

    /**
     * 根据异常类型判断是否需要换个实例
     */
    private boolean needRediscover(Throwable e) {
        while (e != null) {
            if (e instanceof HttpStatusException) {
                // 我自定义的异常类型,这里如果是服务不可用(程序虽然正常但不再提供服务)
                if (((HttpStatusException) e).getStatus() == NOT_AVAILABLE) {
                    return true;
                }
            } else if (e instanceof ConnectException) {
                // 连接异常,想必是不在了
                return true;
            }
            e = e.getCause();
        }
        return false;
    }

有了重试机制,就不怕某些家伙突然掉链子了,当然如果全部掉链子那就没得玩了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一杯82年的JAVA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
微服务引擎 TSE
微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档