Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

endpoints心跳检查,校验节点是否存活的逻辑是不是存在问题 #1045

Open
ityongman opened this issue Sep 20, 2023 · 5 comments

Comments

@ityongman
Copy link

public class HeartbeatClientEndpointManager implements EndpointManager {

private ConcurrentMap<Client, HeartbeatFactory> endpoints = new ConcurrentHashMap<>();

// 一般这个类创建的实例会比较少,如果共享的话,容易“被影响”,如果某个任务阻塞了
private ScheduledExecutorService executorService = null;

@Override
public void init() {
    executorService = Executors.newScheduledThreadPool(1);
    executorService.scheduleWithFixedDelay(new Runnable() {
        @Override
        public void run() {

            for (Map.Entry<Client, HeartbeatFactory> entry : endpoints.entrySet()) {
                Client endpoint = entry.getKey();

                try {
                    // 如果节点是存活状态,那么没必要走心跳
                    if (endpoint.isAvailable()) { **// 这个地方是不是应该检测channel是否可用**
                        continue;
                    }

                    HeartbeatFactory factory = entry.getValue();
                    endpoint.heartbeat(factory.createRequest());
                } catch (Exception e) {
                    LoggerUtil.error("HeartbeatEndpointManager send heartbeat Error: url=" + endpoint.getUrl().getUri() + ", " + e.getMessage());
                }
            }

        }
    }, MotanConstants.HEARTBEAT_PERIOD, MotanConstants.HEARTBEAT_PERIOD, TimeUnit.MILLISECONDS);
    ShutDownHook.registerShutdownHook(new Closable() {
        @Override
        public void close() {
            if (!executorService.isShutdown()) {
                executorService.shutdown();
            }
        }
    });
}

}

// 如果节点是存活状态,那么没必要走心跳
if (endpoint.isAvailable()) {
continue;
}
着部分代码在检测节点存活状态, Endpoint检测的是当前终端是否可用, 应该检查 ((NettyClient)endpoint).channels 是不是都是avaliable, 如果存在不可用状态,调用 endpoint.heartbeat 进行心跳检查

@rayzhang0603
Copy link
Collaborator

Motan的心跳是针对Endpoint(也就是Client)维度的,主要目的是为了解决因链路问题或者服务端压力(比如单节点连续失败10次)触发熔断后的恢复,服务正常的情况下是不会额外执行心跳操作的。

当client被熔断后,发送心跳会触发远程请求,只要请求成功就会恢复client的状态,所以心跳只需要检测client的状态就可以了。

至于单个channel维度的available,是由client内的连接池进行管理的,在获取或者交回链接时会进行单个链接的状态进行判断。

@ityongman
Copy link
Author

如果client所在的服务先启动,服务端后启动, 启动后EndPoint(Client)是OK的,但是检测到的服务端是存在问题的,如果这个检测放在channel中才检测,不管业务过多久进行rpc请求,第一次都会存在问题, motan应该提供机制在服务端后启动场景下, 客户端和服务端能重连

@rayzhang0603
Copy link
Collaborator

前面的回复提到了这个心跳机制是为了解决服务运行中,因链路问题导致熔断后,节点可以自动恢复,并不是用来解决启动时无效server节点的。

你提到的client启动时server节点未启动的问题是可以通过注册中心避免的,server端得先启动完成,并且打开提供服务的开关后,才会触发向注册中心注册server节点(一般server节点在启动后,还需要进行状态自检、预热等行为,保证服务可以正常工作后才会打开开关对外提供服务)。然后client端才能从注册中心发现这个server节点。

@ityongman
Copy link
Author

现在使用的是motan directUrl方式, 没使用注册中心, motan提供了directUrl能力,这种方式应该和有注册中心一样的能力,而不是配置方式不同,体现能力不同;针对注册中心方式,client也是和注册中心进行交互获取 server注册的信息, 如果是directUrl方式应该提供直接检查server是否可用的能力, 不然会存在 “不管业务过多久进行rpc请求,第一次都会提示server不可用的问题”

@rayzhang0603
Copy link
Collaborator

DirectRegistry是一个简单的注册中心实现,是为了方便直连调试等非线上场景,并没有做server节点可用性检测设计。如果direct注册中心要实现与其他注册中心等同的能力,需要在DirectRegistry中对serve节点的可用性进行周期性检测,在可用性发生变更时触发回调动态通知client当前可用的serve节点。这块我们会考虑在后续的版本中增强DirectRegistry。

一般线上业务不建议使用DirectRegistry这种服务发现方式,这种方式不太适合ip经常动态变动的云环境以及具有动态扩容能力的server。

server节点的不可用是可以发生在任何时刻的,client的熔断机制并不特别针对服务启动或者服务运行中,熔断机制是保证节点不可用后可以熔断,并在节点可用后可以恢复。链接可用性的管理是交给链接池负责的,如果请求时没有可用的链接,链接池会触发重新建连。

熔断机制是处理偶发server节点不可用的意外场景的,如果经常会出现server节点不可用的情况,建议使用动态注册中心方式(例如zk),或者优化服务的运维流程

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants