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

com.alibaba.schedulerx.worker.discovery.DefaultServerDiscovery Maven / Gradle / Ivy

There is a newer version: 1.12.2
Show newest version
package com.alibaba.schedulerx.worker.discovery;

import java.net.URLEncoder;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;

import com.alibaba.schedulerx.common.constants.CommonConstants;
import com.alibaba.schedulerx.common.domain.Constants;
import com.alibaba.schedulerx.common.domain.JSONResult;
import com.alibaba.schedulerx.common.domain.ResponseCode;
import com.alibaba.schedulerx.common.domain.ScaleGroupResult;
import com.alibaba.schedulerx.common.util.ConfigUtil;
import com.alibaba.schedulerx.common.util.JsonUtil;
import com.alibaba.schedulerx.worker.SchedulerxWorker;
import com.alibaba.schedulerx.worker.domain.WorkerConstants;
import com.alibaba.schedulerx.worker.log.LogFactory;
import com.alibaba.schedulerx.worker.log.Logger;
import com.alibaba.schedulerx.worker.util.ConsoleUtil;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;

import akka.actor.ActorSelection;
import akka.actor.ActorSystem;

/**
 * heart beat get active leader of specific group through console.
 *
 * @author @yanxun
 * @date 2018/5/22
 */
public class DefaultServerDiscovery implements ServerDiscovery {
    public DefaultServerDiscovery() {}

    private static final Logger LOGGER = LogFactory.getLogger(DefaultServerDiscovery.class);
    private static final String ACTIVE_SERVER_QUERY_PATH = "/worker/v1/appgroup/getLeaderAddr";
    private static final String SERVER_DISCOVERY_THREAD_NAME = "activeServerDiscoveryThread-";
    private ScheduledExecutorService scheduledExecutorService;
    private volatile String activeServerAddr;
    private volatile ActorSelection instanceStatusRouter;
    private volatile ActorSelection mapMasterRouter;
    private volatile ActorSelection taskStatusRouter;
    private volatile ActorSelection heartbeatActor;
    private volatile ActorSelection logActor;
    private volatile ActorSystem actorSystem = SchedulerxWorker.actorSystem;
    private GroupManager groupManager = GroupManager.INSTANCE;
    private Configuration conf = ConfigUtil.getWorkerConfig();

    private long exceptionCount = 0;


    @Override
    public void start(final String namespace, final String namespaceSource, final String groupId, final String appKey) throws Exception {
        final String consoleDomain = conf.getString(WorkerConstants.WORKER_DOMAIN_NAME);
        scheduledExecutorService = new ScheduledThreadPoolExecutor(1,
            new ThreadFactoryBuilder().setNameFormat(SERVER_DISCOVERY_THREAD_NAME + groupId).build(),
            new ThreadPoolExecutor.DiscardPolicy());

        //如果开启了consolelist服务发现,通过console的ip轮询
        final boolean enableConsoleList = ConfigUtil.getWorkerConfig().getBoolean(CommonConstants.SCHEDULERX_CONSOLELIST_ENABLE, false);
        if (enableConsoleList) {
            String consoleIp = ConsoleUtil.getRandomConsoleIp(consoleDomain);
            if (consoleIp != null) {
                conf.setProperty(WorkerConstants.WORKER_DOMAIN_NAME, consoleIp); 
                LOGGER.info("enable consoleList domain={}", consoleIp);
            }
        }
        
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    String domain = ConfigUtil.getWorkerConfig().getString(WorkerConstants.WORKER_DOMAIN_NAME);
                    String activeServer = queryActiveServer(domain, groupId, appKey, namespace, namespaceSource);
                    if (!StringUtils.isEmpty(activeServer) && !activeServer.equalsIgnoreCase(activeServerAddr)) {
                        LOGGER.info("activeServerAddr={} change to {}, actorSystem={}", activeServerAddr, activeServer,
                                actorSystem.provider().getDefaultAddress());
                        activeServerAddr = activeServer;
                        heartbeatActor = actorSystem.actorSelection(getServerHeartbeatAkkaPath(activeServerAddr));
                        instanceStatusRouter = actorSystem.actorSelection(getServerInstanceStatusRouterAkkaPath(activeServerAddr));
                        mapMasterRouter = actorSystem.actorSelection(getServerMapMasterRouterAkkaPath(activeServerAddr));
                        taskStatusRouter = actorSystem.actorSelection(getServerTaskStatusRouterAkkaPath(activeServerAddr));
                        logActor = actorSystem.actorSelection(getServerLogAkkaPath(activeServerAddr));
                    } else if (StringUtils.isEmpty(activeServer)) {
                        //console的ip可能会变,如果当前domain接口获取失败,重新获取新的consolelist
                        if (enableConsoleList) {
                            String consoleIp = ConsoleUtil.getRandomConsoleIp(consoleDomain);
                            if (consoleIp != null) {
                                conf.setProperty(WorkerConstants.WORKER_DOMAIN_NAME, consoleIp); 
                                LOGGER.info("domain change from {} to {}", domain, consoleIp);
                            }
                        }
                    }
                } catch (Throwable t) {
                    //console的ip可能会变,果然当前domain接口获取失败,重新获取新的consolelist
                    if (enableConsoleList) {
                        LOGGER.warn("scheduled query active server lost, you can ignore this message if not often.");
                        String consoleIp = ConsoleUtil.getRandomConsoleIp(consoleDomain);
                        if (consoleIp != null) {
                            conf.setProperty(WorkerConstants.WORKER_DOMAIN_NAME, consoleIp); 
                            LOGGER.info("domain change to {}", consoleIp);
                        }
                    } else {
                        LOGGER.warn("scheduled query active server lost, you can ignore this message if not often.");
                    }
                }
            }
        }, 0L, 10L, TimeUnit.SECONDS);
    }

    @Override
    public String getActiveServerAddr() {
        return activeServerAddr;
    }

    private String getServerInstanceStatusRouterAkkaPath(String serverAddr) {
        return Constants.SERVER_AKKA_PATH_PREFIX + serverAddr + Constants.SERVER_AKKA_INSTANCE_STATUS_ROUTER_PATH;
    }

    private String getServerMapMasterRouterAkkaPath(String serverAddr) {
        return Constants.SERVER_AKKA_PATH_PREFIX + serverAddr + Constants.SERVER_AKKA_MAP_MASTER_ROUTER_PATH;
    }

    private String getServerTaskStatusRouterAkkaPath(String serverAddr) {
        return Constants.SERVER_AKKA_PATH_PREFIX + serverAddr + Constants.SERVER_AKKA_TASK_STATUS_ROUTER_PATH;
    }

    private String getServerHeartbeatAkkaPath(String serverAddr) {
        return Constants.SERVER_AKKA_PATH_PREFIX + serverAddr + Constants.SERVER_AKKA_HEARTBEAT_PATH;
    }
    
    private String getServerLogAkkaPath(String serverAddr) {
        return Constants.SERVER_AKKA_PATH_PREFIX + serverAddr + "/user/log";
    }

    @Override
    public ActorSelection getActiveHeartBeatActor() {
        return heartbeatActor;
    }

    @Override
    public void stop() throws Exception {
        scheduledExecutorService.shutdown();
    }

    private String queryActiveServer(String domain, String groupId, String appKey, String namespace, String namespaceSource) {
        String activeServer = null;
        String activeServerQueryUrl;
        if (namespace != null) {
            activeServerQueryUrl = "http://" + domain + ACTIVE_SERVER_QUERY_PATH
                    + "?groupId=" + groupId + "&namespace=" + namespace;
            if (StringUtils.isNotBlank(namespaceSource)) {
                activeServerQueryUrl += "&namespaceSource="+namespaceSource;
            }
        } else {
            activeServerQueryUrl = "http://" + domain + ACTIVE_SERVER_QUERY_PATH + "?groupId=" + groupId;
        }
        activeServerQueryUrl += "&enableScale=true";
        try {
            activeServerQueryUrl += "&appKey=" + URLEncoder.encode((appKey == null) ? "" : appKey, "UTF-8");
            HttpResponse jsonResponse = Unirest.get(activeServerQueryUrl).asJson();
            JSONResult jsonResult = JsonUtil.fromJson(jsonResponse.getBody().toString(), JSONResult.class);
            LOGGER.debug("queryActiveServer url={}, response={}", activeServerQueryUrl, jsonResponse.getBody().toString());
            if (jsonResult != null && jsonResult.isSuccess()) {
                if (jsonResult.getCode() == ResponseCode.GROUP_HAS_CHILD) {
                    //该应用分组开启了自动扩容并且分裂过孩子,需要解析出所有的groupIds,并注册serverDiscovery
                    ScaleGroupResult groupResult = JsonUtil.fromJson(jsonResult.getData().toString(), ScaleGroupResult.class);
                    activeServer = groupResult.getCurrentLeaderAddr();
                    //返回的分组数大于缓存,说明有新的分裂,需要新注册
                    if (CollectionUtils.isNotEmpty(groupResult.getGroupIds())) {
                        for (String childGroup : groupResult.getGroupIds()) {
                            if (!groupManager.contains(childGroup)) {
                                if (groupResult.getGroupIdMap() != null) {
                                    groupManager.putGroupId2AppKeyMap(childGroup, groupResult.getGroupIdMap().get(childGroup));
                                }
                                groupManager.startServerDiscovery(childGroup);
                                groupManager.appendGroupId(childGroup, groupId);
                            }
                        }
                    }
                } else {
                    activeServer = (String)jsonResult.getData();
                }
            }
            exceptionCount = 0;
        } catch (Throwable e) {
            if (exceptionCount++ < 10) {
                LOGGER.warn("Query active server lost, you can ignore this message if not often, url=" + activeServerQueryUrl);
            } else {
                LOGGER.error("Query active server lost, please check you config, message:{}, url={}", e.getMessage(), activeServerQueryUrl, e);
            }
        }
        return activeServer;
    }


    @Override
    public ActorSelection getInstanceStatusRouter() {
        return instanceStatusRouter;
    }

    @Override
    public ActorSelection getMapMasterRouter() {
        return mapMasterRouter;
    }

    @Override
    public ActorSelection getTaskStatusRouter() {
        return taskStatusRouter;
    }
    
    @Override
    public ActorSelection getLogRouter() {
        return logActor;
    }

    @Override
    public void reset(ActorSystem actorSystem) {
        activeServerAddr = null;
        heartbeatActor = null;
        instanceStatusRouter = null;
        mapMasterRouter = null;
        taskStatusRouter = null;
        logActor = null;
        this.actorSystem = actorSystem;
        
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy