Alibaba Otter實現擴展導入nosql DB和其它DB

簡介:

       Alibaba Otter和Canal是抽取mysqlbinlog的工具,可以在不修改任何業務程序的情況下,以極小的網絡IO代價抽取業務數據庫的數據到離線分析數據庫,在網絡狀況良好的情況下,可以實現延時小於1s的近實時數據同步。但是他僅僅支持同步到mysql,oracle數據庫,實際離線分析中,往往需要數據導入到Kafka,HBase,Cassandra,GreenPlum,ElasticSearch等數據庫中,公司有這個需求,因而花了幾個禮拜擴展開發了Otter,主要步驟如下:

1、修改Manager端,使管理界面支持nosql DB的數據源和表,字段映射的配置;

1.1 數據庫類型擴展定義:

com.alibaba.otter.shared.common.model.config.data.DataMediaType

public enum DataMediaType {
	GREENPLUM,
    /** mysql DB */
    MYSQL,
    /** oracle DB */
    ORACLE,
    /** elsaticsearch  */
    ELASTICSEARCH,
    /** KAFKA DB */
    KAFKA,
    /** HBASE DB */
    HBASE,
    /** CASSANDRA DB */
    CASSANDRA,
    /** HDFS-ARVO DB */
    HDFS_ARVO,
    /** cobar */
    COBAR,
    /** tddl */
    TDDL,
    /** cache */
    MEMCACHE,
    /** mq */
    MQ,
    /** napoli */
    NAPOLI,
    /** diamond push for us */
    DIAMOND_PUSH;

    public boolean isKafka() {
        return this.equals(DataMediaType.KAFKA);
    }

    public boolean isElasticSearch() {
        return this.equals(DataMediaType.ELASTICSEARCH);
    }
    
    public boolean isHBase() {
        return this.equals(DataMediaType.HBASE);
    }

    public boolean isCassandra() {
        return this.equals(DataMediaType.CASSANDRA);
    }
    public boolean isHDFSArvo() {
        return this.equals(DataMediaType.HDFS_ARVO);
    }

    public boolean isOracle() {
        return this.equals(DataMediaType.ORACLE);
    }
    public boolean isMysql() {
        return this.equals(DataMediaType.MYSQL);
    }


    public boolean isTddl() {
        return this.equals(DataMediaType.TDDL);
    }

    public boolean isCobar() {
        return this.equals(DataMediaType.COBAR);
    }

    public boolean isMemcache() {
        return this.equals(DataMediaType.MEMCACHE);
    }

    public boolean isMq() {
        return this.equals(DataMediaType.MQ);
    }
    
    public boolean isGreenPlum() {
        return this.equals(DataMediaType.GREENPLUM);
    }

    public boolean isNapoli() {
        return this.equals(DataMediaType.NAPOLI);
    }

    public boolean isDiamondPush() {
        return this.equals(DataMediaType.DIAMOND_PUSH);
    }
}


   1.2 修改vm頁面和相應webx action,screen類,修改支持頁面參數配置和check.

注意,screen等中原來otter做了判斷非mysql或者oracle類型的不顯示,都需要硬編碼改爲可以顯示,這個有點土,害我找了好會兒微笑


check需要修改的比較多,使用的是Dwr調用的,

com.alibaba.otter.manager.biz.utils.DataSourceChecker

需要修改

 public String check(String name,String url, String username, String password, String encode, String sourceType)  測試數據源連接是否成功

public String checkMap(String namespace, String name, Long dataSourceId)  測試數據表是否連接

 public String checkNamespaceTables(final String namespace, final String name, final Long dataSourceId)  測試多個表是否select連接,對應數據源和表2個模塊。

擴展支持其它NoSql DB類型。

1.3 vm頁面修改


2、因爲Otter使用的Guava版本和HBase等的guava衝突:

otter node中大量使用了guava的MapMaker,需要升級改爲LoadingCache(這個工作量其實不小,而且要小心實現準確,待後面node中調試的時候測試);

主要修改實現不同NoSqlDB的連接獲取,表元數據查詢等:

DbDialectFactory 

DBDataSourceService 原接口返回sql的connection對象改爲Object,在調用的時候根據數據庫類型轉換爲不同的連接

// 構建第一層map
		dataSources = CacheBuilder.newBuilder().maximumSize(1000)
				.build(new CacheLoader<Long, LoadingCache<DbMediaSource, Object>>() {
					@Override
					public LoadingCache<DbMediaSource, Object> load(Long pipelineId) throws Exception {
						return CacheBuilder.newBuilder().maximumSize(1000)
								.build(new CacheLoader<DbMediaSource, Object>() {
									@Override
									public Object load(DbMediaSource dbMediaSource) throws Exception {
										// 擴展功能,可以自定義一些自己實現的 dataSource
										if (dbMediaSource.getType().isCassandra()) {
											return getCluster(dbMediaSource);
										} else if (dbMediaSource.getType().isElasticSearch()) {
											return getClient(dbMediaSource);
										} else if (dbMediaSource.getType().isHBase()) {
											return getHBaseConnection(dbMediaSource);
										} else if (dbMediaSource.getType().isHDFSArvo()) {
											return getHDFS(dbMediaSource);
										} else if (dbMediaSource.getType().isKafka()) {
											return getProducer(dbMediaSource);
										} else {
											DataSource customDataSource = preCreate(pipelineId, dbMediaSource);
											if (customDataSource != null) {
												return customDataSource;
											}
											return createDataSource(dbMediaSource.getUrl(), dbMediaSource.getUsername(),
													dbMediaSource.getPassword(), dbMediaSource.getDriver(),
													dbMediaSource.getType(), dbMediaSource.getEncode());
										}
									}
								});
					}

				});

	}


3、Node端修改:

3.1 實現不同類型的數據寫入

新建 NoSqlTemplate接口,nosql db不使用sqlTemplate的sql,直接實現增刪改和DDL的操作:

package com.alibaba.otter.node.etl.common.db.dialect;

import java.util.List;

import com.alibaba.otter.node.etl.load.exception.ConnClosedException;
import com.alibaba.otter.shared.etl.model.EventData;

public interface NoSqlTemplate {
	/**
	 * 批量執行dml數據操作,增,刪,改
	 * 
	 * @param events
	 * @return 執行失敗的記錄集合返回,失敗原因消息保存在exeResult字段中
	 */
	public List<EventData> batchEventDatas(List<EventData> events) throws ConnClosedException;

	/**
	 * 插入行數據
	 * 
	 * @param event
	 * @return 記錄返回,失敗原因消息保存在exeResult字段中
	 */
	public EventData insertEventData(EventData event) throws ConnClosedException;

	/**
	 * 更新行數句
	 * 
	 * @param event
	 * @return 記錄返回,失敗原因消息保存在exeResult字段中
	 */
	public EventData updateEventData(EventData event) throws ConnClosedException;

	/**
	 * 刪除記錄
	 * 
	 * @param event
	 * @return 記錄返回,失敗原因消息保存在exeResult字段中
	 */
	public EventData deleteEventData(EventData event) throws ConnClosedException;

	/**
	 * 建立表
	 * 
	 * @param event
	 * @return
	 */
	public EventData createTable(EventData event) throws ConnClosedException;

	/**
	 * 修改表
	 * 
	 * @param event
	 * @return
	 */
	public EventData alterTable(EventData event) throws ConnClosedException;

	/**
	 * 刪除表
	 * 
	 * @param event
	 * @return
	 */
	public EventData eraseTable(EventData event) throws ConnClosedException;

	/**
	 * 清空表
	 * 
	 * @param event
	 * @return
	 */
	public EventData truncateTable(EventData event) throws ConnClosedException;

	/**
	 * 改名錶
	 * 
	 * @param event
	 * @return
	 */
	public EventData renameTable(EventData event) throws ConnClosedException;
}

修改原 DbDialect,擴展原來的返回JdbcTemplate和SqlTemplate,在DbLoad端根據返回類型和數據類型分別判斷後執行。

<span style="white-space:pre">	</span>//JdbcTemplate在nosqldb返回相應nosql db的鏈接
<span style="white-space:pre">	</span>public <T> T getJdbcTemplate();

	/**
	 * 獲取數據操作相關類型,關係數據庫,返回SqlTemplate,返回組織的sql NoSql
	 * database放回NoSqlTemplate,執行相關操作
	 * 
	 * @return
	 */
	public <T> T getSqlTemplate();

實現不同Nosql DB的DbDialect和NoSqlTemplate;

每個數據庫系統類型2個實現類:

CassandraDialect;CassandraTemplate;

ElasticSearchDialect;ElasticSearchTemplate;

GreenPlumDialect;GreenPlumSqlTemplate gp繼承SqlTemplate,跟關係數據庫一樣實現即可;

HBaseDialect;HBaseTemplate;

KafkaDialect;KafkaTemplate

3.2  修改DbLoader模塊,支持各個數據類型寫入

com.alibaba.otter.node.etl.load.loader.db.DbLoadAction

主要修改它的inner class 

DbLoadWorker

DoCall方法:

if (dbDialect.isNoSqlDB()) {
							NoSqlTemplate nosqltemplate = (NoSqlTemplate) dbDialect.getSqlTemplate();
							failedDatas.clear(); // 先清理
							processedDatas.clear();
							if (useBatch && canBatch) {
								nosqltemplate.batchEventDatas(splitDatas);
								// 更新統計信息
								for (int i = 0; i < splitDatas.size(); i++) {
									processStat(splitDatas.get(i));
								}
							} else {
								for (EventData event : splitDatas) {
									if (event.getEventType().isDelete()) {// 刪除
										nosqltemplate.deleteEventData(event);
									} else if (event.getEventType().isUpdate()) {
										nosqltemplate.updateEventData(event);
									} else if (event.getEventType().isInsert()) {
										nosqltemplate.insertEventData(event);
									}
									processStat(event);
								}
							}
							
						} else {
							final LobCreator lobCreator = dbDialect.getLobHandler().getLobCreator();

然後基本完成,根據自己修改的過程毛糙的記錄了一下。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章