在AbstractConfigurationProvider类中loadSources方法会将所有的source进行封装成SourceRunner放到了Map<String, SourceRunner> sourceRunnerMap之中。相关代码如下:

       Map<String, String> selectorConfig = context.getSubProperties(
BasicConfigurationConstants.CONFIG_SOURCE_CHANNELSELECTOR_PREFIX); ChannelSelector selector = ChannelSelectorFactory.create(
sourceChannels, selectorConfig); ChannelProcessor channelProcessor = new ChannelProcessor(selector);
Configurables.configure(channelProcessor, context);
source.setChannelProcessor(channelProcessor);
sourceRunnerMap.put(sourceName,
SourceRunner.forSource(source));

  每个source都有selector。上述代码会获取配置文件中关于source的selector配置信息;然后构造ChannelSelector对象selector;并封装selector对象成ChannelProcessor对象channelProcessor;执行channelProcessor.configure方法进行配置;设置soure的channelprocessor,最后封装为sourceRunner和source名称一起放入sourceRunnerMap中。 

  一、ChannelSelector selector = ChannelSelectorFactory.create(sourceChannels, selectorConfig)会根据配置文件中指定的类型实例化一个ChannelSelector(共两种ReplicatingChannelSelector复制和MultiplexingChannelSelector复用)如果没有指定类型默认是ReplicatingChannelSelector,也就是配置文件中不用配置selector会将每个event复制发送到多个channel;selector.setChannels(channels);对此slector进行配置configure(context)。这两中selector都实现了三个方法getRequiredChannels(Event event)、getOptionalChannels(Event event) 以及configure(Context context)。其实Event要发送到的channel有两种组成:RequiredChannels和OptionalChannels,对应两个方法。

  (1)ReplicatingChannelSelector的configure(context)方法会获得通过"optional"在配置文件中指定的可选发送的channels(可以多个,通过空格分割);获取requiredChannels是此source对应的channel中可以活动的channel列表;然后获取所有channel的名字及其与channel的映射channelNameMap;然后将可选的channel加入optionalChannels并从requiredChannels去掉有对应的channel,在这里并没有检查可选channel的合法性以及可以配置此source指定的channel之外的channel,requiredChannels和optionalChannels不能有交集,有交集的话会从requiredChannels中删除相交的channel,所以如果配置文件中optional指定的channel列表和source指定的列表相同getOptionalChannels方法有可能会返回全部可活动channel列表使得数据重复,所以建议optional指定的channel最好是source指定之外的其他channel(比如是其他source的channel)。getOptionalChannels方法就是直接返回optionalChannels列表,getRequiredChannels方法返回requiredChannels列表,如果requiredChannels为null,则返回全部的可以活动的channel列表。

  (2)MultiplexingChannelSelector的configure(context)先获取要匹配的event的header的headerName,只能选择一个headerName;获得默认发送到的channel列表defaultChannels,可以指定多个默认channel;获得mapping的各个子值,及对应的channel名称mapConfig;用来存储header不同的值及其对应的要发送到的channel列表(每个map可以发送到多个channel中,每个channel也可以同时对应多个mapping),存入channelMapping(这个数据结构是用来存储mapping值及对应的channel列表的);optionalChannels是配置的可选值及其要发送到的channel列表的映射关系,channelMapping中已经出现的channel不允许再次在optionalChannels出现(防止数据重复),如果channelMapping没有这个值对应的channel列表(表示可能会使用默认的channel列表)则使过滤与默认channel列表的交集,optionalChannels存储的是对应header的各个值及其等于该值的event要发送到的可选择的channel列表。getOptionalChannels(Event event)方法返回的是optionalChannels中该event的指定header对应的可选择的channel列表。getRequiredChannels(Event event)方法返回的是channelMapping中该event的指定header对应的channel列表,如果为null(表示由于该event的headers没有匹配的channel就发送到默认的channel中)就返回默认发送列表defaultChannels。需要说明的是选择器配置文件中的"default"、"mapping."、"optional."这三个是同等级的,没有匹配后两者的值时才会选择发送到default对应的channel列表,后两者的值都是event的header中对应配置文件中指定的"header"的各种值。当调用getRequiredChannels(Event event)和getOptionalChannels(Event event)方法时都会对这个event的相应header查找对应要发送到的channel列表。

  二、 ChannelProcessor channelProcessor = new ChannelProcessor(selector)这个是封装选择器构造channelprocessor。其构造方法会赋值selector并构造一个InterceptorChain对象interceptorChain。ChannelProcessor类负责管理选择器selector和拦截器interceptor。

  三、执行channelProcessor.configure(Context)进行必要的配置,该方法会调用channelProcessor.configureInterceptors(context)对拦截器们进行获取和配置,configureInterceptors方法会先从配置文件中获取interceptor的组件名字interceptorNames[](可以多个),然后获取所有的“interceptors.”的配置信息interceptorContexts,然后遍历所有interceptorNames从配置文件中获取属于这个interceptor的配置信息及类型(type),根据类型构建相应的interceptor并进行配置configure,加入interceptors列表(用来存放实例化的interceptor);最后将列表传递给interceptorChain。关于更多interceptor的信息可以看这篇Flume-NG源码阅读之Interceptor(原创) 。  

  四、source.setChannelProcessor(channelProcessor)赋值。各个source通过getChannelProcessor()方法获取processor调用其processEventBatch(events)或者processEvent(event)来将event送到channel中。

  五、sourceRunnerMap.put(sourceName,SourceRunner.forSource(source))将source封装成SourceRunner放入sourceRunnerMap。SourceRunner.forSource会根据这个source所实现的接口封装成不同的Runner,有两种接口PollableSource和EventDrivenSource,前者是有自己线程来驱动的需要实现process方法,后者是没有单独的线程来驱动的没有process方法。

 public static SourceRunner forSource(Source source) {
SourceRunner runner = null; if (source instanceof PollableSource) {
runner = new PollableSourceRunner();
((PollableSourceRunner) runner).setSource((PollableSource) source);
} else if (source instanceof EventDrivenSource) {
runner = new EventDrivenSourceRunner();
((EventDrivenSourceRunner) runner).setSource((EventDrivenSource) source);
} else {
throw new IllegalArgumentException("No known runner type for source "
+ source);
} return runner;
}

  (1)PollableSourceRunner的start()方法会获取source的ChannelProcessor,然后执行其initialize()方法,该方法会调用interceptorChain.initialize()方法对拦截器们进行初始化(遍历所有拦截器然后执行拦截器的initialize()方法);然后执行source.start()启动source;再启动一个线程PollingRunner,它的run方法会始终执行source.process()并根据返回的状态值做一些统计工作。

  (2)EventDrivenSourceRunner的start()方法会获取source的ChannelProcessor,然后执行其initialize()方法,该方法会调用interceptorChain.initialize()方法对拦截器们进行初始化(遍历所有拦截器然后执行拦截器的initialize()方法);然后执行source.start()启动source。

  这样就完成了sourceRunnerMap的组装。当在Application中的startAllComponents方法中通过materializedConfiguration.getSourceRunners()获取所有的SourceRunner并放入supervisor.supervise中去执行,会调用到SourceRunner.start()方法,即上面刚讲到的内容。这样source就启动了。然后当将封装的Events或者Event发送到channel时,需要使用对应的方法ChannelProcessor.processEventBatch(List<Event> events)或者ChannelProcessor.processEvent(Event event)就可以将数据从source传输到channel中,这两个方法都会在开始调用interceptorChain.intercept(events)或者interceptorChain.intercept(event)对event增加headers(如果有多个interceptor会遍历interceptors处理每个event)。ChannelProcessor都是通过在source中直接调用getChannelProcessor()(在所有的source的父类AbstractSource中实现的)获得。看一看processEventBatch(List<Event> events)代码:

 public void processEventBatch(List<Event> events) {
Preconditions.checkNotNull(events, "Event list must not be null"); events = interceptorChain.intercept(events); Map<Channel, List<Event>> reqChannelQueue = //需要发送到的每个channel及其要发送到这个channel的event列表
new LinkedHashMap<Channel, List<Event>>(); Map<Channel, List<Event>> optChannelQueue = //可选的每个channel及其要发送到这个channel的event列表
new LinkedHashMap<Channel, List<Event>>(); for (Event event : events) {
List<Channel> reqChannels = selector.getRequiredChannels(event); //获取需要发送到的所有channel for (Channel ch : reqChannels) {
List<Event> eventQueue = reqChannelQueue.get(ch);
if (eventQueue == null) {
eventQueue = new ArrayList<Event>();
reqChannelQueue.put(ch, eventQueue);
}
eventQueue.add(event); //将event放入对应channel的event列表
} List<Channel> optChannels = selector.getOptionalChannels(event); //获取可选的要发送到的所有channel for (Channel ch: optChannels) {
List<Event> eventQueue = optChannelQueue.get(ch);
if (eventQueue == null) {
eventQueue = new ArrayList<Event>();
optChannelQueue.put(ch, eventQueue);
} eventQueue.add(event); //将event放入对应channel的event列表
}
} // Process required channels
for (Channel reqChannel : reqChannelQueue.keySet()) {
Transaction tx = reqChannel.getTransaction(); //创建事务
Preconditions.checkNotNull(tx, "Transaction object must not be null");
try {
tx.begin(); List<Event> batch = reqChannelQueue.get(reqChannel); for (Event event : batch) { //发送到需要发送到的channel
reqChannel.put(event);
} tx.commit();
} catch (Throwable t) {
tx.rollback(); //事务回滚
if (t instanceof Error) {
LOG.error("Error while writing to required channel: " +
reqChannel, t);
throw (Error) t;
} else {
throw new ChannelException("Unable to put batch on required " +
"channel: " + reqChannel, t);
}
} finally {
if (tx != null) {
tx.close();
}
}
}

  上述代码不复杂,会获得所有需要发送到的channel和所有可选的channel,然后针对每个channel,将所有event放入一个列表与该channel组成映射;然后会遍历两种channel列表中的每个channel将它对应的所有event发送到对应的channel中。这个方法写的不够友好,还可以再优化,因为方法的参数本身就是一个列表可以省去一层for循环,直接将reqChannelQueue.put(ch, eventQueue)和optChannelQueue.put(ch, eventQueue)中的eventQueue改为传递过来的参数List<Event> events就可以达到优化的目的。

  processEvent(Event event)方法就更简单了,将这个event发送到这两种channel列表中每个channel就可以。

  在发送到channel的过程中我们也发现都会有事务的创建(getTransaction())、开始(tx.begin())、提交(tx.commit())、回滚(tx.rollback())、关闭(tx.close())等操作,这是必须的。在sink中这些操作需要显示的去调用,而在source端则封装在processEvent和processEventBatch方法中,不需要显示的调用了,但不是不调用。

  至此,sourceRunner的配置、初始化、执行就讲解完毕了。在配置文件中看到的interceptor和selector都是在这里进行配置及执行的。通过了解上述,我们自定义source组件是不是更容易了。呵呵

  后续还有精彩内容!敬请期待哈!

Flume-NG源码阅读之SourceRunner,及选择器selector和拦截器interceptor的执行的更多相关文章

  1. Mina源码阅读笔记(七)—Mina的拦截器FilterChain

    Filter我们很熟悉,在Mina中,filter chain的用法也类似于Servlet的filters,这种拦截器的设计思想能够狠轻松的帮助我们实现对资源的统一处理.我们先大致连接下mina中的f ...

  2. 21 BasicTaskScheduler基本任务调度器(一)——Live555源码阅读(一)任务调度相关类

    21_BasicTaskScheduler基本任务调度器(一)——Live555源码阅读(一)任务调度相关类 BasicTaskScheduler基本任务调度器 BasicTaskScheduler基 ...

  3. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  4. ng2048源码阅读

    ng2048源码阅读 Tutorial: http://www.ng-newsletter.com/posts/building-2048-in-angularjs.html Github: http ...

  5. Pytorch版本yolov3源码阅读

    目录 Pytorch版本yolov3源码阅读 1. 阅读test.py 1.1 参数解读 1.2 data文件解析 1.3 cfg文件解析 1.4 根据cfg文件创建模块 1.5 YOLOLayer ...

  6. Flume-NG源码阅读之AvroSink

    org.apache.flume.sink.AvroSink是用来通过网络来传输数据的,可以将event发送到RPC服务器(比如AvroSource),使用AvroSink和AvroSource可以组 ...

  7. 编译spark源码及塔建源码阅读环境

    编译spark源码及塔建源码阅读环境 (一),编译spark源码 1,更换maven的下载镜像: <mirrors> <!-- 阿里云仓库 --> <mirror> ...

  8. spark源码阅读

    根据spark2.2的编译顺序来确定源码阅读顺序,只阅读核心的基本部分. 1.common目录 ①Tags②Sketch③Networking④Shuffle Streaming Service⑤Un ...

  9. Sping学习笔记(一)----Spring源码阅读环境的搭建

    idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...

随机推荐

  1. JRE not compatible with workspace .class file compatibility: 1.7

    在进行Eclipse开发的时候,经常会遇到一些小问题,现在开始每天积累一些小问题的解决方法.出现:JRE not compatible with workspace .class file compa ...

  2. WebBrowser 控件-说明

    WebBrowser.Document 为活动的文档返回自动化对象,引用 Microsoft HTML Object Library 可查看详细属性和方法 下面的解说假设窗体中有一个名称为 Web1 ...

  3. simplest_ffmpeg_grabdesktop:屏幕录制。 simplest_ffmpeg_readcamera:读取摄像头

    最简单的基于FFmpeg的AVDevice例子(屏幕录制) - 雷霄骅(leixiaohua1020)的专栏 - CSDN博客 https://blog.csdn.net/leixiaohua1020 ...

  4. java基础10 吃货联盟点餐系统

    public class OrderMsg { public static void main(String[] args) throws Exception { /** * 订餐人姓名.选择菜品.送 ...

  5. 转!!SQL左右连接中的on and和on where的区别

    原博文地址:http://blog.csdn.net/xingzhemoluo/article/details/39677891 原先一直对SQL左右连接中的on and和on where的区别不是太 ...

  6. 原!!关于java 单元测试Junit4和Mock的一些总结

    最近项目有在写java代码的单元测试,然后在思考一个问题,为什么要写单元测试??单元测试写了有什么用??百度了一圈,如下: 软件质量最简单.最有效的保证: 是目标代码最清晰.最有效的文档: 可以优化目 ...

  7. Django的模型层(1)- 单表操作(下)

    一.查询表记录 在学习查询表记录之前,先了解一下QuerySet,这是一种类似列表的数据类型,是由ORM创建的.我们学习查询表记录的方法时,一定要明确哪些方法返回了QuerySet类型,哪些方法返回m ...

  8. day4-迭代器、生成器yield

    一.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大优 ...

  9. python代码结构

    1. 使用#单行注释,使用'''...'''多行注释 2. 使用连接符\来把一行过长的代码分为多行 3. 用缩进来控制代码块,推荐使用PEP8缩进风格,即四个空格 4. if ...: elif... ...

  10. 在python中如何使用多进制数字

    我们在python中,除十进制外还可以使用二进制.八进制和十六进制 1.二进制数字由0和1组成,我们使用0b或0B前缀表示二进制数 2.使用bin()函数将一个数字转换为它的二进制形式 print(b ...