Diagramm + Quellcode zur Erläuterung des Synchronisierungsmechanismus für die Cluster-Registrierung von Eureka Server

Machen Sie es sich zur Gewohnheit, gemeinsam zu schreiben! Dies ist der 9. Tag meiner Teilnahme an der „Nuggets Daily New Plan · April Update Challenge“, klicken Sie hier, um die Details der Veranstaltung anzuzeigen

Logik zum Aussortieren des Eureka-Server-Dienstes

Beharrlichkeit bei der Leistung ist wichtiger als Hartnäckigkeit beim Scheitern - Laroshvko

Verwandte Artikel
Diagramm + Erläuterung des Quellcodes Diagramm zur Analyse des Startvorgangs des Eureka-Servers
+ Erläuterung des Quellcodes Diagramm zur Analyse des Startvorgangs des Eureka-Clients + Erläuterung des Quellcodes Ablaufdiagramm der
Registry-Cache des Eureka-Servers + Erläuterung des Quellcodes Ablaufdiagramm der
Pull-Registrierung des Eureka -Clients
+ Erläuterung des Quellcodes Eureka-Client-Service Registrierungsprozess
Diagramm + Quellcode zur Erläuterung des Heartbeat-Mechanismus des Eureka-Clients
Flussdiagramm + Quellcode zur Erläuterung der Offline-Prozessanalyse des Eureka-Clients
Diagramm + Quellcode zur Erläuterung der Eliminierungslogik des Eureka-Server-Dienstes

Kernschema

    Interkommunikation zwischen Dienstknoten des Eureka-Servers, sodass unabhängig davon, welcher Dienstknoten ausgefallen ist, der Zugriff auf andere Dienstknoten auch auf den Knoten zugreifen kann, auf den zugegriffen werden soll. In der Abbildung registriert sich beispielsweise der Client bei Dienst A. Wenn die Registrierung erfolgreich ist, Der aktuelle Knoten wird mit anderen Dienstknoten synchronisiert, was der Cluster-Synchronisationsmechanismus ist
Bild.png

Wo Sie mit Ihrer Analyse beginnen können

    Da die Registrierung als Beispiel verwendet wird, sehen wir uns an, wie der Vorgang nach der Registrierung die Clusterinformationen synchronisiert.Betrachten wir zuerst den Registrierungscode.

public void register(final InstanceInfo info, final boolean isReplication) {
    // 90
    int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
    if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
        leaseDuration = info.getLeaseInfo().getDurationInSecs();
    }
    /**
     * 真正的注册是在父类的方法中
     */
    super.register(info, leaseDuration, isReplication);
    /**
     * 同步当前实例信息到其他的eureka-server节点中
     */
    replicateToPeers(Action.Register, info.getAppName(), 
                     info.getId(), info, null, isReplication);
}
复制代码

    Die Registrierungsmethode wurde im Artikel zur Client-Registrierung analysiert, wobei wir uns hauptsächlich mit der Synchronisierung der nachfolgenden Knoteninformationen befassen.

replicateToPeers(Action.Register, info.getAppName(), info.getId(), 
                 info, null, isReplication);
复制代码

Kernprozess

Code-Kern-Flussdiagramm

Bild.png
    Zur Synchronisierung werden unsere Instanzregistrierung, Offline, Heartbeat, Statusaktualisierung usw. mit anderen Serviceknoten synchronisiert.

public enum Action {
    // 心跳、注册、下线、状态更新
    Heartbeat, Register, Cancel, StatusUpdate, DeleteStatusOverride;
}
复制代码

    Nur die beim Aufruf der Methode „replicateToPeers“ übergebenen Werte sind unterschiedlich.Betrachten wir zunächst den Registrierungsprozess.

Haben Sie die Registrierungssynchronisierung selbst initiiert oder wurde sie von jemand anderem initiiert?

    如果是别人发起的同步那么就放入到自己的注册表中就好了,如果不是的话那么就是自己发起的同步,这样一来就是将自己过滤除去同步给其他人,isReplication 这个值就是控制它的

private void replicateToPeers(Action action, String appName, String id,
                      InstanceInfo info /* optional */,
                      InstanceStatus newStatus /* optional */,
                      boolean isReplication) {
    /**
     * isReplication 如果当前实例有eureka-server已经同步过的话那么
     * 就不用其他eureka-server在进行同步了
     * 或者服务节点为null的话也不需要同步了
     */
    if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {
        return;
    }

for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) {
    /**
     * 同步的时候排除自己,只需要同步给其他eureka-server即可
     */
    if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {
        continue;
    }
    /**
     * 如果是某台eureka client来找eureka server进行注册,此时会给其他所有的你配置的
     * eureka server都同步这个注册请求,
     * 此时一定会基于jersey,调用其他所有的eureka server的restful接口,
     * 去执行这个服务实例的注册的请求
     */
    replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node);
    }
}
复制代码

遍历所有节点去进行注册当前实例

    其实就是在遍历peerEurekaNodes.getPeerEurekaNodes() 里面的值,这里面的值是在服务端初始化的时候获取的其他服务中心节点的值,所以就知道里面有多少服务中心了,将要注册的客户端注册给其他服务中心

replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node)
    
private void replicateInstanceActionsToPeers(Action action, 
       String appName, String id, InstanceInfo info, 
              InstanceStatus newStatus, PeerEurekaNode node) {
    InstanceInfo infoFromRegistry;
    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;
    }
}
复制代码

    上面的switch 操作分别对应了下线、发送心跳、注册、状态更新等操作,我们这里是注册所以看一下node.register(info) 方法

节点注册

    客户端注册之前我们都讲过了,就是将当前的服务节点通过jersey框架进行访问之后在走一遍注册逻辑,我们看看同步的时候是怎么操作的是不是我们想的样子

public void register(final InstanceInfo info) throws Exception {
    // 到期时间 默认是当前的系统时间 + 90s
    long expiryTime = System.currentTimeMillis() + getLeaseRenewalOf(info);
    batchingDispatcher.process(
            // 创建了一个任务ID
            taskId("register", info),
            // 创建了一个实例复制任务
            new InstanceReplicationTask(targetHost, Action.Register, info, null, true) {
                public EurekaHttpResponse<Void> execute() {
                    return replicationClient.register(info);
                }
            },
        // 到期时间
            expiryTime
    );
}
复制代码

    我们看到这里用了一个 batchingDispatcher 批量调度器,这个调度器是在创建节点的时候尽心初始化的调度器

this.batchingDispatcher = TaskDispatchers.createBatchingTaskDispatcher(
            // 调度器名字
            batcherName,
            // 默认是 10000 个
            config.getMaxElementsInPeerReplicationPool(),
            // 批量的大小
            batchSize,
            // 最大实例同步个数默认20个
            config.getMaxThreadsForPeerReplication(),
            // 时间
            maxBatchingDelayMs,
            serverUnavailableSleepTimeMs,
            retrySleepTimeMs,
            // 任务执行器
            taskProcessor
);
复制代码

    这样一来就是通过这个批量调度器在一定的时间内也就是请求到来的时间+90s内进行同步请求注册就可以了,通过这个 replicationClient 进行注册请求,后续的逻辑就是走的上面的注册逻辑,唯一的区别就是同步的时候是放到了调度其中去执行的,不是直接去请求访问注册的

批量调度器的创建以及运行流程

// 任务调度器
public static <ID, T> TaskDispatcher<ID, T> createBatchingTaskDispatcher(String id,
                                                 int maxBufferSize,
                                                 int workloadSize,
                                                 int workerCount,
                                                 long maxBatchingDelay,
                                                 long congestionRetryDelayMs,
                                                 long networkFailureRetryMs,
                                                 TaskProcessor<T> taskProcessor) {
    // 任务接收执行器
    final AcceptorExecutor<ID, T> acceptorExecutor = new AcceptorExecutor<>(
            id, maxBufferSize, workloadSize, maxBatchingDelay, 
        congestionRetryDelayMs, networkFailureRetryMs
    );
    // 任务执行器
    final TaskExecutors<ID, T> taskExecutor = 
        TaskExecutors.batchExecutors(id, workerCount, taskProcessor, acceptorExecutor);
    return new TaskDispatcher<ID, T>() {
        @Override
        public void process(ID id, T task, long expiryTime) {
            acceptorExecutor.process(id, task, expiryTime);
        }

        @Override
        public void shutdown() {
            acceptorExecutor.shutdown();
            taskExecutor.shutdown();
        }
    };
}
复制代码

任务接收器创建

AcceptorExecutor(String id,
                 int maxBufferSize,
                 int maxBatchingSize,
                 long maxBatchingDelay,
                 long congestionRetryDelayMs,
                 long networkFailureRetryMs) {
    this.id = id;
    this.maxBufferSize = maxBufferSize;
    this.maxBatchingSize = maxBatchingSize;
    this.maxBatchingDelay = maxBatchingDelay;
    ThreadGroup threadGroup = new ThreadGroup("eurekaTaskExecutors");
    this.acceptorThread = new Thread(threadGroup, new AcceptorRunner(),
                                     "TaskAcceptor-" + id);
    this.acceptorThread.setDaemon(true);
    this.acceptorThread.start();
}
复制代码

    核心代码是 new AcceptorRunner() 这个代码,接收运行任务代码,进行任务队列处理,就是接收队列进入处理队列的逻辑,之后通过定义的任务执行器进行执行

任务执行器创建

    上面将要执行的请求任务进行了处理操作得到了一些需要处理的批量任务

static class BatchWorkerRunnable<ID, T> extends WorkerRunnable<ID, T> {
// 线程运行的主要逻辑代码是这块了
@Override
public void run() {
    while (!isShutdown.get()) {
        List<TaskHolder<ID, T>> holders = getWork();
        metrics.registerExpiryTimes(holders);

        List<T> tasks = getTasksOf(holders);
        // 任务执行器真正处理代码的地方
        ProcessingResult result = processor.process(tasks);
    }
}
复制代码

任务执行器执行任务

    Die Methode „processor.process(tasks)“ ruft die Methode „process“ im „ReplicationTaskProcessor“ auf, um die Anforderung zu verarbeiten, übermittelt Tasks für Stapelaktualisierungsvorgänge über den „replicationClient“ und fordert außerdem die Methode „submitBatchUpdates“ über das Jersey-Framework an, um die Anforderung des Pfads „peerreplication/batch/“ zu finden

public ProcessingResult process(List<ReplicationTask> tasks) {
  // 进行任务的细节处理
    ReplicationList list = createReplicationListOf(tasks);
    EurekaHttpResponse<ReplicationListResponse> response = 
        replicationClient.submitBatchUpdates(list);
}
复制代码

Batch-Task-Kernverarbeitungsanforderungsklasse

    Es gibt eine PeerReplicationResource im Projekt von eureka-core.Der Anforderungspfad in dieser Klasse hat Peerreplication, und es gibt eine Methode mit einer Batch-ID auf dem Pfad, also hier.

@Path("/{version}/peerreplication")
@Produces({"application/xml", "application/json"})
public class PeerReplicationResource {
@Path("batch")
@POST
public Response batchReplication(ReplicationList replicationList) {
    ReplicationListResponse batchResponse = new ReplicationListResponse();
    for (ReplicationInstance instanceInfo : replicationList.getReplicationList()) {
            batchResponse.addResponse(dispatch(instanceInfo));
    }
    return Response.ok(batchResponse).build();
}
// 真正的处理方法
private ReplicationInstanceResponse dispatch(ReplicationInstance instanceInfo) {
    ApplicationResource applicationResource = createApplicationResource(instanceInfo);
    InstanceResource resource = createInstanceResource(instanceInfo, 
                                                       applicationResource);
    String lastDirtyTimestamp = toString(instanceInfo.getLastDirtyTimestamp());
    String overriddenStatus = toString(instanceInfo.getOverriddenStatus());
    String instanceStatus = toString(instanceInfo.getStatus());
    
    Builder singleResponseBuilder = new Builder();
    // 根据动作信息进行处理不同的动作
    switch (instanceInfo.getAction()) {
        case Register: // 注册
            singleResponseBuilder = handleRegister(instanceInfo, applicationResource);
            break;
        case Heartbeat:// 心跳
            singleResponseBuilder = handleHeartbeat(serverConfig, resource,
                      lastDirtyTimestamp, overriddenStatus, instanceStatus);
            break;
        case Cancel: // 下线
            singleResponseBuilder = handleCancel(resource);
            break;
        case StatusUpdate: // 状态更新
            singleResponseBuilder = handleStatusUpdate(instanceInfo, resource);
            break;
        case DeleteStatusOverride:
            singleResponseBuilder = handleDeleteStatusOverride(instanceInfo, resource);
            break;
    }
    return singleResponseBuilder.build();
}
复制代码

Verarbeitung von Heartbeat-Anfragen

    handleRegister (instanceInfo, applicationResource), diese Methode ist die Methode zur Behandlung von Registrierungsanfragen

private static Builder handleRegister(ReplicationInstance instanceInfo,
                                      ApplicationResource applicationResource) {
    applicationResource.addInstance(instanceInfo.getInstanceInfo(), REPLICATION);
    return new Builder().setStatusCode(Status.OK.getStatusCode());
}
复制代码

    Unten sehen Sie den Code der Instanzregistrierungsanforderung, die wir zuvor gesehen haben, aber zu diesem Zeitpunkt ist der Wert von REPLICATION wahr, sodass der aktuelle Dienstknoten nur eine Registrierungsanforderung ist und nicht bei anderen Knoten registriert wird.

public Response addInstance(InstanceInfo info, 
           @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
    /**
     *  "true".equals(isReplication) 注意这个参数
     */
    registry.register(info, "true".equals(isReplication));
    return Response.status(204).build();  // 204 to be backwards compatible
}
复制代码

Zusammenfassung

  1. Der Client initiiert die Registrierung beim Server
  2. Ob synchronisiert werden soll, je nachdem, ob es sich um eine von ihm selbst initiierte Registrierung handelt
  3. Erstellen Sie einen Aufgabenplaner für den Stapelempfang und die Planung von Aufgaben
  4. Task-Core-Anforderungen für Batch-Verarbeitung
  5. Verarbeitung von Heartbeat-Anfragen

Höhepunkte

    Die dreistufige Warteschlange führt Aufgabenverarbeitungsoperationen, Empfangswarteschlangen, Verarbeitungswarteschlangen, Stapelausführungs-Aufgabenwarteschlangen und die Ausführung synchroner Operationen durch

Ich denke du magst

Origin juejin.im/post/7084364130508865572
Empfohlen
Rangfolge