Eureka高可用之Eureka Server复制机制:PeerAwareInstanceRegistryImpl

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36960211/article/details/85278254

还是先提出几个疑问,看本篇文章前最好看过Eureka高可用之Client重试机制:RetryableEurekaHttpClient,要知道Eureka Client只会向一个Server节点进行注册(心跳、状态改变等类似),注册失败时才会尝试下一个server节点。当然正是由于这种机制,才会有Eureka Server的复制行为,个人认为,Eureka Client向每个Eureka Server都发送注册、心跳等事件,会更好的保证一致性

1、如果有4个Eureka Server集群节点,一个Client向其中一个Server进行注册(或者心跳、状态改变事件等),那么这个server是怎样通知剩余3个server集群节点的?

2、一个Eureka Server在收到其他server节点发送的复制信息时,它是怎么样处理的,它会把收到的复制信息继续向其它节点复制吗?

先看一下PeerAwareInstanceRegistryImpl的继承类图,它继承了抽象的实例注册,实现了复制实例注册(能意识到临节点的实例注册),那么它就具备注册实例信息,还能复制给临节点的功能

直接去PeerAwareInstanceRegistryImpl里面看代码,看看是如何将实例信息复制给临节点的(只列出register方法,heartBeat等类似),在收到client的注册信息时,isReplication为false,而当收到其他Eureka Server节点的复制信息时,isReplication为true

@Override
public void register(final InstanceInfo info, final boolean isReplication) {
    int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
    if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
        //默认租约90s,如果用户更改了心跳周期等,使用用户自定义的租约
        leaseDuration = info.getLeaseInfo().getDurationInSecs();
    }
    //调用父类的注册,注册到本地双层Map中
    super.register(info, leaseDuration, isReplication);
    //本地注册完成后,向其他节点复制,注意isReplication这个属性
    //用来判断是client发来的注册,还是其他Eureka Server临节点复制过来的注册
    //如果是复制过来的注册信息,那么就不再向其他Eureka Server节点进行传播复制
    replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
}

super.register(info, leaseDuration, isReplication)先不介绍,这个方法是把实例信息保存到自己的内存中,重点看replicateToPeers方法,在这个方法中,有个for循环遍历了所有的Eureka Server临节点,然后向他们复制这些信息

private void replicateToPeers(Action action, String appName, String id,
                              InstanceInfo info /* optional */,
                              InstanceStatus newStatus /* optional */, boolean isReplication) {
    Stopwatch tracer = action.getTimer().start();
    try {
        if (isReplication) {
            //记录每分钟收到的复制次数
            numberOfReplicationsLastMin.increment();
        }
        // If it is a replication already, do not replicate again as this will create a poison replication
        if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {
            //如果没有Eureka Server临节点,或者是别的Eureka Server复制过来的信息
            //那么就不再向其他临节点进行复制,
            //也就是说既然收到了复制过来的信息,那么其他eureka server节点也会收到
            //所以就没必要再去发送一遍复制了,return。
            return;
        }

        //遍历所有的Eureka Server邻节点,向它们复制register、cancel等信息
        for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) {
            // If the url represents this host, do not replicate to yourself.
            //如果这个节点url是自己的,那么不向自身复制
            if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {
                continue;
            }
            replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node);
        }
    } finally {
        tracer.stop();
    }
}

一个Eureka Server在收到了Client的注册等信息时,会挨个通知其他Eureka Server临节点,复制的流程图也就是下面这个样子

那么再来看看for循环里面的replicateInstanceActionsToPeers方法,在循环里面,根据请求类型action的不同,调用不同PeerEurekaNode的方法,重点还是这个异常处理。

private void replicateInstanceActionsToPeers(Action action, String appName,
                                             String id, InstanceInfo info, InstanceStatus newStatus,
                                             PeerEurekaNode node) {
    try {
        InstanceInfo infoFromRegistry = null;
        CurrentRequestVersion.set(Version.V2);
        switch (action) {
            case Cancel:
                node.cancel(appName, id);
                break;
            case Heartbeat:
                InstanceStatus overriddenStatus = overriddenInstanceStatusMap.get(id);
                infoFromRegistry = getInstanceByAppAndId(appName, id, false);
                node.heartbeat(appName, id, infoFromRegistry, overriddenStatus, false);
                break;
            case Register:
                node.register(info);
                break;
            case StatusUpdate:
                infoFromRegistry = getInstanceByAppAndId(appName, id, false);
                node.statusUpdate(appName, id, newStatus, infoFromRegistry);
                break;
            case DeleteStatusOverride:
                infoFromRegistry = getInstanceByAppAndId(appName, id, false);
                node.deleteStatusOverride(appName, id, infoFromRegistry);
                break;
        }
    } catch (Throwable t) {
        //由于此方法是循环复制操作,如果发生异常不进行处理,直接抛出,那么就不会向后面的节点复制了
        //比如有10个Eureka Server节点,再向第2个复制的时候抛出异常,后面8个节点就收不到复制信息
        //这个地方,只是做log,并没有抛出异常
        logger.error("Cannot replicate information to {} for action {}", node.getServiceUrl(), action.name(), t);
    }
}
扫描二维码关注公众号,回复: 4685201 查看本文章

猜你喜欢

转载自blog.csdn.net/qq_36960211/article/details/85278254