Package com.alibaba.otter.node.etl.load.loader.db.interceptor.operation

Source Code of com.alibaba.otter.node.etl.load.loader.db.interceptor.operation.AbstractOperationInterceptor

/*
* 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.load.loader.db.interceptor.operation;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import com.alibaba.otter.node.common.config.ConfigClientService;
import com.alibaba.otter.node.etl.common.db.dialect.DbDialect;
import com.alibaba.otter.node.etl.load.loader.db.context.DbLoadContext;
import com.alibaba.otter.node.etl.load.loader.interceptor.AbstractLoadInterceptor;
import com.alibaba.otter.shared.common.model.config.channel.Channel;
import com.alibaba.otter.shared.etl.model.EventData;
import com.alibaba.otter.shared.etl.model.Identity;

/**
* @author jianghang 2011-10-31 下午02:24:28
* @version 4.0.0
*/
public abstract class AbstractOperationInterceptor extends AbstractLoadInterceptor<DbLoadContext, EventData> {

    protected final Logger         logger              = LoggerFactory.getLogger(getClass());
    protected static final int     GLOBAL_THREAD_COUNT = 1000;
    protected static final int     INNER_THREAD_COUNT  = 300;
    protected static final String  checkDataSql        = "SELECT COUNT(*) FROM {0} WHERE id BETWEEN 0 AND {1}";
    protected static final String  deleteDataSql       = "DELETE FROM {0}";

    protected String               updateSql;
    protected String               updateInfoSql;
    protected String               clearSql            = "UPDATE {0} SET {1} = 0 WHERE id = ? and {1} = ?";
    protected String               clearInfoSql        = "UPDATE {0} SET {1} = 0 , {2} = null WHERE id = ? and {1} = ? and {2} = ?";
    protected int                  innerIdCount        = INNER_THREAD_COUNT;
    protected int                  globalIdCount       = GLOBAL_THREAD_COUNT;
    protected ConfigClientService  configClientService;
    protected Set<JdbcTemplate>    tableCheckStatus    = Collections.synchronizedSet(new HashSet<JdbcTemplate>());
    protected AtomicInteger        THREAD_COUNTER      = new AtomicInteger(0);
    protected ThreadLocal<Integer> threadLocal         = new ThreadLocal<Integer>();

    protected AbstractOperationInterceptor(String updateSql, String updateInfoSql){
        this.updateSql = updateSql;
        this.updateInfoSql = updateInfoSql;
    }

    private void init(final JdbcTemplate jdbcTemplate, final String markTableName, final String markTableColumn) {
        int count = jdbcTemplate.queryForInt(MessageFormat.format(checkDataSql, markTableName, GLOBAL_THREAD_COUNT - 1));
        if (count != GLOBAL_THREAD_COUNT) {
            if (logger.isInfoEnabled()) {
                logger.info("Interceptor: init " + markTableName + "'s data.");
            }
            TransactionTemplate transactionTemplate = new TransactionTemplate();
            transactionTemplate.setTransactionManager(new DataSourceTransactionManager(jdbcTemplate.getDataSource()));
            transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);// 注意这里强制使用非事务,保证多线程的可见性
            transactionTemplate.execute(new TransactionCallback() {

                public Object doInTransaction(TransactionStatus status) {
                    jdbcTemplate.execute(MessageFormat.format(deleteDataSql, markTableName));
                    String batchSql = MessageFormat.format(updateSql, new Object[] { markTableName, markTableColumn });
                    jdbcTemplate.batchUpdate(batchSql, new BatchPreparedStatementSetter() {

                        public void setValues(PreparedStatement ps, int idx) throws SQLException {
                            ps.setInt(1, idx);
                            ps.setInt(2, 0);
                            // ps.setNull(3, Types.VARCHAR);
                        }

                        public int getBatchSize() {
                            return GLOBAL_THREAD_COUNT;
                        }
                    });
                    return null;
                }
            });

            if (logger.isInfoEnabled()) {
                logger.info("Interceptor: Init EROSA Client Data: " + updateSql);
            }
        }

    }

    public void transactionBegin(DbLoadContext context, List<EventData> currentDatas, DbDialect dialect) {
        boolean needInfo = StringUtils.isNotEmpty(context.getPipeline().getParameters().getChannelInfo());
        if (context.getChannel().getPipelines().size() > 1 || needInfo) {// 如果是双向同步,需要记录clientId
            String hint = currentDatas.get(0).getHint();
            String sql = needInfo ? updateInfoSql : updateSql;
            threadLocal.remove();// 进入之前先清理
            int threadId = currentId();
            updateMark(context, dialect, threadId, sql, needInfo, hint);
            threadLocal.set(threadId);
        }
    }

    public void transactionEnd(DbLoadContext context, List<EventData> currentDatas, DbDialect dialect) {
        boolean needInfo = StringUtils.isNotEmpty(context.getPipeline().getParameters().getChannelInfo());
        if (context.getChannel().getPipelines().size() > 1 || needInfo) {// 如果是双向同步,需要记录clientId
            String hint = currentDatas.get(0).getHint();
            String sql = needInfo ? clearInfoSql : clearSql;
            Integer threadId = threadLocal.get();
            updateMark(context, dialect, threadId, sql, needInfo, hint);
            threadLocal.remove();
        }
    }

    /**
     * 更新一下事务标记
     */
    private void updateMark(DbLoadContext context, DbDialect dialect, int threadId, String sql, boolean needInfo,
                            String hint) {
        Identity identity = context.getIdentity();
        Channel channel = context.getChannel();
        // 获取dbDialect
        String markTableName = context.getPipeline().getParameters().getSystemSchema() + "."
                               + context.getPipeline().getParameters().getSystemMarkTable();
        String markTableColumn = context.getPipeline().getParameters().getSystemMarkTableColumn();
        synchronized (dialect.getJdbcTemplate()) {
            if (tableCheckStatus.contains(dialect.getJdbcTemplate()) == false) {
                init(dialect.getJdbcTemplate(), markTableName, markTableColumn);
                tableCheckStatus.add(dialect.getJdbcTemplate());
            }
        }

        int affectedCount = 0;
        if (needInfo) {
            String infoColumn = context.getPipeline().getParameters().getSystemMarkTableInfo();
            String info = context.getPipeline().getParameters().getChannelInfo();// 记录一下channelInfo
            String esql = MessageFormat.format(sql, new Object[] { markTableName, markTableColumn, infoColumn });
            if (hint != null) {
                esql = hint + esql;
            }
            affectedCount = dialect.getJdbcTemplate().update(esql, new Object[] { threadId, channel.getId(), info });
        } else {
            String esql = MessageFormat.format(sql, new Object[] { markTableName, markTableColumn });
            if (hint != null) {
                esql = hint + esql;
            }
            affectedCount = dialect.getJdbcTemplate().update(esql, new Object[] { threadId, channel.getId() });
        }

        if (affectedCount <= 0) {
            logger.warn("## update {} failed by [{}]", markTableName, threadId);
        } else {
            if (logger.isInfoEnabled()) {
                logger.debug("Interceptor For [{}]", identity);
            }
        }
    }

    private int currentId() {
        synchronized (this) {
            if (THREAD_COUNTER.get() == INNER_THREAD_COUNT) {
                THREAD_COUNTER.set(0);
            }

            return THREAD_COUNTER.incrementAndGet();
        }
    }

    // ========================= setter / getter ========================

    public void setInnerIdCount(int innerIdCount) {
        this.innerIdCount = innerIdCount;
    }

    public void setGlobalIdCount(int globalIdCount) {
        this.globalIdCount = globalIdCount;
    }

    public void setConfigClientService(ConfigClientService configClientService) {
        this.configClientService = configClientService;
    }

}
TOP

Related Classes of com.alibaba.otter.node.etl.load.loader.db.interceptor.operation.AbstractOperationInterceptor

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.