kafka 非同步雙活方案 mirror maker2 深度解析

zzzzMing發表於2021-01-12

mirror maker2背景

通常情況下,我們都是使用一套kafka叢集處理業務。但有些情況需要使用另一套kafka叢集來進行資料同步和備份。在kafka早先版本的時候,kafka針對這種場景就有推出一個叫mirror maker的工具(mirror maker1,以下mm1即代表mirror maker1),用來同步兩個kafka叢集的資料。

最開始版本的mirror maker本質上就是一個消費者 + 生產者的程式。但它有諸多諸多不足,包括

  1. 目標叢集的Topic使用預設配置建立,但通常需要手動repartition。
  2. acl和配置修改的時候不會自動同步,給多叢集管理帶來一些困難
  3. 訊息會被DefaultPartitioner打散到不同分割槽,即對一個topic ,目標叢集的partition與源叢集的partition不一致。
  4. 任何配置修改,都會使得叢集變得不穩定。比如比較常見的增加topic到whitelist。
  5. 無法讓源叢集的producer或consumer直接使用目標叢集的topic。
  6. 不保證exactly-once,可能出現重複資料到情況
  7. mm1支援的資料備份模式較簡單,比如無法支援active <-> active互備
  8. rebalance會導致延遲

因為存在這些問題,mirror maker難以在生產環境中使用。所以kafka2.4版本,推出一個新的mirror maker2(以下mm2即代表mirror maker2)。mirror maker2基於kafka connect工具,解決了上面說的大部分問題。

今天主要介紹mirror maker2的設計,主要功能和部署。

設計和功能

整體設計

mirror maker2是基於kafka connect框架進行開發的,可以簡單地將mirror maker2視作幾個source connector和sink connector的組合。包括:

  • MirrorSourceConnector, MirrorSourceTask:用來進行同步資料的connector
  • MirrorCheckpointConnector, MirrorCheckpointTask:用來同步輔助資訊的connector,這裡的輔助資訊主要是consumer的offset
  • MirrorHeartbeatConnector, MirrorHeartbeatTask:維持心跳的connector

不過雖然mirror maker2歲基於kafka connect框架,但它卻做了一定的改造,可以單獨部署一個mirror maker2叢集,當然也可以部署在kafka connect單機或kafka connect叢集環境上。這部分後面介紹部署的時候再介紹。

和mm1一樣,在最簡單的主從備份場景中,mm2建議部署在目標(target)叢集,即從遠端消費然後本地寫入。如果部署在源叢集端,那麼出錯的時候可能會出現丟資料的情況。

其整體架構如圖:

image-20201123221826698

內部topic設計

mm2會在kafka生成多個內部topic ,來儲存源叢集topic相關的狀態和配置資訊,以及維持心跳。主要有三個內部topic:

  • hearbeat topic
  • checkpoints topic
  • offset sync topic

這幾個內部topic都比較好理解,一看名字基本就知道是幹嘛用的,值得一提的是這其中checkpoints和hearbeat功能都可以通過配置關閉。下面我們詳細介紹下這幾個topic的功能和資料格式。

heartbeat topic

在預設的配置中,源叢集和目標叢集都會有一個用於傳送心跳的topic,consumer 客戶端通過這個 topic,一方面可以確認當前的 connector 是否存活,另一方面確認源叢集是否處於可用狀態。

heartbeat topic的schema如下:

  • target cluster:接收心跳叢集
  • source cluster:傳送心跳的叢集
  • timestamp:時間戳

checkpoints topic

對應的connector(即MirrorCheckpointConnector)會定期向目標叢集傳送checkpoint資訊,主要是consumer group提交的offset ,以及相關輔助資訊。

checkpoints topic 的schema如下:

  • consumer group id (String)
  • topic (String) :包含源叢集和目標叢集的 topic
  • partition (int)
  • upstream offset (int): 源叢集指定consumer group已提交的offset(latest committed offset in source cluster)
  • downstream offset (int): 目標叢集已同步的offset(latest committed offset translated to target cluster)
  • metadata (String):partition後設資料
  • timestamp

mm2提供的另一個功能,consumer切換叢集消費就是通過這個topic實現的。因為這個topic中存放了源叢集consumer group的消費offset,在某些場景(比如源叢集故障)下要切換consumer到目標叢集,就可以通過這個topic獲取消費offset然後繼續消費。

offset sync

這個topic ,主要是在兩個叢集間同步topic partition的offset。這裡的offset並不是consumer的offset,而是日誌的offset。

它的 schema 如下:

  • topic (String):topic 名
  • partition (int)
  • upstream offset (int):源叢集的 offset
  • downstream offset (int):目標叢集的 offset,和源叢集的應該保持一致

config sync

mm2會將源叢集的資料同步到目標叢集,那麼目標叢集對應的topic的讀寫許可權上怎樣的呢?mm2約定了,目標叢集對應的topic(源叢集備份的那個)只有source和sink connector能夠寫入。為了實施此策略,MM2使用以下規則將 ACL 策略傳播到下游主題:

  • 若使用者對源叢集的topic有read的許可權,那麼對目標叢集對應的topic也有read的許可權
  • 除了mm2,別的使用者都不能寫入目標叢集對應的topic

同時會同步topic相關配置資訊

acl

consumer切換叢集

源叢集的consumer group offset ,是儲存在目標叢集的checkpoint topic中,這點我們上面已經有說到過。要獲取這些offset資訊,可以使用MirrorClient#remoteConsumerOffsets這個 api,然後就能用 consumer#seek api 根據給出的offset消費。

這裡順便提供下大致程式碼,首先maven新增依賴:

    <dependency>
      <groupId>org.apache.kafka</groupId>
      <artifactId>connect-mirror</artifactId>
      <version>2.4.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.kafka</groupId>
      <artifactId>connect-mirror-client</artifactId>
      <version>2.4.0</version>
    </dependency>

然後獲取offset資訊:

        MirrorMakerConfig mmConfig = new MirrorMakerConfig(mm2.getProp());
        MirrorClientConfig mmClientConfig = mmConfig.clientConfig("target-cluster");
        MirrorClient mmClient = new MirrorClient(mmClientConfig);

        Map<TopicPartition, OffsetAndMetadata> offsetMap =
                mmClient.remoteConsumerOffsets("my-consumer-group", "source-cluster", Duration.ofMinutes(1));

consumer#seek的用法就不演示了。

其他功能

最後順便介紹下其他比較基礎的功能。

源叢集和目標叢集partition保持同步

  • 訊息的分割槽和排序,源叢集和目標叢集都會保持一樣
  • 目標叢集的分割槽數與源叢集分割槽保持一樣
  • 目標叢集只會有一個topic與源叢集topic對應
  • 目標叢集只會有一個分割槽與源叢集的分割槽對應
  • 目標叢集的partition i對應源叢集partition i

說白了就是源叢集和目標叢集的partition和訊息會盡量保持一致,當然可能會有重複訊息的情況,因為目前還不指定exactly-once,據說後續版本會有(2.4版本以後)。

同步topic增加字首
mm1有一個缺陷,因為mm1備份資料的時候,源叢集和目標叢集的topic名稱都是一樣的,所以可能出現兩個叢集的訊息無限遞迴的情況(就是兩個名稱相同的topic,一條訊息a傳b,b再傳a,迴圈往復)。mm2解決這個缺陷,採用了給topic加一個字首的方式,如果是兩個叢集相互備份,那麼有字首的topic的訊息,是不會備份的。

同步配置和acl
mm1的時候,配置資訊和topic acl相關的資訊是不會同步的,這會給叢集管理帶來一定的困難,mm2解決了這個問題,即源叢集的配置和acl都會自動同步到目標叢集中。

說完功能,最後再介紹下部署方式。

部署方式

目前主要支援三種部署方式

  • mm2專用叢集部署:無需依賴kafka connect,mm2已經提供了一個driver可以單獨部署mm2叢集,僅需一條命令就可以啟動:./bin/connect-mirror-maker.sh mm2.properties
  • 依賴kafka connect叢集部署:需要先啟動kafka connect叢集模式,然後手動啟動每個mm2相關的connector,相對比較繁瑣。適合已經有kafka connect叢集的場景。
  • 依賴kafka connect單機部署:需要在配置檔案中配置好各個connector,然後啟動Kafka connect單機服務。不過這種方式便捷性不如mm2專用叢集模式,穩定性不如kafka connect 叢集模式,適合測試環境下部署。

mm2 相關的配置參照KIP-382,主要配置包括 source 和 target 的 broker 配置,hearbeat ,checkpoint 功能是否啟用,同步時間間隔等。

mm2獨立叢集部署

要部署mm2叢集相對比較簡單,只需要先在config/mm2.properties寫個配置檔案:

# 指定兩個叢集,以及對應的host
clusters = us-west, us-east
us-west.bootstrap.servers = host1:9092
us-east.bootstrap.servers = host2:9092

# 指定同步備份的topic & consumer group,支援正則
topics = .*
groups = .*
emit.checkpoints.interval.seconds = 10

# 指定複製鏈條,可以是雙向的
us-west->us-east.enabled = true
# us-east->us-west.enabled = true  # 雙向,符合條件的兩個叢集的topic會相互備份

# 可以自定義一些配置
us-west.offset.storage.topic = mm2-offsets
# 也可以指定是否啟用哪個鏈條的hearbeat,預設是雙向hearbeat的
us-west->us-east.emit.heartbeats.enabled = false

然後使用一條命令就可以啟動了,./bin/connect-mirror-maker.sh mm2.properties。啟動後用jps觀察程式,再list下topic,可以發現多了許多個topic,這種時候應該就啟動成功了。

順便說下,如果是使用kafka connect叢集,那需要手動啟動每個connector,類似這樣:

PUT /connectors/us-west-source/config HTTP/1.1
 
{
    "name": "us-west-source",
    "connector.class": "org.apache.kafka.connect.mirror.MirrorSourceConnector",
    "source.cluster.alias": "us-west",
    "target.cluster.alias": "us-east",
    "source.cluster.bootstrap.servers": "us-west-host1:9091",
    "topics": ".*"
}

以上~

相關文章