weibocom / motan

A cross-language remote procedure call(RPC) framework for rapid development of high performance distributed services.
Other
5.88k stars 1.78k forks source link

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

Open ityongman opened 11 months ago

ityongman commented 11 months ago

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 commented 11 months ago

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

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

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

ityongman commented 11 months ago

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

rayzhang0603 commented 11 months ago

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

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

ityongman commented 11 months ago

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

rayzhang0603 commented 11 months ago

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

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

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

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