LocalCatalog详解之Catalogd处理流程

我们在LocalCatalog详解之Coordinator处理流程这篇文章中介绍了,在LocalCatalog模式下,coordinator(以下简称c节点)的相关流程。在此模式下,catalogd的处理流程也与之前的会有所不同。本文笔者就跟大家一起来学习下。

Catalogd处理流程

要开启LocalCatalog模式,需要在c节点的配置文件中配置use_local_catalog为true。同样,在catalogd这边,需要设置catalog_topic_mode为minimal。这个配置项默认为full,表示启动普通的Catalog模式;还有一个mixed表示混合两种模式,这里不做过多介绍。下面就来看下,在LocalCatalog模式中,catalogd是如何向statestored发送元数据信息的。
Catalogd启动后,会创建一个名为“catalog-update-gathering-thread”的线程,专门收集元数据的更新,然后发送到statestored的指定topic,如下所示:

//catalog-server.cc
RETURN_IF_ERROR(Thread::Create("catalog-server", "catalog-update-gathering-thread",
    &CatalogServer::GatherCatalogUpdatesThread, this,
    &catalog_update_gathering_thread_));

我们也可以在catalogd的web页面看到该线程的信息:
1
线程创建之后,就会执行GatherCatalogUpdatesThread方法,这个方法会循环收集元数据的更新信息,然后保存在指定结构体中。结构体信息如下所示:

//catalog-server.h
/// The latest available set of catalog topic updates (additions/modifications, and
/// deletions). Set by the catalog_update_gathering_thread_ and protected by
/// catalog_lock_.
std::vector<TTopicItem> pending_topic_updates_;

这是一个TTopicItem类型的集合,每一个TTopicItem成员,就代表了topic中的一个entry,包含一个key和value。这里我们以表的元数据加载为例,来看下catalogd是如何收集元数据更新信息的。当我们第一次查询某个表的时候,例如全表的count计算,此时catalogd首先会去hms和nn中加载表的元数据信息,然后准备发送给statestored,此时gathering thread就会收集表的信息,相关调用栈如下所示:
2
Impala在4.0提供了一个新的配置项:enable_incremental_metadata_updates,默认是true,表示在发送topic消息到statestored的时候,发送的是分区级别的变更信息。如果改成false,就会将整个表的元数据作为一个消息发送出去。在addTableToCatalogDeltaHelper方法中,当该配置项为true,会调用addHdfsPartitionsToCatalogDelta方法,将表按照分区粒度逐个构造,调用addCatalogObject;否则直接调用addCatalogObject方法,将整个表构造为一个entry。
这里我们以functional_parquet.alltypes表为例,简单看下两种场景下的不同之处。当我们第一次对表进行count查询的时候,表的元数据需要进行加载。在false的情况下,默认会将表构造为一个entry,即类型为TABLE的TCatalogObject对象,并且包含详细的分区信息,然后发送保存到pending_topic_updates_中,如下所示:
3
如果是true,同样会将表构造为一个类型TABLE的TCatalogObject对象,但是这个对象只包含分区的id信息。每一个分区的具体信息,则会构造类型为HDFS_PARTITION的TCatalogObject对象进行保存,这样就将表与分区的信息解耦了。如下所示:
4
不管是表还是分区,最后都是通过调用addCatalogObject方法将数据保存到pending_topic_updates_。我们来继续看下后面的调用栈:
5
这里的TopicMode就是通过catalog_topic_mode配置项来进行设置的。当设置为minimal的时候,会使用CATALOG_TOPIC_V2_PREFIX作为topic中entry的key前缀,然后通过getMinimalObjectForV2方法,将上述的TCatalogObject重新构造为只包含db名和table名,如果是HDFS_PARTITION,则还会带上分区名以及分区id,其他信息则不会带上。然后将这个新的TCatalogObject通过JNI保存到BE端的pending_topic_updates_。如果配置为full,则使用CATALOG_TOPIC_V1_PREFIX作为topic中entry的key前缀,然后直接将原始的TCatalogObject传输到BE端。这里的key前缀主要是为了c节点在获取topic的时候,根据前缀进行过滤,获取对应的元数据更新信息。相关的更新信息保存到pending_topic_updates_之后,catalogd会有一个专门的回调函数来读取这个集合中的数据,然后发送到statestored:

//catalog-server.cc
StatestoreSubscriber::UpdateCallback cb =
    bind<void>(mem_fn(&CatalogServer::UpdateCatalogTopicCallback), this, _1, _2);
string filter_prefix = "!";
Status status = statestore_subscriber_->AddTopic(IMPALA_CATALOG_TOPIC,
    /* is_transient=*/ false, /* populate_min_subscriber_topic_version=*/ false,
    filter_prefix, cb);

主要就是通过UpdateCatalogTopicCallback方法来进行操作的,IMPALA_CATALOG_TOPIC对应的就是“catalog-update”这个topic,我们也可以在statestored的web页面进行查看:
6

C节点元数据缓存更新

上面我们讲了在LocalCatalog模式下,catalogd是如何收集元数据更新信息,发送到statestored的。这一节我们来看下,c节点是如何从statestored获取这些信息,然后更新本地缓存的。
当某个impalad节点被设置为coordinator之后,会注册一个回调函数,来定时获取statestored的指定topic信息,如下所示:

//impala-server.cc
if (!TestInfo::is_test() && FLAGS_is_coordinator) {
  auto catalog_cb = [this] (const StatestoreSubscriber::TopicDeltaMap& state,
      vector<TTopicDelta>* topic_updates) {
    this->CatalogUpdateCallback(state, topic_updates);
  };
  string filter_prefix = FLAGS_use_local_catalog ?
      g_CatalogService_constants.CATALOG_TOPIC_V2_PREFIX :
      g_CatalogService_constants.CATALOG_TOPIC_V1_PREFIX;
  ABORT_IF_ERROR(exec_env->subscriber()->AddTopic(
      CatalogServer::IMPALA_CATALOG_TOPIC, /* is_transient=*/ true,
      /* populate_min_subscriber_topic_version=*/ true,
      filter_prefix, catalog_cb));
}

这里的topic,就是上面catalogd发送元数据时指定的topic。可以看到,这里也是根据use_local_catalog配置项来确定用于进行过滤的前缀。当处于LocalCatalog模式下,c节点只会获取前缀为CATALOG_TOPIC_V2_PREFIX的topic消息。这里绑定的回调函数就是CatalogUpdateCallback,更新cache的相关调用流程如下所示:

CatalogUpdateCallback(impala-server.cc):2006
-UpdateCatalogCache(frontend.cc):147
--CallJniMethod(jni-util.h)
...JNI...
updateCatalogCache(JniFrontend.java):183
-updateCatalogCache(Frontend.java):399
--updateCatalogCache(CatalogdMetaProvider.java):1189
---invalidateCacheForObject(CatalogdMetaProvider.java)

最终在invalidateCacheForObject方法中,针对不同类型的TCatalogObject对象,例如DATABASE、TABLE、HDFS_PARTITION等,对cache中的缓存进行更新。主要就是更新CatalogdMetaProvider中的cache_成员,关于这个成员我们在之前的文章介绍过,这里不再赘述。

C节点获取partial metadata

LocalCatalog详解之Coordinator处理流程一文中,我们提到,在LocalCatalog模式下,c节点主要是通过CatalogdMetaProvider的相关方法来与catalogd进行通信,获取所需的元数据。这里我们仍然以loadTableList函数为例,看下相关的流程:

//CatalogdMetaProvider.java
public ImmutableCollection<TBriefTableMeta> loadTableList(final String dbName)
    throws MetaException, UnknownDBException, TException {
  ImmutableMap<String, TBriefTableMeta> res = loadWithCaching(
      ...,
      new Callable<ImmutableMap<String, TBriefTableMeta>>() {
        public ImmutableMap<String, TBriefTableMeta> call() throws Exception {
          TGetPartialCatalogObjectRequest req = newReqForDb(dbName);
          req.db_info_selector.want_brief_meta_of_tables = true;
          TGetPartialCatalogObjectResponse resp = sendRequest(req);
          ImmutableMap.Builder<String, TBriefTableMeta> map = ImmutableMap.builder();
          for (TBriefTableMeta meta : resp.db_info.brief_meta_of_tables) {
            map.put(meta.getName(), meta);
          }
          return map.build();
        }
    });
  return res.values();
}

这里我们省略了在上篇文章提到的几个参数,主要展示call方法的主体。首先是构造了一个TGetPartialCatalogObjectRequest类型的变量,这个thrift结构体的主要成员如下所示:
7
这个结构体,包含了三个级别的元数据请求:table、db和catalog,针对每种请求,又有各自具体的场景。例如TDbInfoSelector,又分为获取db信息、获取db下的table和获取db下的function。针对当前这个例子,由于loadTableList方法是获取指定db下的table list,所以TDbInfoSelector.want_brief_meta_of_tables对象设置为true。如果是其他的场景,则会设置相应的变量为true。
接着,就会按照我们在Impala与内嵌Jvm之间的交互文章中提及到的那样,先通过JNI调用BE端的方法,然后通过rpc与catalogd进行通信。最终catalogd会通过JNI调用FE端的getPartialCatalogObject方法,相关的函数调用栈如下所示:

GetPartialCatalogObject(catalog-server.cc):212
-GetPartialCatalogObject(catalog-server.cc):102
--CallJniMethod(jni-util.h)
...JNI...
getPartialCatalogObject(JniCatalog.java):267
-getPartialCatalogObject(CatalogServiceCatalog.java):3355
--doGetPartialCatalogObject(CatalogServiceCatalog.java):3409
---getPartialInfo(Db.java):492

最终在getPartialInfo方法中,通过want_brief_meta_of_tables这个条件,构造了对应的TGetPartialCatalogObjectResponse对象,并返回到catalogd的BE端。关于这个response对象的成员如下所示:
8
与request类似,也是分为了table、db和catalog三种不同类型的response。当请求返回之后,c节点就可以获取到所需要的元数据信息了,即“fetch-on-demand”。

总结

到这里,关于LocalCatalog模式下,catalogd的处理流程基本就介绍完毕了。简单总结一下,本文主要介绍了在LocalCatalog模式下,catalogd是如何获取元数据更新信息,然后发送到statestored的。接着,又介绍了c节点是如何通过statestored的topic消息来更新自己的本地缓存的。最后,我们还提到了c节点是如何通过rpc从catalogd获取partial metadata的。至此,LocalCatalog的相关介绍就已经基本结束了。此外,本文是笔者基于社区4.0.0代码的分析而来,如有错误,欢迎批评指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值