All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.ponfee.disjob.supervisor.application.ServerInvokeService Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2022-2024 Ponfee (http://www.ponfee.cn/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.ponfee.disjob.supervisor.application;

import cn.ponfee.disjob.common.base.RetryTemplate;
import cn.ponfee.disjob.common.base.SingletonClassConstraint;
import cn.ponfee.disjob.common.base.TextTokenizer;
import cn.ponfee.disjob.common.collect.Collects;
import cn.ponfee.disjob.common.concurrent.MultithreadExecutors;
import cn.ponfee.disjob.common.concurrent.ThreadPoolExecutors;
import cn.ponfee.disjob.common.util.Numbers;
import cn.ponfee.disjob.common.util.Strings;
import cn.ponfee.disjob.core.base.*;
import cn.ponfee.disjob.core.dto.worker.ConfigureWorkerParam;
import cn.ponfee.disjob.core.dto.worker.ConfigureWorkerParam.Action;
import cn.ponfee.disjob.core.dto.worker.GetMetricsParam;
import cn.ponfee.disjob.core.exception.AuthenticationException;
import cn.ponfee.disjob.registry.Registry;
import cn.ponfee.disjob.registry.rpc.DestinationServerRestProxy;
import cn.ponfee.disjob.registry.rpc.DestinationServerRestProxy.DestinationServerClient;
import cn.ponfee.disjob.supervisor.application.converter.ServerMetricsConverter;
import cn.ponfee.disjob.supervisor.application.request.ConfigureAllWorkerRequest;
import cn.ponfee.disjob.supervisor.application.request.ConfigureOneWorkerRequest;
import cn.ponfee.disjob.supervisor.application.response.SupervisorMetricsResponse;
import cn.ponfee.disjob.supervisor.application.response.WorkerMetricsResponse;
import cn.ponfee.disjob.supervisor.base.ExtendedSupervisorRpcService;
import cn.ponfee.disjob.supervisor.base.SupervisorEvent;
import cn.ponfee.disjob.supervisor.base.SupervisorMetrics;
import cn.ponfee.disjob.supervisor.component.WorkerClient;
import cn.ponfee.disjob.supervisor.exception.KeyExistsException;
import cn.ponfee.disjob.supervisor.exception.KeyNotExistsException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static cn.ponfee.disjob.common.base.Symbol.Str.COLON;

/**
 * Invoke remote server service
 *
 * @author Ponfee
 */
@Service
public class ServerInvokeService extends SingletonClassConstraint {
    private static final Logger LOG = LoggerFactory.getLogger(ServerInvokeService.class);

    private final WorkerClient workerClient;
    private final Registry supervisorRegistry;
    private final Supervisor.Local localSupervisor;
    private final DestinationServerClient supervisorRpcClient;

    public ServerInvokeService(WorkerClient workerClient,
                               Registry supervisorRegistry,
                               Supervisor.Local localSupervisor,
                               ExtendedSupervisorRpcService localSupervisorRpcProvider,
                               ServerProperties serverProperties,
                               @Qualifier(JobConstants.SPRING_BEAN_NAME_REST_TEMPLATE) RestTemplate restTemplate) {
        String supervisorContextPath = Strings.trimPath(serverProperties.getServlet().getContextPath());
        this.workerClient = workerClient;
        this.supervisorRegistry = supervisorRegistry;
        this.localSupervisor = localSupervisor;
        this.supervisorRpcClient = DestinationServerRestProxy.create(
            ExtendedSupervisorRpcService.class, localSupervisorRpcProvider, localSupervisor,
            supervisor -> supervisorContextPath, restTemplate, RetryProperties.none()
        );
    }

    // ------------------------------------------------------------public methods

    public List supervisors() {
        List list = supervisorRegistry.getRegisteredServers();
        list = Collects.sorted(list, Comparator.comparing(e -> localSupervisor.equals(e) ? 0 : 1));
        return MultithreadExecutors.call(list, this::getSupervisorMetrics, ThreadPoolExecutors.commonThreadPool());
    }

    public List workers(String group) {
        List list = workerClient.getDiscoveredWorkers(group);
        // 当前Supervisor同时也是Worker时,此Worker排到最前面
        list = Collects.sorted(list, Comparator.comparing(e -> e.equals(Worker.local()) ? 0 : 1));
        return MultithreadExecutors.call(list, this::getWorkerMetrics, ThreadPoolExecutors.commonThreadPool());
    }

    public WorkerMetricsResponse worker(String group, String worker) {
        if (StringUtils.isBlank(worker) || !worker.contains(COLON)) {
            return null;
        }
        TextTokenizer tokenizer = new TextTokenizer(worker, COLON);
        String host = tokenizer.next();
        Integer port = Numbers.toWrapInt(tokenizer.next());
        if (StringUtils.isBlank(host) || port == null) {
            return null;
        }
        WorkerMetricsResponse metrics = getWorkerMetrics(new Worker(group, "-", host, port));
        return StringUtils.isBlank(metrics.getWorkerId()) ? null : metrics;
    }

    public void configureOneWorker(ConfigureOneWorkerRequest req) {
        Worker worker = req.toWorker();
        if (req.getAction() == Action.ADD_WORKER) {
            List workers = workerClient.getDiscoveredWorkers(req.getGroup());
            if (workers != null && workers.stream().anyMatch(worker::matches)) {
                throw new KeyExistsException("Worker already registered: " + worker);
            }
        } else {
            List workers = getRequiredDiscoveredWorkers(req.getGroup());
            if (!workers.contains(worker)) {
                throw new KeyNotExistsException("Not found worker: " + worker);
            }
        }

        configureWorker(worker, req.getAction(), req.getData());
    }

    public void configureAllWorker(ConfigureAllWorkerRequest req) {
        List workers = getRequiredDiscoveredWorkers(req.getGroup());
        Consumer action = worker -> configureWorker(worker, req.getAction(), req.getData());
        MultithreadExecutors.run(workers, action, ThreadPoolExecutors.commonThreadPool());
    }

    public void publishOtherSupervisors(SupervisorEvent event) {
        try {
            List supervisors = supervisorRegistry.getRegisteredServers()
                .stream()
                .filter(e -> !localSupervisor.equals(e))
                .collect(Collectors.toList());
            Consumer action = supervisor -> publishSupervisor(supervisor, event);
            MultithreadExecutors.run(supervisors, action, ThreadPoolExecutors.commonThreadPool());
        } catch (Exception e) {
            LOG.error("Publish other supervisor error.", e);
        }
    }

    // ------------------------------------------------------------private methods

    private SupervisorMetricsResponse getSupervisorMetrics(Supervisor supervisor) {
        SupervisorMetrics metrics = null;
        Long pingTime = null;
        try {
            long start = System.currentTimeMillis();
            metrics = supervisorRpcClient.call(supervisor, ExtendedSupervisorRpcService::getMetrics);
            pingTime = System.currentTimeMillis() - start;
        } catch (Throwable e) {
            LOG.warn("Ping supervisor occur error: {} {}", supervisor, e.getMessage());
        }

        SupervisorMetricsResponse response;
        if (metrics == null) {
            response = new SupervisorMetricsResponse();
        } else {
            response = ServerMetricsConverter.INSTANCE.convert(metrics);
        }

        response.setHost(supervisor.getHost());
        response.setPort(supervisor.getPort());
        response.setPingTime(pingTime);
        return response;
    }

    private WorkerMetricsResponse getWorkerMetrics(Worker worker) {
        WorkerMetrics metrics = null;
        Long pingTime = null;
        GetMetricsParam param = GetMetricsParam.of(createSupervisorAuthenticationToken(worker));
        try {
            long start = System.currentTimeMillis();
            metrics = workerClient.call(worker, service -> service.getMetrics(param));
            pingTime = System.currentTimeMillis() - start;
        } catch (Throwable e) {
            LOG.warn("Ping worker occur error: {} {}", worker, e.getMessage());
        }

        WorkerMetricsResponse response;
        if (metrics == null || !Supervisor.local().verifyWorkerSignatureToken(worker.getGroup(), metrics.getSignature())) {
            response = new WorkerMetricsResponse();
        } else {
            response = ServerMetricsConverter.INSTANCE.convert(metrics);
        }
        response.setHost(worker.getHost());
        response.setPort(worker.getPort());
        response.setPingTime(pingTime);
        return response;
    }

    private List getRequiredDiscoveredWorkers(String group) {
        List list = workerClient.getDiscoveredWorkers(group);
        if (CollectionUtils.isEmpty(list)) {
            throw new KeyNotExistsException("Group '" + group + "' not exists workers.");
        }
        return list;
    }

    private void verifyWorkerSignature(Worker worker) {
        GetMetricsParam param = GetMetricsParam.of(createSupervisorAuthenticationToken(worker));
        WorkerMetrics metrics = workerClient.call(worker, service -> service.getMetrics(param));
        if (!Supervisor.local().verifyWorkerSignatureToken(worker.getGroup(), metrics.getSignature())) {
            throw new AuthenticationException("Worker authenticated failed: " + worker);
        }
    }

    private void configureWorker(Worker worker, Action action, String data) {
        if (action == Action.ADD_WORKER) {
            verifyWorkerSignature(worker);
        }
        ConfigureWorkerParam param = ConfigureWorkerParam.of(createSupervisorAuthenticationToken(worker), action, data);
        workerClient.invoke(worker, service -> service.configureWorker(param));
    }

    private void publishSupervisor(Supervisor supervisor, SupervisorEvent event) {
        RetryTemplate.executeQuietly(
            () -> supervisorRpcClient.invoke(supervisor, service -> service.publishEvent(event)),
            1,
            2000
        );
    }

    private String createSupervisorAuthenticationToken(Worker worker) {
        return Supervisor.local().createSupervisorAuthenticationToken(worker.getGroup());
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy