我们一起来了解Source、Channel和Sink的全链路过程。

一、Flume架构分析

这个图中核心的组件是:

Source,ChannelProcessor,Channel,Sink。他们的关系结构如下:

Source  {
ChannelProcessor {
Channel ch1
Channel ch2

}
}
Sink {
Channel ch;
}
SinkGroup {
Channel ch;
Sink s1;
Sink s2;

}

二、各组件详细介绍

1、Source组件

Source是数据源的总称,我们往往设定好源后,数据将源源不断的被抓取或者被推送。

常见的数据源有:ExecSource,KafkaSource,HttpSource,NetcatSource,JmsSource,AvroSource等等。

所有的数据源统一实现一个接口类如下:

@InterfaceAudience.Public
@InterfaceStability.Stable
public interface Source extends LifecycleAware, NamedComponent { /**
* Specifies which channel processor will handle this source's events.
*
* @param channelProcessor
*/
public void setChannelProcessor(ChannelProcessor channelProcessor); /**
* Returns the channel processor that will handle this source's events.
*/
public ChannelProcessor getChannelProcessor(); }

Source提供了两种机制: PollableSource(轮询拉取)和EventDrivenSource(事件驱动):

上图展示的Source继承关系类图。

通过类图我们可以看到NetcatSource,ExecSource和HttpSource属于事件驱动模型。KafkaSource,SequenceGeneratorSource和JmsSource属于轮询拉取模型。

Source接口继承了LifecycleAware接口,它的的所有逻辑的实现在接口的start和stop方法中进行。

下图是类关系方法图:

Source接口定义的是最终的实现过程,比如通过日志抓取日志,这个抓取的过程和实际操作就是在对应的Source实现中,比如:ExecSource。那么这些Source实现由谁来驱动的呢?现在我们将介绍SourceRunner类。看一下类继承结构图:

我们看一下PollableSourceRunner和EventDrivenSourceRunner的具体实现:

//PollableSourceRunner:
public void start() {
PollableSource source = (PollableSource) getSource();
ChannelProcessor cp = source.getChannelProcessor();
cp.initialize();
source.start(); runner = new PollingRunner(); runner.source = source; //Source实现类就在这里被赋与。
runner.counterGroup = counterGroup;
runner.shouldStop = shouldStop; runnerThread = new Thread(runner);
runnerThread.setName(getClass().getSimpleName() + "-" +
source.getClass().getSimpleName() + "-" + source.getName());
runnerThread.start(); lifecycleState = LifecycleState.START;
} //EventDrivenSourceRunner:
@Override
public void start() {
Source source = getSource();
ChannelProcessor cp = source.getChannelProcessor();
cp.initialize();
source.start();
lifecycleState = LifecycleState.START;
}

注:其实所有的Source实现类内部都维护着线程,执行source.start()其实就是启动了相应的线程。

刚才我们看代码,代码中一直都在展示channelProcessor这个类,同时最上面架构设计图里面也提到了这个类,那它到底是干什么呢,下面我们就对其分解。

2、Channel组件

Channel用于连接Source和Sink,Source将日志信息发送到Channel,Sink从Channel消费日志信息;Channel是中转日志信息的一个临时存储,保存有Source组件传递过来的日志信息。

先看代码如下:

ChannelSelectorConfiguration selectorConfig = config.getSelectorConfiguration();

ChannelSelector selector = ChannelSelectorFactory.create(sourceChannels, selectorConfig);

ChannelProcessor channelProcessor = new ChannelProcessor(selector);
Configurables.configure(channelProcessor, config); source.setChannelProcessor(channelProcessor);

ChannelSelectorFactory.create方法实现如下:

public static ChannelSelector create(List<Channel> channels,
ChannelSelectorConfiguration conf) {
String type = ChannelSelectorType.REPLICATING.toString();
if (conf != null){
type = conf.getType();
}
ChannelSelector selector = getSelectorForType(type);
selector.setChannels(channels);
Configurables.configure(selector, conf);
return selector;
}

其中我们看一下ChannelSelectorType这个枚举类,包括了几种类型:

public enum ChannelSelectorType {

  /**
* Place holder for custom channel selectors not part of this enumeration.
*/
OTHER(null), /**
* 复用通道选择器
*/
REPLICATING("org.apache.flume.channel.ReplicatingChannelSelector"), /**
* 多路通道选择器
*/
MULTIPLEXING("org.apache.flume.channel.MultiplexingChannelSelector");
}

ChannelSelector的类结构图如下所示:

注:RelicatingChannelSelector和MultiplexingChannelSelector是二个通道选择器,第一个是复用型通道选择器,也就是的默认的方式,会把接收到的消息发送给其他每个channel。第二个是多路通道选择器,这个会根据消息header中的参数进行通道选择。

说完通道选择器,正式来解释Channel是什么,先看一个接口类:

public interface Channel extends LifecycleAware, NamedComponent {
public void put(Event event) throws ChannelException;
public Event take() throws ChannelException;
public Transaction getTransaction();
}

注:put方法是用来发送消息,take方法是获取消息,transaction是用于事务操作。

类结构图如下:

3、Sink组件

Sink负责取出Channel中的消息数据,进行相应的存储文件系统,数据库,或者提交到远程服务器。

Sink在设置存储数据时,可以向文件系统中,数据库中,hadoop中储数据,在日志数据较少时,可以将数据存储在文件系中,并且设定一定的时间间隔保存数据。在日志数据较多时,可以将相应的日志数据存储到Hadoop中,便于日后进行相应的数据分析。

Sink接口类内容如下:

public interface Sink extends LifecycleAware, NamedComponent {
public void setChannel(Channel channel);
public Channel getChannel();
public Status process() throws EventDeliveryException;
public static enum Status {
READY, BACKOFF
}
}

Sink是通过如下代码进行的创建:

Sink sink = sinkFactory.create(comp.getComponentName(),  comp.getType());

DefaultSinkFactory.create方法如下:

public Sink create(String name, String type) throws FlumeException {
Preconditions.checkNotNull(name, "name");
Preconditions.checkNotNull(type, "type");
logger.info("Creating instance of sink: {}, type: {}", name, type);
Class<? extends Sink> sinkClass = getClass(type);
try {
Sink sink = sinkClass.newInstance();
sink.setName(name);
return sink;
} catch (Exception ex) {
System.out.println(ex);
throw new FlumeException("Unable to create sink: " + name
+ ", type: " + type + ", class: " + sinkClass.getName(), ex);
}
}

注:Sink是通过SinkFactory工厂来创建,提供了DefaultSinkFactory默认工厂,程序会查找org.apache.flume.conf.sink.SinkType这个枚举类找到相应的Sink处理类,比如:org.apache.flume.sink.LoggerSink,如果没找到对应的处理类,直接通过Class.forName(className)进行直接查找实例化实现类。

Sink的类结构图如下:

与ChannelProcessor处理类对应的是SinkProcessor,由SinkProcessorFactory工厂类负责创建,SinkProcessor的类型由一个枚举类提供,看下面代码:

public enum SinkProcessorType {
/**
* Place holder for custom sinks not part of this enumeration.
*/
OTHER(null), /**
* 故障转移 processor
*
* @see org.apache.flume.sink.FailoverSinkProcessor
*/
FAILOVER("org.apache.flume.sink.FailoverSinkProcessor"), /**
* 默认processor
*
* @see org.apache.flume.sink.DefaultSinkProcessor
*/
DEFAULT("org.apache.flume.sink.DefaultSinkProcessor"), /**
* 负载processor
*
* @see org.apache.flume.sink.LoadBalancingSinkProcessor
*/
LOAD_BALANCE("org.apache.flume.sink.LoadBalancingSinkProcessor"); private final String processorClassName; private SinkProcessorType(String processorClassName) {
this.processorClassName = processorClassName;
} public String getSinkProcessorClassName() {
return processorClassName;
}
}

SinkProcessor的类结构图如下:

说明:

1、FailoverSinkProcessor是故障转移处理器,当sink从通道拿数据信息时出错进行的相关处理,代码如下:

public Status process() throws EventDeliveryException {
// 经过了冷却时间,再次发起重试
Long now = System.currentTimeMillis();
while(!failedSinks.isEmpty() && failedSinks.peek().getRefresh() < now) {
//从失败队列中获取sink节点
FailedSink cur = failedSinks.poll();
Status s;
try {
//调用相应sink进行处理,比如将channel的数据读取存放到文件中,
//这个存放文件的动作就在process中进行。
s = cur.getSink().process();
if (s == Status.READY) {
//如果处理成功,则放到存活队列中
liveSinks.put(cur.getPriority(), cur.getSink());
activeSink = liveSinks.get(liveSinks.lastKey());
logger.debug("Sink {} was recovered from the fail list",
cur.getSink().getName());
} else {
// if it's a backoff it needn't be penalized.
//如果处理失败,则继续放到失败队列中
failedSinks.add(cur);
}
return s;
} catch (Exception e) {
cur.incFails();
failedSinks.add(cur);
}
} Status ret = null;
while(activeSink != null) {
try {
ret = activeSink.process();
return ret;
} catch (Exception e) {
logger.warn("Sink {} failed and has been sent to failover list",
activeSink.getName(), e);
activeSink = moveActiveToDeadAndGetNext();
}
}

2、LoadBalancingSinkProcessor是负载Sink处理器

首先我们和ChannelProcessor一样,我们也要重点说明一下SinkSelector这个选择器。

先看一下SinkSelector.configure方法的部分代码:

if (selectorTypeName.equalsIgnoreCase(SELECTOR_NAME_ROUND_ROBIN)) {
selector = new RoundRobinSinkSelector(shouldBackOff);
} else if (selectorTypeName.equalsIgnoreCase(SELECTOR_NAME_RANDOM)) {
selector = new RandomOrderSinkSelector(shouldBackOff);
} else {
try {
@SuppressWarnings("unchecked")
Class<? extends SinkSelector> klass = (Class<? extends SinkSelector>)
Class.forName(selectorTypeName); selector = klass.newInstance();
} catch (Exception ex) {
throw new FlumeException("Unable to instantiate sink selector: "
+ selectorTypeName, ex);
}
}

结合上面的代码,再看类结构图如下:

注:RoundRobinSinkSelector是轮询选择器,RandomOrderSinkSelector是随机分配选择器。

最后我们以KafkaSink为例看一下Sink里面的具体实现:

public Status process() throws EventDeliveryException {
Status result = Status.READY;
Channel channel = getChannel();
Transaction transaction = null;
Event event = null;
String eventTopic = null;
String eventKey = null; try {
long processedEvents = 0; transaction = channel.getTransaction();
transaction.begin(); messageList.clear();
for (; processedEvents < batchSize; processedEvents += 1) {
event = channel.take(); if (event == null) {
// no events available in channel
break;
} byte[] eventBody = event.getBody();
Map<String, String> headers = event.getHeaders(); if ((eventTopic = headers.get(TOPIC_HDR)) == null) {
eventTopic = topic;
} eventKey = headers.get(KEY_HDR); if (logger.isDebugEnabled()) {
logger.debug("{Event} " + eventTopic + " : " + eventKey + " : "
+ new String(eventBody, "UTF-8"));
logger.debug("event #{}", processedEvents);
} // create a message and add to buffer
KeyedMessage<String, byte[]> data = new KeyedMessage<String, byte[]>
(eventTopic, eventKey, eventBody);
messageList.add(data); } // publish batch and commit.
if (processedEvents > 0) {
long startTime = System.nanoTime();
producer.send(messageList);
long endTime = System.nanoTime();
counter.addToKafkaEventSendTimer((endTime-startTime)/(1000*1000));
counter.addToEventDrainSuccessCount(Long.valueOf(messageList.size()));
} transaction.commit(); } catch (Exception ex) {
String errorMsg = "Failed to publish events";
logger.error("Failed to publish events", ex);
result = Status.BACKOFF;
if (transaction != null) {
try {
transaction.rollback();
counter.incrementRollbackCount();
} catch (Exception e) {
logger.error("Transaction rollback failed", e);
throw Throwables.propagate(e);
}
}
throw new EventDeliveryException(errorMsg, ex);
} finally {
if (transaction != null) {
transaction.close();
}
} return result;
}

注:方法从channel中不断的获取数据,然后通过Kafka的producer生产者将消息发送到Kafka里面。

转自:http://www.cnblogs.com/hd-zg/p/5975399.html

Flume之核心架构深入解析的更多相关文章

  1. Flume NG基本架构与Flume NG核心概念

    导读 Flume NG是一个分布式.可靠.可用的系统,它能够将不同数据源的海量日志数据进行高效收集.聚合.移动,最后存储到一个中心化数据存储系统中. 由原来的Flume OG到现在的Flume NG, ...

  2. netty源码解解析(4.0)-1 核心架构

    netty是java开源社区的一个优秀的网络框架.使用netty,我们可以迅速地开发出稳定,高性能,安全的,扩展性良好的服务器应用程序.netty封装简化了在服务器开发领域的一些有挑战性的问题:jdk ...

  3. 大数据Hadoop核心架构HDFS+MapReduce+Hbase+Hive内部机理详解

    微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...

  4. Hadoop核心架构HDFS+MapReduce+Hbase+Hive内部机理详解

    转自:http://blog.csdn.net/iamdll/article/details/20998035 分类: 分布式 2014-03-11 10:31 156人阅读 评论(0) 收藏 举报 ...

  5. SpringMVC核心架构的具体流程

    核心架构的具体流程步骤如下: 1.首先用户发送请求-->DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行 处理,作为统一访问点,进行全局的流程控 ...

  6. Tomcat 架构原理解析到架构设计借鉴

    Tomcat 发展这么多年,已经比较成熟稳定.在如今『追新求快』的时代,Tomcat 作为 Java Web 开发必备的工具似乎变成了『熟悉的陌生人』,难道说如今就没有必要深入学习它了么?学习它我们又 ...

  7. 【PHP】用了这么久的Laravel框架,你分析过核心架构了没

    Laravel最初的设计是为了面向MVC架构的,它可以满足如事件处理.用户身份验证等各种需求.另外它还有一个由管理数据库强力支持,用于管理模块化和可扩展性代码的软件包管理器. Laravel以其简洁. ...

  8. RocketMQ架构原理解析(四):消息生产端(Producer)

    RocketMQ架构原理解析(一):整体架构 RocketMQ架构原理解析(二):消息存储(CommitLog) RocketMQ架构原理解析(三):消息索引(ConsumeQueue & I ...

  9. 浅谈 jQuery 核心架构设计

    jQuery对于大家而言并不陌生,因此关于它是什么以及它的作用,在这里我就不多言了,而本篇文章的目的是想通过对源码简单的分析来讨论 jQuery 的核心架构设计,以及jQuery 是如何利用javas ...

随机推荐

  1. 【BZOJ1731】[Usaco2005 dec]Layout 排队布局 差分约束

    [BZOJ1731][Usaco2005 dec]Layout 排队布局 Description Like everyone else, cows like to stand close to the ...

  2. 《从零开始学Swift》学习笔记(Day 44)——重写属性

    原创文章,欢迎转载.转载请注明:关东升的博客 重写实例属性 我们可以在子类中重写从父类继承来的属性,属性有实例属性和静态属性之分,他们在具体实现也是不同的. 实例属性的重写一方面可以重写getter和 ...

  3. Carries

    Carries frog has nn integers a1,a2,…,ana1,a2,…,an, and she wants to add them pairwise. Unfortunately ...

  4. OAuth授权

    重新梳理下授权认证模式. OWIN OWIN的英文全称是Open Web Interface for .NET.OWIN是针对.NET平台的开放Web接口. https://blog.csdn.net ...

  5. 如何用 JavaScript 控制 Arduino?

    Arduino 运行 C 语言,而主控端运行 JavaScript,一次要编写和维护两种程序.既然浏览器和服务器都用 JavaScript,若 Arduino 也能用 JavaScript 控制,那岂 ...

  6. java线程:Atomic(原子)

    .何谓Atomic? Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位.计算机中的Atomic是指不能分割成若干部分的意思.如果一段代码被认为是Atomic,则表示这段代码在执行过程中, ...

  7. Linux运维-zabbix_agent最新版的yum安装

    agentd端可以直接使用yum来进行安装 rpm -ivh http://repo.zabbix.com/zabbix/2.4/rhel/6/x86_64/zabbix-release-2.4-1. ...

  8. 回顾: Python面向对象三大特性

    继承 待整理^_^ 封装 待整理^_^ 多态 待整理^_^

  9. Python3.6全栈开发实例[006]

    6.检查传入字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者. dic = {"k1": "v1v1", " ...

  10. 部署samba

    1.首先需要关闭防火墙 2,创建用户名 3.IP地址配置ping下能不能成功 4.yum install samba -y 进行软件包的安装 5,vim修改.etc/samba/smb.conf/的配 ...