Package com.alibaba.otter.node.etl.select.selector.canal

Source Code of com.alibaba.otter.node.etl.select.selector.canal.OtterDownStreamHandler

/*
* 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.node.etl.select.selector.canal;

import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import com.alibaba.otter.canal.common.AbstractCanalLifeCycle;
import com.alibaba.otter.canal.common.CanalLifeCycle;
import com.alibaba.otter.canal.sink.CanalEventDownStreamHandler;
import com.alibaba.otter.canal.sink.CanalEventSink;
import com.alibaba.otter.canal.store.model.Event;
import com.alibaba.otter.node.etl.OtterConstants;
import com.alibaba.otter.shared.arbitrate.ArbitrateEventService;
import com.alibaba.otter.shared.arbitrate.model.MainStemEventData;
import com.alibaba.otter.shared.arbitrate.model.TerminEventData;
import com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;
import com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;

/**
* 在{@linkplain CanalEventSink}消费数据之前,更新到对应的store中
*
* @author jianghang 2012-7-31 下午03:27:18
* @version 4.1.0
*/
public class OtterDownStreamHandler extends AbstractCanalLifeCycle implements CanalEventDownStreamHandler<Event>, CanalLifeCycle {

    private static final Logger      logger                   = LoggerFactory.getLogger(OtterDownStreamHandler.class);
    private static final String      DETECTING_FAILED_MESSAGE = "pid:%s canal elapsed %s seconds no data";
    private Long                     pipelineId;
    private ArbitrateEventService    arbitrateEventService;
    // 心跳检查控制mainstem信号
    private ScheduledExecutorService scheduler                = null;
    private ScheduledFuture          future                   = null;
    private AtomicBoolean            working                  = new AtomicBoolean(false);
    private Boolean                  detectingEnable;
    private Integer                  detectingIntervalInSeconds;                                                      // 心跳包发送时间
    private volatile Long            lastEventExecuteTime     = 0L;
    // detecting临时数据
    private int                      detectingThresoldCount   = 10;
    private int                      detectingExpCount        = 1;                                                    // 增常趋势
    private AtomicLong               detectingFailedCount     = new AtomicLong(0);                                    // 检测失败的次数
    private AtomicLong               detectingSuccessedCount  = new AtomicLong(0);                                    // 检测成功的次数

    public void stop() {
        super.stop();

        if (working.compareAndSet(true, false)) {
            stopDetecting();
        }

    }

    public void before(List<Event> events) {
        lastEventExecuteTime = System.currentTimeMillis();// 记录最后一条数据时间

        if (working.compareAndSet(false, true)) {// 第一次有数据时
            startDetecting();
        }

    }

    public void retry(List<Event> events) {
        lastEventExecuteTime = System.currentTimeMillis();// 记录最后一条数据时间
    }

    public void after(List<Event> events) {
        // do nothing
    }

    private void startDetecting() {
        // 直接发送已追上的状态,保持和eromanga兼容处理
        MainStemEventData mainStemData = new MainStemEventData();
        mainStemData.setPipelineId(pipelineId);
        mainStemData.setStatus(MainStemEventData.Status.OVERTAKE);
        arbitrateEventService.mainStemEvent().single(mainStemData);

        // 启动异步线程定时监控,一定会有数据过来
        String schedulerName = String.format("pipelineId = %s , CanalDetecting", String.valueOf(pipelineId));
        scheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory(schedulerName));
        future = scheduler.scheduleAtFixedRate(new Runnable() {

            public void run() {
                try {
                    MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));
                    // 检查下和最后收到的数据的时间戳,如果超过一定时间没收到,说明canal解析存在问题
                    // (因为会有心跳包数据,理论上时间间隔会小于一定值)
                    if (isDelayed(System.currentTimeMillis(), lastEventExecuteTime)) {
                        notifyFailed();
                    } else {
                        notifySuccessed();
                    }
                } catch (Exception e) {
                    logger.error("heartbeat check failed!", e);
                } finally {
                    MDC.remove(OtterConstants.splitPipelineLogFileKey);
                }

            }
        }, detectingIntervalInSeconds, detectingIntervalInSeconds, TimeUnit.SECONDS);
    }

    private void stopDetecting() {
        ((ScheduledThreadPoolExecutor) scheduler).remove((Runnable) future);
        scheduler.shutdownNow();
    }

    private void notifyFailed() {
        detectingSuccessedCount.set(0);
        long failedCount = detectingFailedCount.incrementAndGet();
        if (detectingEnable) { // 如果存在心跳包,需要处理notifyFailed情况,发送Taking
            if (failedCount == 1) {
                detectingExpCount = 1;// 系数重置

                notifyMainstemStatus(MainStemEventData.Status.TAKEING);
            }

            if (failedCount >= detectingThresoldCount * detectingExpCount * detectingExpCount) {
                notifyMainstemStatus(MainStemEventData.Status.TAKEING);
                detectingExpCount++; // 系数增大一次

                // 并且发送一次报警信息,系统不太正常了,超过一定时间一次都没有拿到对应的数据
                // 可能出现的情况:
                // 1. 主备发生切换,定位position花费了过久的时间
                // 2. MysqlEventParser工作不正常,一直拿不到数据,比如数据库挂了,但是又没通知其进行主备切换
                TerminEventData errorEventData = new TerminEventData();
                errorEventData.setPipelineId(pipelineId);
                errorEventData.setType(TerminType.WARNING);
                errorEventData.setCode("mainstem");
                errorEventData.setDesc(String.format(DETECTING_FAILED_MESSAGE, pipelineId,
                                                     String.valueOf(detectingIntervalInSeconds * failedCount)));
                arbitrateEventService.terminEvent().single(errorEventData);
            }
        }
    }

    private void notifySuccessed() {
        detectingFailedCount.set(0);
        long successedCount = detectingSuccessedCount.incrementAndGet();
        if (successedCount == 1) {
            detectingExpCount = 1;// 系数重置
            notifyMainstemStatus(MainStemEventData.Status.OVERTAKE);
        }

        if (successedCount >= detectingThresoldCount * detectingExpCount * detectingExpCount) {
            detectingExpCount++; // 系数增大一次
            notifyMainstemStatus(MainStemEventData.Status.OVERTAKE);
        }

    }

    private void notifyMainstemStatus(MainStemEventData.Status status) {
        MainStemEventData mainStemData = new MainStemEventData();
        mainStemData.setPipelineId(pipelineId);
        mainStemData.setStatus(status);
        arbitrateEventService.mainStemEvent().single(mainStemData);
    }

    private boolean isDelayed(Long detectingExecuteTime, Long lastExecuteTime) {
        long delayTime = detectingExecuteTime - lastExecuteTime;
        return delayTime > detectingIntervalInSeconds * 2 * 1000;
    }

    public void setPipelineId(Long pipelineId) {
        this.pipelineId = pipelineId;
    }

    public void setDetectingEnable(Boolean detectingEnable) {
        this.detectingEnable = detectingEnable;
    }

    public void setDetectingIntervalInSeconds(Integer detectingIntervalInSeconds) {
        this.detectingIntervalInSeconds = detectingIntervalInSeconds;
    }

    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {
        this.arbitrateEventService = arbitrateEventService;
    }

    public void setDetectingThresoldCount(int detectingThresoldCount) {
        this.detectingThresoldCount = detectingThresoldCount;
    }

}
TOP

Related Classes of com.alibaba.otter.node.etl.select.selector.canal.OtterDownStreamHandler

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.