storm-kafka源码分析

本文深入分析storm-kafka的源码,主要探讨trident的实现,包括storm-kafka的基础类、KafkaSpout的open和nextTuple方法,以及trident的spout流程、Coordinator和Emitter的细节。storm-kafka作为storm读取kafka消息的连接器,其核心在于处理storm-core和trident部分。storm-kafka的spout分为OpaqueTridentKafkaSpout和TransactionalTridentKafkaSpout两种类型,它们的实现涉及到Kafka配置、zk协调、分区管理和消息读取等关键功能。
摘要由CSDN通过智能技术生成

storm-kafka源码分析

@(KAFKA)[kafka, 大数据, storm]

一、概述

storm-kafka是storm用于读取kafka消息的连接器,本文主要对trident的实现部分作了解读。

(一)代码结构

storm-kafka中多7个package中,其中的org.apache.storm.kafka与org.apache.storm.kafka.trident中最核心的2个,分别用于处理storm-core与trident,其它package只是这2个的辅助。我们下面分别先简单看一下这2个package的内容。

注:还有一个包org.apache.storm.kafka.bolt用于向kafka写入数据,用得较少,暂不分析。

(二)org.apache.storm.kafka

org.apache.storm.kafka这个package包括了一些公共模块,以及storm-core的spout处理。

(三)org.apache.storm.kafka.trident

trident这个package中的类按照其功能可大致分为3类:spout, state和metric。除此之外,trident还调用了一些org.apache.storm.kafka中的类用于处理相同的事务,如metric, exception, DynamicBrokerReader等

1、spout

spout指定了如何从kafka中读取消息,根据trident的构架,它涉及的主要类为:
* OpaqueTridentKafkaSpout, TransactionalTridentKafkaSpout: 2种类型的spout
* Coordinator, TridentKafkaEmitter: 即Coordinator与Emitter的具体实现。
* GlobalPartitionInformation, ZkBrokerReader:2个重要的辅助类,分别记录了partition的信息以及如何从zk中读取kafka的状态(还有一个静态指定的,这里不分析)。

2、state

3、metric

主要涉及一个类:MaxMetric,其实还有其它metric,但在org.apache.storm.kafka中定义了。

(四)其它说明

1、线程与分区

注意,storm-kafka中的spout只是其中一个线程。
严格来说是每个partition只能由一个task负责,当然,一个task可以处理多个partition。但task和partition之间是怎么对应的呢?如何决定一个task处理哪些partition?

在trident拓扑中,多个batch会同时被处理(由MAX_SPOUT_PENDING决定),每个batch包含多个或者全部分区,每个batch读取的消息大小由fetchSizeBytes决定。

二、org.apache.storm.kafka

(一)基础类

这些基础的功能类可以大致分为以下几类:
* Bean类:表示某一种实体,包括Broker,BrokerHost, Partition 和trident.GlobalPartitionInformation
* 配置类: 包括KafkaConfig 和 SpoutConfig。
* zk读写类:包括获取state内容的ZkState,以及读取broker信息的DynamicBrokersReader和trident.ZkBrokerReader。
* 数据处理类:ZkCoordinator用于确定自已这个spout要处理哪些分区,以及某个分区对于的PartitionManager对象,而PartitionManager则真正的对某个分区进行处理了,DynamicPartitionConnections用于被PartitionManager调用以获取分区对应的SimpleConsumer,
* KafkaUtils: 一些功能方法。
另外还有一些metric和错误处理的类等,暂不介绍。

1、Broker

Broker只有2个变量:

public String host;
public int port;

表示一台kafka机器的地址与端口。

2、BrokerHosts

有2种实现:StaticHosts 与 ZkHost。
以ZkHost为例:

private static final String DEFAULT_ZK_PATH = "/brokers";
public String brokerZkStr = null;
public String brokerZkPath = null; // e.g., /kafka/brokers
public int refreshFreqSecs = 60;

可以看出,这是记录了kafka在zk中的位置(ip与路径),以及多久刷新一下这个信息。默认为/kafka/brokers,有2个子目录:

topic   ids

分别记录了topic信息及broker信息。

3、Partition

Partition记录了一个分区的具体信息,包括(所在的broker, 所属的topic,partition号)。

Partition(Broker host, String topic, int partition)

4、trident.GlobalPartitionInformation

GlobalPartitionInformation记录的是某个topic的所有分区信息,其中分区信息以一个TreeMap的形式来保存。

public String topic;
private Map<Integer, Broker> partitionMap;

它有一个getOrderedPartitions()方法,返回的就是这个topic的所有分区信息:

public List<Partition> getOrderedPartitions() {
    List<Partition> partitions = new LinkedList<Partition>();
    for (Map.Entry<Integer, Broker> partition : partitionMap.entrySet()) {
        partitions.add(new Partition(partition.getValue(), this.topic, partition.getKey(), this.bUseTopicNameForPartitionPathId));
    }
    return partitions;
}

注意,因为使用了TreeMap的数据结构,因此返回的结果就是有序的。

5、KafkaConfig

就是关于kafkaSpout的一些配置项,完整列表为:

public final BrokerHosts hosts;
public final String topic;
public final String clientId;

public int fetchSizeBytes = 1024 * 1024;
public int socketTimeoutMs = 10000;
public int fetchMaxWait = 10000;
public int bufferSizeBytes = 1024 * 1024;
public MultiScheme scheme = new RawMultiScheme();
public boolean ignoreZkOffsets = false;
public long startOffsetTime = kafka.api.OffsetRequest.EarliestTime();
public long maxOffsetBehind = Long.MAX_VALUE;
public boolean useStartOffsetTimeIfOffsetOutOfRange = true;
public int metricsTimeBucketSizeInSecs = 60;

6、SpoutConfig

SpoutConfig extends KafkaConfig

加了几个配置项:

public List<String> zkServers = null;
public Integer zkPort = null;
public String zkRoot = null;
public String id = null;

public String outputStreamId;

// setting for how often to save the current kafka offset to ZooKeeper
public long stateUpdateIntervalMs = 2000;

// Exponential back-off retry settings.  These are used when retrying messages after a bolt
// calls OutputCollector.fail().
public long retryInitialDelayMs = 0;
public double retryDelayMultiplier = 1.0;
public long retryDelayMaxMs = 60 * 1000;

7、ZkState

ZkState记录了每个partition的处理情况,它是通过读写zk来实现的,zk中的内容如下:

{"topology":{"id":"2e3226e2-ef45-4c53-b03f-aacd94068bc9","name":"ljhtest"},"offset":8066973,"partition":0,"broker":{"host":"gdc-kafka08-log.i.nease.net","port":9092},"topic":"ma30"}

上面的信息分别为topoId,拓扑名称,这个分区处理到的offset,分区号,这个分区在哪台kafka机器,哪个端口,以及topic名称。
ZkState只要提供了对这个zk信息的读写操作,如readJSON, writeJSON。

这些信息在zk中的位置通过构建KafkaConfig对象时的第3、4个参数指定,如下面的配置,则数据被写在/kafka2/ljhtest下面。因此第4个参数必须唯一,否则不同拓扑会有冲突。

 SpoutConfig kafkaConfig = new SpoutConfig(brokerHosts, "ma30", "/kafka2", "ljhtest");

而trident的默认位置为/transactional/${topo}

8、DynamicBrokersReader

读取zk中关于kafka的信息,如topic的分区等。

public List<GlobalPartitionInformation> getBrokerInfo() 

获取所有topic的分区信息。

private int getNumPartitions(String topic)

获取某个topic的分区数量。

9、trident.ZkBrokerReader

trident.ZkBrokerReader大部分功能通过DynamicBrokersReader完成,关于与zk的连接,都是通过前者完成。同时增加了以下2个方法:

  • getBrokerForTopic():返回某个topic的分区信息,返回的是GlobalPartitionInformation对象。这是由于可能同时读取多个分区的情况。
  • getAllBrokers():读取所有的分区,不指定topic。因为支持正则topic,所以有可能有多个topic。
  • refresh(): 这是一个private方法,每隔一段时间去refresh分区信息,在上面2个方法中被调用。
    每次发送一个新的batch时,会通过DynamicPartitionConnections#register()方法调用上面的方法,当时间超过refreshFreqSecs时,即会刷新分区信息。

10、ZkCoordinator

ZkCoordinator implements PartitionCoordinator

与之对应的还有个StaticCoordinator。
主要功能是读取zk中的分区信息,然后计算自己这个task负责哪些分区。

PartitionCoordinator只有3个方法:
(1)主要方法为getMyManagedPartitions(),即计算自己这个spout应该处理哪些分区。
还有refresh是去刷新分区信息的。

List<PartitionManager> getMyManagedPartitions();

(2)获取PartitionManager对象:

PartitionManager getManager(Partition partition);

(3)定期刷新分区信息

void refresh();

11、PartitionManager

记录了某个分区的连接信息,如:

Long _committedTo;
LinkedList<MessageAndOffset> _waitingToEmit = new LinkedList<MessageAndOffset>();
Partition _partition;
SpoutConfig _spoutConfig;
String _topologyInstanceId;
SimpleConsumer _consumer;
DynamicPartitionConnections _connections;
ZkState _state;

即这个分区的分区号,consumer等信息,还有用于发送消息的next()方法等,反正对某个分区的处理都在这个类中。
2个重点方法:
* fill()用于从kafka中获取消息,写到_waitingToEmit这个列表中。
* next()从上面准备的列表中读取数据,通过emit()发送出去。
* 还有ack(),fail等方法。
PartitionManager持有一个DynamicPartitionConnections对象,通过这个对象的regist方法可以获取到一个SimpleConsumer对象,从而对消息进行读取。

12、DynamicPartitionConnections

DynamicPartitionConnections用于记录broker—SimpleConsumber—-分区之间的关系。* 一个broker对应一个SimpleConsumber,但一个SimpleConsumer可以对应多个分区。尤其是spout的数量比分区数量少的时候*

主要用于创建SimpleConsumer,通过Partition信息,返回一个SimpleConsumer对象:

public SimpleConsumer register(Partition partition) {...}

以及unRegister()方法,取消关联。

Map<Broker, ConnectionInfo> _connections = new HashMap();

这个变量记录了一个broker的连接信息,其中ConnectionInfo有2个成员变量:

static class ConnectionInfo {
    SimpleConsumer consumer;
    Set<String> partitions = new HashSet<String>();

    public ConnectionInfo(SimpleConsumer consumer) {
        this.consumer =
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值