一切从kafkaSpout的open函数开始。
①、new一个ZKState的类,这个类主要是用于读写zk的数据。接下来的很多类会调用这个类中的方法,来获取partition信息、broker信息和offset信息,并更新offset信息。
②、new一个DynamicPartitionConnections类。这个类的_reader变量会存放哪个partition的leader在哪个broker上,还有每个border的host和port这些信息。这里要注意一点,这个信息的获取的路径,默认是zk的/brokers/topics和/brokers/ids下面。如果kafka的配置文件里面有指定zk的目录级别,那spoutConf的brokerHosts就要指定这个路径。
③、new一个ZkCoordinator或者StaticCoordinator类。这个类主要用来分配哪些partition分配给这个task,然后为分配而来的每个partition创建一个PartitionManager。每个PartitionManager负责一个partition的管理。获取该partition的数据、记录offset都靠这个类来协调。这个类被创建的时候,会根据自己负责的partition去zk那里获取初始化的offset。获取offset的zk路径是通过spoutConf的zkRoot和id设置的。
open函数的前期处理大概就以上这些。然后进入nextTuple函数。
①、从ZkCoordinator那里获取该task需要管理的partition对应的partitionManager。然后遍历各个partitionManager,调用每个partitionManager的next函数。
②、next函数先判断_waitintToEmit对象是否为空,如果为空,就调用fill函数进行填充。
③、fill函数主要通过KafkaUtils.fetchMessages从kafka获取消息回来,获取回来的每个消息会带有offset。然后遍历这些消息,如果消息的cur_offset大于历史offset,就把消息添加到_waitintToEmit中,并把cur_offset添加到_pending中。
④、回到next函数,遍历_waitintToEmit,把message发射出去。然后把offset从_pending中移除。
⑤、经过②、③、④这三个步骤,就把kafka中的数据发射出去了。
⑥、隔一段时间执行每个partitionManager的commit方法,这个方法就是把已经发射出去的消息的最大offset记录到zk的对应目录下。