官方guide:http://flume.apache.org/FlumeUserGuide.html
flume是一个分布式、可靠、高可用的海量日志采集、聚合、传输的系统。支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(比如文本、HDFS、Hbase等)的能力 。
当节点出现故障时,日志能够被传送到其他节点上而不会丢失。Flume提供了三种级别的可靠性保障,从强到弱依次分别为:
end-to-end: 收到数据agent首先将event写到磁盘上,当数据传送成功后,再删除;如果数据发送失败,可以重新发送。
Store on failure: 这也是scribe采用的策略,当数据接收方crash时,将数据写到本地,待恢复后,继续发送
Besteffort: 数据发送到接收方后,不会进行确认
还是靠Channel。推荐使用FileChannel,事件持久化在本地文件系统里(性能较差)。
组件 |
功能 |
Agent |
使用JVM 运行Flume。每台机器运行一个agent,但是可以在一个agent中包含多个sources和sinks。 |
Client |
生产数据,运行在一个独立的线程。 |
Source |
从Client收集数据,传递给Channel。 |
Sink |
从Channel收集数据,运行在一个独立线程。 |
Channel |
连接 sources 和 sinks ,这个有点像一个队列。 |
Events |
可以是日志记录、 avro 对象等。 |
Flume的基本数据单位,它携带日志数据(字节数组形式)并且携带有头信息,这些Event由Agent外部的Source生成,然后Source会把Event推入(单个或多个)Channel中。你可以把Channel看作是一个缓冲区,它将保存事件直到Sink处理完该Event。Sink负责持久化日志或者把Event推向另一个Source。
完成对日志数据的收集,分成 transtion 和 event 打入到channel之中。
主要提供一个队列的功能,对source提供中的数据进行简单的缓存。
取出Channel中的数据,进行相应的存储文件系统,数据库,或者提交到远程服务器。 对现有程序改动最小的使用方式是使用是直接读取程序原来记录的日志文件,基本可以实现无缝接入,不需要对现有程序进行任何改动。
从整体配置上描述代理agent中sources、sinks、channels所涉及到的组件:
# Name the components on this agent
a1.sources = r1
a1.sinks = k1
a1.channels = c1
每个agent会有3个成员,分别是sources, sinks和channels。这几行行分别是给agent a1的三个成员赋予名字,单纯只是名字
详细描述agent中每一个source、sink与channel:即指定source到底是什么类型的,是文件的、http、还是thrift 的;对于sink也是同理,需要指定结果是输出到HDFS中,还是Hbase中等;对于channel 需要指定是内存,还是数据库,还是文件等。
# Describe/configure the source
a1.sources.r1.type = netcat
a1.sources.r1.bind = localhost
a1.sources.r1.port = 44444
# Describe the sink
a1.sinks.k1.type = logger
# Use a channel which buffers events in memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
对sources的配置,根据type的不同,属性名字也有所不同,比如例子里的type是netcat,那r1就有bind、port之类的属性,而如果type是exec的话,那么r1就会有command, channels等属性,有些属性是必须要指定的,不然在运行flume的时候日志会报错。其他sinks和channels的配置规则也跟sources的类似,具体要填什么值的话就要参看configuration部分的选项说明,从中选取适合项目的配置,具体可以参见官网;
# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
flume-ng agent -n a1 -c ../conf -f ../conf/example.file
-D flume.root.logger=DEBUG,console
参数说明: -n 指定agent名称(与配置文件中代理的名字相同)
-c 指定flume中配置文件的目录
-f 指定配置文件
-D flume.root.logger=DEBUG,console 设置日志等级
官方文档:http://kafka.apache.org/documentation/
Csdn博客:https://www.cnblogs.com/hei12138/p/7805475.html
Kafka是一种高吞吐量的分布式发布订阅消息系统,由Scala和Java编写,有如下特性:
kafka和 (activeMQ)不同的是:即使消息被消费,消息仍然不会被立即删除.日志文件将会根据broker中的配置要求,保留一定的时间之后删除;比如log文件保留2天,那么两天后,文件会被清除,无论其中的消息是否被消费.kafka通过这种简单的手段,来释放磁盘空间,以及减少消息消费之后对文件内容改动的磁盘IO开支;
kafka集群几乎不需要维护任何consumer和producer状态信息,这些信息有zookeeper保存;因此producer和consumer的客户端实现非常轻量级,它们可以随意离开,而不会对集群造成额外的影响;
一个Topic可以认为是一类消息,每个topic将被分成多个partition(区),每个partition在存储层面是append log文件。任何发布到此partition的消息都会被直接追加到log文件的尾部。
Offset
每条消息在文件中的位置称为offset(偏移量),offset为一个long型数字,它唯一的标记一条消息。kafka并没有提供其他额外的索引机制来存储offset,因为在kafka中几乎不允许对消息进行“随机读写”
对于consumer而言,它需要保存消费消息的offset,对于offset的保存和使用,有consumer来控制;当consumer正常消费消息时,offset将会"线性"的向前驱动,即消息将依次顺序被消费.事实上consumer可以使用任意顺序消费消息,它只需要将offset重置为任意值;
Partition
一个Topic的多个partitions,被分布在kafka集群中的多个server上;每个server(kafka实例)负责partitions中消息的读写操作;此外kafka还可以配置partitions需要备份的个数(replicas),每个partition将会被备份到多台机器上,以提高可用性;
partitions的设计目的有多个,最根本原因是kafka基于文件存储,通过分区,可以将日志内容分散到多个server上,来避免文件尺寸达到单机磁盘的上限,每个partiton都会被当前server(kafka实例)保存;可以将一个topic切分多任意多个partitions,此外越多的partitions意味着可以容纳更多的consumer,有效提升并发消费的能力;
基于replicated方案,那么就意味着需要对多个备份进行调度;每个partition都有一个server为"leader";leader负责所有的读写操作,如果leader失效,那么将会有其他follower来接管(成为新的leader);follower只是单调的和leader跟进,由此可见作为leader的server承载了全部的请求压力,因此从集群的整体考虑,有多少个partitions就意味着有多少个"leader",kafka会将"leader"均衡的分散在每个实例上,来确保整体的性能稳定;
leader-follower的同步
kafka将每个partition数据复制到多个server上,任何一个partition有一个leader和多个follower(可以没有);备份的个数可以通过broker配置文件来设定;leader处理所有的read-write请求,follower需要和leader保持同步;Follower和consumer一样,消费消息并保存在本地日志中;leader负责跟踪所有的follower状态,如果follower"落后"太多或者失效,leader将会把它从replicas同步列表中删除;当所有的follower都将一条消息保存成功,此消息才被认为是"committed",那么此时consumer才能消费它;即使只有一个replicas实例存活,仍然可以保证消息的正常发送和接收,只要zookeeper集群存活即可;(不同于其他分布式存储,比如hbase需要"多数派"存活才行)
当leader失效时,需在followers中选取出新的leader,可能此时follower落后于leader,因此需要选择一个"up-to-date"的follower;选择follower时需要兼顾一个问题,就是新leaderserver上所已经承载的partition leader的个数,如果一个server上有过多的partition leader,意味着此server将承受着更多的IO压力;在选举新leader,需要考虑到"负载均衡;
Producer将消息发布到指定的Topic中,生产者在向kafka集群发送消息的时候,可以通过指定分区来发送到指定的分区中,也可以通过指定均衡策略来将消息发送到不同的分区中,如果不指定,就会采用默认的随机均衡策略,将消息随机的存储到不同的分区中;
一个partition中的消息只会被group中的一个consumer消费;每个group中consumer消息消费互相独立;可以认为一个group是一个"订阅"者,一个Topic中的每个partions,只会被一个"订阅者"中的一个consumer消费,不过一个consumer可以消费多个partitions中的消息;
同一个group中不能有多于partitions个数的consumer同时消费,否则某些consumer将无法得到消息; 如果所有的consumer都具有相同的group,消息将会在consumers之间负载均衡;如果所有的consumer都具有不同的group,那这就是"发布-订阅";消息将会广播给所有的消费者;
kafka只能保证一个partition中的消息被某个consumer消费时,消息是顺序的,从Topic角度来说消息仍不是有序的;
如果一个my_topic有2个partitions,那么日志将会保存在my_topic_0和my_topic_1两个目录中;日志文件中保存了一序列"log entries"(日志条目),每个log entry格式为"4个字节的数字N表示消息的长度" + "N个字节的消息内容";每个日志都有一个offset来唯一的标记一条消息(8字节的数字),表示此消息在此partition中所处的起始位置;每个partition在物理存储层面,有多个log file组成(称为segment);segmentfile的命名为"最小offset";kafka;例如"00000000000;kafka";其中"最小offset"表示此segment中起始消息的offset;
其中每个partiton中所持有的segments列表信息会存储在zookeeper中,当segment文件尺寸达到一定阀值时(可以通过配置文件设定,默认1G),将会创建一个新的文件;当buffer中消息的条数达到阀值时将会触发日志信息flush到日志文件中,同时如果"距离最近一次flush的时间差"达到阀值时,也会触发flush到日志文件;如果broker失效,极有可能会丢失那些尚未flush到文件的消息;因为server意外实现,仍然会导致log文件格式的破坏(文件尾部),那么就要求当server启动时需要检测最后一个segment的文件结构是否合法并进行必要的修复;
获取消息时,需要指定offset和最大chunk尺寸,offset用来表示消息的起始位置,chunk size用来表示最大获取消息的总长度(间接的表示消息的条数);根据offset,可以找到此消息所在segment文件,然后根据segment的最小offset取差值,得到它在file中的相对位置,直接读取输出即可;
日志文件的删除策略非常简单:启动一个后台线程定期扫描log file列表,把保存时间超过阀值的文件直接删除(根据文件的创建时间);为了避免删除文件时仍然有read操作(consumer消费),采取copy-on-write方式;
官方文档:http://storm.apache.org/releases/2.0.0-SNAPSHOT/index.html
Storm是一个分布式的、高容错的实时计算系统,被业界称为实时版Hadoop。随着越来越多的场景对Hadoop的MapReduce高延迟无法容忍,比如网站统计、推荐系统、预警系统、金融系统(高频交易、股票)等等,大数据实时处理解决方案(流计算)的应用日趋广泛,目前已是分布式技术领域最新爆发点,而Storm是主流流计算技术。
Storm有如下特点:
Storm的集群里面有两种节点:控制节点(master node)和工作节点(worker node)。控制节点上面运行一个后台程序:Nimbus,它的作用类似Hadoop里面的JobTracker。Nimbus负责在集群里面分布代码,分配工作给机器,并且监控状态。
Nimbus和Supervisor之间的所有协调工作都是通过一个Zookeeper集群来完成。并且,nimbus进程和supervisor都是快速失败(fail-fast)和无状态的。所有的状态要么在Zookeeper里面,要么在本地磁盘上。这也就意味着你可以用kill -9来杀死nimbus和supervisor进程,然后再重启它们,它们可以继续工作,就好像什么都没有发生过似的。这个设计使得storm不可思议的稳定。
Nimbus:即Storm的Master,负责资源分配和任务调度。一个Storm集群只有一个Nimbus。
Supervisor:即Storm的Slave,负责接收Nimbus分配的任务,管理所有Worker,一个Supervisor节点中包含多个Worker进程。
Worker:工作进程,每个工作进程中都有多个Task。
Task:任务,在 Storm 集群中每个 Spout 和 Bolt 都由若干个任务(tasks)来执行。每个任务都与一个执行线程相对应。
Topology:计算拓扑,Storm 的拓扑是对实时计算应用逻辑的封装,它的作用与 MapReduce 的任务(Job)很相似,区别在于 MapReduce 的一个 Job 在得到结果之后总会结束,而拓扑会一直在集群中运行,直到你手动去终止它。拓扑还可以理解成由一系列通过数据流(Stream Grouping)相互关联的 Spout 和 Bolt 组成的的拓扑结构。
Stream:数据流(Streams)是 Storm 中最核心的抽象概念。一个数据流指的是在分布式环境中并行创建、处理的一组元组(tuple)的无界序列。数据流可以由一种能够表述数据流中元组的域(fields)的模式来定义。
Spout:数据源(Spout)是拓扑中数据流的来源。一般 Spout 会从一个外部的数据源读取元组然后将他们发送到拓扑中。根据需求的不同,Spout 既可以定义为可靠的数据源,也可以定义为不可靠的数据源。一个可靠的 Spout能够在它发送的元组处理失败时重新发送该元组,以确保所有的元组都能得到正确的处理;相对应的,不可靠的 Spout 就不会在元组发送之后对元组进行任何其他的处理。一个 Spout可以发送多个数据流。
Bolt:拓扑中所有的数据处理均是由 Bolt 完成的。通过数据过滤(filtering)、函数处理(functions)、聚合(aggregations)、联结(joins)、数据库交互等功能,Bolt 几乎能够完成任何一种数据处理需求。一个 Bolt 可以实现简单的数据流转换,而更复杂的数据流变换通常需要使用多个 Bolt 并通过多个步骤完成。
Stream grouping:为拓扑中的每个 Bolt 的确定输入数据流是定义一个拓扑的重要环节。数据流分组定义了在 Bolt 的不同任务(tasks)中划分数据流的方式。在 Storm 中有八种内置的数据流分组方式。
Reliability:可靠性。Storm 可以通过拓扑来确保每个发送的元组都能得到正确处理。通过跟踪由 Spout 发出的每个元组构成的元组树可以确定元组是否已经完成处理。每个拓扑都有一个“消息延时”参数,如果 Storm 在延时时间内没有检测到元组是否处理完成,就会将该元组标记为处理失败,并会在稍后重新发送该元组。
Stream
Stream是storm里面的关键抽象。一个stream是一个没有边界的tuple序列。storm提供一些原语来分布式地、可靠地把一个stream传输进一个新的stream。比如:你可以把一个tweets流传输到热门话题的流。
storm提供的最基本的处理stream的原语是spout和bolt。你可以实现Spout和Bolt对应的接口以处理你的应用的逻辑。
Spout
Spout是流的源头。比如一个spout可能从Kestrel队列里面读取消息并且把这些消息发射成一个流。又比如一个spout可以调用twitter的一个api并且把返回的tweets发射成一个流。
通常Spout会从外部数据源(队列、数据库等)读取数据,然后封装成Tuple形式,之后发送到Stream中。Spout是一个主动的角色,在接口内部有个nextTuple函数,Storm框架会不停的调用该函数。
Bolt
bolt可以接收任意多个输入stream,作一些处理,有些bolt可能还会发射一些新的stream。一些复杂的流转换,比如从一些tweet里面计算出热门话题,需要多个步骤,从而也就需要多个bolt。 Bolt可以做任何事情:运行函数, 过滤tuple, 做一些聚合, 做一些合并以及访问数据库等等。
Bolt处理输入的Stream,并产生新的输出Stream。Bolt可以执行过滤、函数操作、Join、操作数据库等任何操作。Bolt是一个被动的 角色,其接口中有一个execute(Tuple input)方法,在接收到消息之后会调用此函数,用户可以在此方法中执行自己的处理逻辑。
topology
spout和bolt所组成一个网络会被打包成topology,topology是storm里面最高一级的抽象(类似 Job);
图片有三种模式:
storm使用tuple来作为它的数据模型。每个tuple是一堆值,每个值有一个名字,并且每个值可以是任何类型,tuple可以看作一个没有方法的java对象。总体来看,storm支持所有的基本类型、字符串以及字节数组作为tuple的值类型。你也可以使用你自己定义的类型来为值类型,只要你实现对应的序列化器(serializer)。
一个Tuple代表数据流中的一个基本的处理单元,例如一条cookie日志,它可以包含多个Field,每个Field表示一个属性。
Tuple本来应该是一个Key-Value的Map,由于各个组件间传递的tuple的字段名称已经事先定义好了,所以Tuple只需要按序填入各个Value,所以就是一个Value List。一个没有边界的、源源不断的、连续的Tuple序列就组成了Stream。
Storm的数据交互图可以看出两个模块Nimbus和Supervisor之间没有直接交互。状态都是保存在Zookeeper上。Worker之间通过ZeroMQ传送数据。