Package com.alibaba.otter.shared.arbitrate.impl.setl.rpc

Source Code of com.alibaba.otter.shared.arbitrate.impl.setl.rpc.RpcStageController

/*
* Copyright (C) 2010-2101 Alibaba Group Holding Limited.
*
* 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
*
*     http://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 com.alibaba.otter.shared.arbitrate.impl.setl.rpc;

import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;
import com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;
import com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;
import com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;
import com.alibaba.otter.shared.arbitrate.impl.setl.helper.ReplyProcessQueue;
import com.alibaba.otter.shared.arbitrate.impl.setl.helper.StageProgress;
import com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor.ProcessListener;
import com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor.ProcessMonitor;
import com.alibaba.otter.shared.arbitrate.model.EtlEventData;
import com.alibaba.otter.shared.common.model.config.enums.StageType;
import com.google.common.base.Function;
import com.google.common.collect.MapMaker;

/**
* 基于rpc的stage调度控制器,排除select调度,主要控制e/t/l的调度控制
*
* @author jianghang 2012-9-28 下午10:05:26
* @version 4.1.0
*/
public class RpcStageController extends ArbitrateLifeCycle implements ProcessListener {

    private static final Logger               logger                 = LoggerFactory.getLogger(RpcStageController.class);
    private Map<StageType, ReplyProcessQueue> replys;
    private Map<Long, StageProgress>          progress;
    private ProcessMonitor                    processMonitor;
    private volatile Long                     lastestLoadedProcessId = -1L;                                              // 最近一次同步成功的processId

    public RpcStageController(Long pipelineId){
        super(pipelineId);

        replys = new MapMaker().makeComputingMap(new Function<StageType, ReplyProcessQueue>() {

            public ReplyProcessQueue apply(StageType input) {
                int size = ArbitrateConfigUtils.getParallelism(getPipelineId()) * 10;
                if (size < 100) {
                    size = 100;
                }
                return new ReplyProcessQueue(size);
            }
        });

        progress = new MapMaker().makeMap();
        // 注册一下监听事件变化
        processMonitor = ArbitrateFactory.getInstance(pipelineId, ProcessMonitor.class);
        processMonitor.addListener(this);
        processMonitor.reload();
    }

    public Long waitForProcess(StageType stage) throws InterruptedException {
        if (stage.isSelect()) {
            throw new ArbitrateException("not support");
        }

        return replys.get(stage).take();
    }

    /**
     * 获取上一个stage传递的数据信息
     */
    public EtlEventData getLastData(Long processId) {
        return progress.get(processId).getData();
    }

    public void destory() {
        processMonitor.removeListener(this);
        replys.clear();
        progress.clear();
    }

    public synchronized boolean single(StageType stage, EtlEventData etlEventData) {
        boolean result = true;
        switch (stage) {
            case SELECT:
                progress.put(etlEventData.getProcessId(), new StageProgress(StageType.SELECT, etlEventData));
                replys.get(StageType.EXTRACT).offer(etlEventData.getProcessId());
                break;
            case EXTRACT:
                progress.put(etlEventData.getProcessId(), new StageProgress(StageType.EXTRACT, etlEventData));
                replys.get(StageType.TRANSFORM).offer(etlEventData.getProcessId());
                break;
            case TRANSFORM:
                progress.put(etlEventData.getProcessId(), new StageProgress(StageType.TRANSFORM, etlEventData));
                // 并不是立即触发,通知最小的一个process启动
                computeNextLoad();
                break;
            case LOAD:
                Object removed = progress.remove(etlEventData.getProcessId());
                // 并不是立即触发,通知下一个最小的一个process启动
                if (removed == null) {
                    result = false;
                } else {
                    // 如果2个process 1和2, 1先执行完了load,此时2还不符合条件,等2到了Transform时,还需要依赖zookeeper的process列表变化进行判断
                    // 记录一下上一次同步成功的processId,提升load响应速度,方便在内存中直接判断
                    lastestLoadedProcessId = etlEventData.getProcessId();
                    computeNextLoad(); // 通知load processId,触发下一个
                }
                break;
            default:
                break;
        }

        return result;
    }

    /**
     * 计算下一个load的processId
     */
    private void computeNextLoad() {
        // 针对上一个id为本地load成功的,直接忽略,触发下一个id
        Long processId = getMinTransformedProcessId(lastestLoadedProcessId);
        if (processId != null) {
            replys.get(StageType.LOAD).offer(processId);
        }
    }

    /**
     * 获取最小一个符合条件的processId,排除loadedProcessId
     */
    private Long getMinTransformedProcessId(Long loadedProcessId) {
        ProcessMonitor processMonitor = ArbitrateFactory.getInstance(getPipelineId(), ProcessMonitor.class);
        List<Long> processIds = processMonitor.getCurrentProcessIds();
        // 如果需要当前node处理当前process的load时,rpc请求一定会将对应的stage状态发到这机器上,并保存到progress中
        if (!CollectionUtils.isEmpty(processIds) && !CollectionUtils.isEmpty(progress)) {
            // 上一次load成功的在当前的processId中不存在,可能有两种情况:
            // 1. zk还未将数据通知过来,当前current processIds还是为老版本的值
            // 2. processId已经被删除,比如好久没有数据同步了,定时触发时发觉列表一直为空
            // if (loadedProcessId != null && !processIds.contains(loadedProcessId)) {
            // // 强制刷新一次,不过也可能是刷到老版本的值,杭州leader还没同步到美国
            // processIds = processMonitor.getCurrentProcessIds(true);
            // }

            Long result = null;
            // 做的一个优化,如果上一个processId load成功是在本机,直接忽略
            // 因为存在一个问题:比如两个process,都先完成了T模块,然后逐个触发L模块,此时第二个process需要等zookeeper回调watcher时才会被触发
            for (Long processId : processIds) {
                if (loadedProcessId == null || processId > loadedProcessId) {
                    result = processId;
                    break;
                }
            }

            // 如果不存在符合>loadedProcessId的记录,直接假设下一个processId就是上一个id+1
            // 因为processId目前的机制永远只会递增
            if (result == null) {
                result = loadedProcessId + 1;
            }

            if (result != null) {
                StageProgress stage = progress.get(result);
                if (stage != null && stage.getStage().isTransform()) {
                    return result;
                } else {
                    logger.info("rpc compute [{}] but stage [{}]", result, stage == null ? null : stage.getStage());
                    return null;
                }
            }
        }

        return null;
    }

    public void processChanged(List<Long> processIds) {
        compareProgress(processIds);

        for (ReplyProcessQueue replyProcessIds : replys.values()) {
            compareReply(processIds, replyProcessIds);
        }

        // process发生变化,可能是process load完成,需要触发下一个process进行load
        computeNextLoad();
    }

    /**
     * 删除已经被废弃的processId
     */
    private synchronized void compareProgress(List<Long> processIds) {
        if (CollectionUtils.isEmpty(processIds) == false) {
            Long minProcessId = processIds.get(0);
            // 对比一下progress中的记录,如果小于当前最小的processId,直接删除内存中的记录
            // 因为发生跨机器调用或者出现restart指令,对应的process记录不会被删除
            for (Long processId : progress.keySet()) {
                if (processId < minProcessId) {
                    progress.remove(processId);
                }
            }
        }
    }

    /**
     * 将当前的符合条件的processIds和当前的reply queue进行校对,剔除不在processIds里的内容
     */
    private synchronized void compareReply(List<Long> processIds, ReplyProcessQueue replyProcessIds) {
        Object[] replyIds = replyProcessIds.toArray();
        for (Object replyId : replyIds) {
            if (processIds.contains((Long) replyId) == false) { // 判断reply id是否在当前processId列表中
                // 因为存在并发问题,如在执行Listener事件的同时,可能触发了process的创建,这时新建的processId会进入到reply队列中
                // 此时接受到的processIds变量为上一个版本的内容,所以会删除新建的process,导致整个通道被挂住
                if (CollectionUtils.isEmpty(processIds) == false) {
                    Long processId = processIds.get(0);
                    if (processId > (Long) replyId) { // 如果当前最小的processId都大于replyId, processId都是递增创建的
                        logger.info("## {} remove reply id [{}]", ClassUtils.getShortClassName(this.getClass()),
                                    (Long) replyId);
                        replyProcessIds.remove((Long) replyId);
                        progress.remove((Long) replyId);
                    }
                }
            }
        }
    }

}
TOP

Related Classes of com.alibaba.otter.shared.arbitrate.impl.setl.rpc.RpcStageController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.