浅议Atlas HiveMetaStoreBridge 运行机制

在使用Atlas采集Hive元数据信息时,可以通过Hive Hook的方式实时采集元数据变动,也可以使用 HiveMetaStoreBridge将历史元数据初始化到​Atlas中。

本文分析后者即使用HiveMetaStoreBridge的方式来导入Hive的历史元数据​。

​通过执行import-hive.sh脚本来导入历史元数据,脚本中可看到主类为HiveMetaStoreBridge。

...
"${JAVA_BIN}" ${JAVA_PROPERTIES} -cp "${CP}" org.apache.atlas.hive.bridge.HiveMetaStoreBridge $IMPORT_ARGS
...

分析HiveMetaStoreBridge的源码,大致的执行流程如下​。

下面是各个方法的带注释的源码​程序。

#importDatabases方法

    /**
     * 导入数据库到Atlas。根据给定的条件导入数据库和表。
     *
     * @param failOnError        是否在出现错误时终止操作
     * @param databaseToImport  要导入的数据库名称
     * @param tableToImport     要导入的表名称
     * @throws Exception 可能的异常
     */
    private void importDatabases(boolean failOnError, String databaseToImport, String tableToImport) throws Exception {
        // 用于存储数据库名称的列表
        List<String> databaseNames = null;

        // 当数据库和表都为空时,导入所有数据库
        if (StringUtils.isEmpty(databaseToImport) && StringUtils.isEmpty(tableToImport)) {
            databaseNames = hiveClient.getAllDatabases();
        } 
        // 当数据库为空且表不为空时,检查表中是否包含数据库名称,如果是,则导入该数据库和表
        else if (StringUtils.isEmpty(databaseToImport) && StringUtils.isNotEmpty(tableToImport)) {
            // 检查表名中是否包含数据库名
            if (isTableWithDatabaseName(tableToImport)) {
                // 以"."分割表名,获取数据库名和表名
                String val[] = tableToImport.split("\\.");
                if (val.length > 1) {
                    databaseToImport = val[0];
                    tableToImport = val[1];
                }
                // 根据数据库名的模式获取数据库列表
                databaseNames = hiveClient.getDatabasesByPattern(databaseToImport);
            } else {
                // 如果表名中不包含数据库名,则导入所有数据库
                databaseNames = hiveClient.getAllDatabases();
            }
        } 
        // 当数据库不为空时,导入指定数据库及其所有表
        else {
            databaseNames = hiveClient.getDatabasesByPattern(databaseToImport);
        }

        // 如果找到了数据库,则进行导入操作
        if (!CollectionUtils.isEmpty(databaseNames)) {
            LOG.info("Found {} databases", databaseNames.size());

            // 遍历每个数据库
            for (String databaseName : databaseNames) {
                // 注册数据库实体到Atlas
                AtlasEntityWithExtInfo dbEntity = registerDatabase(databaseName);

                // 如果数据库实体成功注册,则导入数据库中的表
                if (dbEntity != null) {
                    importTables(dbEntity.getEntity(), databaseName, tableToImport, failOnError);
                }
            }
        } 
        // 如果没有找到数据库,则打印错误日志并退出程序
        else {
            LOG.error("No database found");
            System.exit(EXIT_CODE_FAILED);
        }
    }

#importTables方法

    /**
     * 导入表到Atlas。根据给定的条件导入数据库中的表。
     *
     * @param dbEntity       数据库实体对象
     * @param databaseName   数据库名称
     * @param tblName        要导入的表名称
     * @param failOnError    是否在出现错误时终止操作
     * @return 成功导入的表的数量
     * @throws Exception 可能的异常
     */
    private int importTables(AtlasEntity dbEntity, String databaseName, String tblName, final boolean failOnError) throws Exception {
        // 记录成功导入的表的数量
        int tablesImported = 0;

        // 存储表名的列表
        final List<String> tableNames;

        // 如果表名为空,则导入数据库中的所有表
        if (StringUtils.isEmpty(tblName)) {
            tableNames = hiveClient.getAllTables(databaseName);
        } 
        // 如果表名非空,则根据模式匹配获取表名列表
        else {
            tableNames = hiveClient.getTablesByPattern(databaseName, tblName);
        }

        // 如果找到要导入的表,则进行导入操作
        if (!CollectionUtils.isEmpty(tableNames)) {
            LOG.info("Found {} tables to import in database {}", tableNames.size(), databaseName);

            try {
                // 遍历每个表
                for (String tableName : tableNames) {
                    // 导入单个表,记录导入的数量
                    int imported = importTable(dbEntity, databaseName, tableName, failOnError);

                    tablesImported += imported;
                }
            } finally {
                // 根据导入结果打印日志信息
                if (tablesImported == tableNames.size()) {
                    LOG.info("Successfully imported {} tables from database {}", tablesImported, databaseName);
                } else {
                    LOG.error("Imported {} of {} tables from database {}. Please check logs for errors during import", tablesImported, tableNames.size(), databaseName);
                }
            }
        } 
        // 如果没有找到要导入的表,则打印错误日志
        else {
            LOG.error("No tables to import in database {}", databaseName);
        }

        // 返回成功导入的表的数量
        return tablesImported;
    }

#importTable方法

    /**
     * 导入单个表到Atlas。根据给定的数据库名称和表名称导入表的元数据信息。
     *
     * @param dbEntity       数据库实体对象
     * @param databaseName   数据库名称
     * @param tableName      表名称
     * @param failOnError    是否在出现错误时终止操作
     * @return 成功导入的表的数量(1表示成功,0表示失败)
     * @throws Exception 可能的异常
     */
    @VisibleForTesting
    public int importTable(AtlasEntity dbEntity, String databaseName, String tableName, final boolean failOnError) throws Exception {
        try {
            // 获取表的元数据信息
            Table table = hiveClient.getTable(databaseName, tableName);

            // 注册表实体到Atlas
            AtlasEntityWithExtInfo tableEntity = registerTable(dbEntity, table);

            // 如果表类型为EXTERNAL_TABLE,则创建相应的Hive进程实体
            if (table.getTableType() == TableType.EXTERNAL_TABLE) {
                String processQualifiedName = getTableProcessQualifiedName(metadataNamespace, table);
                // 检查进程实体是否已存在
                AtlasEntityWithExtInfo processEntity = findProcessEntity(processQualifiedName);

                if (processEntity == null) {
                    // 获取表的位置信息和创建表的查询语句
                    String tableLocationString = isConvertHdfsPathToLowerCase() ? lower(table.getDataLocation().toString()) : table.getDataLocation().toString();
                    Path location = table.getDataLocation();
                    String query = getCreateTableString(table, tableLocationString);

                    // 创建Hive进程实体
                    PathExtractorContext pathExtractorCtx = new PathExtractorContext(getMetadataNamespace(), isConvertHdfsPathToLowerCase(), awsS3AtlasModelVersion);
                    AtlasEntityWithExtInfo entityWithExtInfo = AtlasPathExtractorUtil.getPathEntity(location, pathExtractorCtx);
                    AtlasEntity pathInst = entityWithExtInfo.getEntity();
                    AtlasEntity tableInst = tableEntity.getEntity();
                    AtlasEntity processInst = new AtlasEntity(HiveDataTypes.HIVE_PROCESS.getName());

                    long now = System.currentTimeMillis();

                    processInst.setAttribute(ATTRIBUTE_QUALIFIED_NAME, processQualifiedName);
                    processInst.setAttribute(ATTRIBUTE_NAME, query);
                    processInst.setAttribute(ATTRIBUTE_CLUSTER_NAME, metadataNamespace);
                    processInst.setRelationshipAttribute(ATTRIBUTE_INPUTS, Collections.singletonList(AtlasTypeUtil.getAtlasRelatedObjectId(pathInst, RELATIONSHIP_DATASET_PROCESS_INPUTS)));
                    processInst.setRelationshipAttribute(ATTRIBUTE_OUTPUTS, Collections.singletonList(AtlasTypeUtil.getAtlasRelatedObjectId(tableInst, RELATIONSHIP_PROCESS_DATASET_OUTPUTS)));
                    String userName = table.getOwner();
                    if (StringUtils.isEmpty(userName)) {
                        userName = ApplicationProperties.get().getString(HIVE_USERNAME, "hive");
                    }
                    processInst.setAttribute(ATTRIBUTE_USER_NAME, userName);
                    processInst.setAttribute(ATTRIBUTE_START_TIME, now);
                    processInst.setAttribute(ATTRIBUTE_END_TIME, now);
                    processInst.setAttribute(ATTRIBUTE_OPERATION_TYPE, "CREATETABLE");
                    processInst.setAttribute(ATTRIBUTE_QUERY_TEXT, query);
                    processInst.setAttribute(ATTRIBUTE_QUERY_ID, query);
                    processInst.setAttribute(ATTRIBUTE_QUERY_PLAN, "{}");
                    processInst.setAttribute(ATTRIBUTE_RECENT_QUERIES, Collections.singletonList(query));

                    AtlasEntitiesWithExtInfo createTableProcess = new AtlasEntitiesWithExtInfo();

                    createTableProcess.addEntity(processInst);

                    if (pathExtractorCtx.getKnownEntities() != null) {
                        pathExtractorCtx.getKnownEntities().values().forEach(entity -> createTableProcess.addEntity(entity));
                    } else {
                        createTableProcess.addEntity(pathInst);
                    }

                    // 注册进程实体及相关实体到Atlas
                    registerInstances(createTableProcess);
                } else {
                    // 如果进程实体已存在,则打印日志信息
                    LOG.info("Process {} is already registered", processQualifiedName);
                }
            }

            // 返回成功导入的表的数量
            return 1;
        } catch (Exception e) {
            // 打印导入失败的错误日志
            LOG.error("Import failed for hive_table {}", tableName, e);

            // 根据配置决定是否在导入失败时终止操作
            if (failOnError) {
                throw e;
            }

            // 返回导入失败的标志
            return 0;
        }
    }

#registerInstance方法

    /**
     * 注册实体信息到Atlas。通过Atlas REST API将给定的实体信息注册到Atlas服务器。
     *
     * @param entities 包含要注册的实体信息的对象
     * @return 包含已注册实体信息的对象
     * @throws Exception 可能的异常
     */
    private AtlasEntitiesWithExtInfo registerInstances(AtlasEntitiesWithExtInfo entities) throws Exception {
        // 如果调试日志启用,打印要创建的实体信息数量和详细信息
        if (LOG.isDebugEnabled()) {
            LOG.debug("creating {} entities: {}", entities.getEntities().size(), entities);
        }

        // 用于存储已注册的实体信息
        AtlasEntitiesWithExtInfo ret = null;
        
        // 调用Atlas客户端的createEntities方法,将实体信息注册到Atlas
        EntityMutationResponse response = atlasClientV2.createEntities(entities);
        
        // 获取通过CREATE操作创建的实体信息
        List<AtlasEntityHeader> createdEntities = response.getEntitiesByOperation(EntityMutations.EntityOperation.CREATE);

        // 如果有成功创建的实体,则处理并返回结果
        if (CollectionUtils.isNotEmpty(createdEntities)) {
            ret = new AtlasEntitiesWithExtInfo();

            // 遍历每个创建成功的实体信息
            for (AtlasEntityHeader createdEntity : createdEntities) {
                // 根据GUID获取已创建的实体信息
                AtlasEntityWithExtInfo entity = atlasClientV2.getEntityByGuid(createdEntity.getGuid());

                // 将实体信息添加到结果对象中
                ret.addEntity(entity.getEntity());

                // 如果存在关联实体信息,则也添加到结果对象中
                if (MapUtils.isNotEmpty(entity.getReferredEntities())) {
                    for (Map.Entry<String, AtlasEntity> entry : entity.getReferredEntities().entrySet()) {
                        ret.addReferredEntity(entry.getKey(), entry.getValue());
                    }
                }

                // 打印日志,表示已成功创建实体信息
                LOG.info("Created {} entity: name={}, guid={}", entity.getEntity().getTypeName(), entity.getEntity().getAttribute(ATTRIBUTE_QUALIFIED_NAME), entity.getEntity().getGuid());
            }
        }

        // 清理结果对象中的关系属性
        clearRelationshipAttributes(ret);

        // 返回包含已注册实体信息的结果对象
        return ret;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值