四、eureka服务端同步注册操作

所有文章

https://www.cnblogs.com/lay2017/p/11908715.html

正文

eureka服务端注册服务一文中,我们提到register方法做了两件事

1)注册服务实例信息到当前节点

2)复制服务实例信息到其它节点

本文关注第二点,复制服务实例信息到其它节点。为此,我们先简单看一下register方法的代码

打开PeerAwareInstanceRegistryImpl类的register方法

public void register(final InstanceInfo info, final boolean isReplication) {
    // ...
    super.register(info, leaseDuration, isReplication);
    // 复制到其它节点
    replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
}

replicateToPeers担负了复制的功能,跟进它

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();
        }
        // 如果本次register操作本身就是复制,就不再复制到其它节点了
        if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {
            return;
        }

        // 遍历左右节点
        for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) {
            // 如果是当前节点,直接跳过
            if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {
                continue;
            }
            // 复制操作触发
            replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node);
        }
    } finally {
        tracer.stop();
    }
}

这里其实就是向所有其它节点发送注册请求

跟进replicateInstanceActionsToPeers,可以看到除了register还有一些其它操作一样是需要同步到其它节点的。这里我们只关注register操作

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) {
        // ... 
    }
}

跟进register方法,复制实例信息被构造成了一个任务丢给了batchingDispatcher去异步执行,如果失败将会重试。

public void register(final InstanceInfo info) throws Exception {
    long expiryTime = System.currentTimeMillis() + getLeaseRenewalOf(info);
    // 异步执行任务
    batchingDispatcher.process(
            taskId("register", info),
            // 构造了一个复制实例信息的任务
            new InstanceReplicationTask(targetHost, Action.Register, info, null, true) {
                public EurekaHttpResponse<Void> execute() {
                    return replicationClient.register(info);
                }
            },
            expiryTime
    );
}

InstanceReplicationTask的主要逻辑就是调用了replicationClient的register方法,跟进它

这里以Jersey的实现为例

public EurekaHttpResponse<Void> register(InstanceInfo info) {
    String urlPath = "apps/" + info.getAppName();
    ClientResponse response = null;
    try {
        Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder();
        addExtraHeaders(resourceBuilder);
        response = resourceBuilder
                .header("Accept-Encoding", "gzip")
                .type(MediaType.APPLICATION_JSON_TYPE)
                .accept(MediaType.APPLICATION_JSON)
                .post(ClientResponse.class, info);
        return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
    } finally {
        // ...
    }
}

可以看到,其实就是发送了http请求到其它eureka Server端

总结

eureka Server会将register、cancel、heartbeat等操作从一个节点同步发送到其它节点,从而实现了复制的功能。eureka和zookeeper不一样,它是遵循ap的,所以采用了最终一致性,并没有像zookeeper一样选择强一致。eureka Server之间的维持最终一致性的细节点还是很多的,比如失败重试、超时、心跳、实例的版本号、同一个节点的锁控制等等。有兴趣的话可以详细了解它。

猜你喜欢

转载自www.cnblogs.com/lay2017/p/11919370.html