月度归档:2014年11月

三种循环的流程图画法总结

C语言编程中常用的三种循环为for(;;),while  和 do-while。

 

1.  for循环

for循环形式: for(表达式1;表达式2;表达式3)

流程图:

图1 for循环流程图

 

2.  while循环

while循环形式:

while(判断条件)

{

执行语句;

}

流程图:

图2 while循环流程图

 

3.  do-while循环

do-while循环形式:

do
{

执行语句;

}while(条件判断)

do-while循环流程图:

图3 do-while循环流程图

 

作者:绿茶叶
出处:http://www.cnblogs.com/greenteaone/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Hadoop2.0NameNode HA实践

天云趋势在2012年下半年开始为某大型国有银行的历史交易数据备份及查询提供基于Hadoop的技术解决方案,由于行业的特殊性,客户对服务的可 用性有着非常高的要求,而HDFS长久以来都被单点故障的问题所困扰,直到Apache Hadoop在2012年5月发布了2.0的alpha版本,其中MRv2还很不成熟,可HDFS的新功能已经基本可用,尤其是其中的的High Availability(以下简称HA)和Federation。Cloudera也于7月制作了CDH4.0.1,包含了Hadoop 2.0的诸多新功能和组件,于是我们就基于CDH4.0.1进行了HA和Federation的测试。

此工作由我和同事张军、钱兴会共同完成。

二、hadoop为什么需要HA和Federation

1. 单点故障

在Hadoop 2.0之前,也有若干技术试图解决单点故障的问题,我们在这里做个简短的总结

  • Secondary NameNode。它不是HA,它只是阶段性的合并edits和fsimage,以缩短集群启动的时间。当NameNode(以下简称NN)失效的时 候,Secondary NN并无法立刻提供服务,Secondary NN甚至无法保证数据完整性:如果NN数据丢失的话,在上一次合并后的文件系统的改动会丢失。
  • Backup NameNode (HADOOP-4539)。它在内存中复制了NN的当前状态,算是Warm Standby,可也就仅限于此,并没有failover等。它同样是阶段性的做checkpoint,也无法保证数据完整性。
  • 手动把name.dir指向NFS。这是安全的Cold Standby,可以保证元数据不丢失,但集群的恢复则完全靠手动。
  • Facebook AvatarNode。 Facebook有强大的运维做后盾,所以Avatarnode只是Hot Standby,并没有自动切换,当主NN失效的时候,需要管理员确认,然后手动把对外提供服务的虚拟IP映射到Standby NN,这样做的好处是确保不会发生脑裂的场景。其某些设计思想和Hadoop 2.0里的HA非常相似,从时间上来看,Hadoop 2.0应该是借鉴了Facebook的做法。
  • 还有若干解决方案,基本都是依赖外部的HA机制,譬如DRBD,Linux HA,VMware的FT等等。

2. 集群容量和集群性能

单NN的架构使得HDFS在集群扩展性和性能上都有潜在的问题,当集群大到一定程度后,NN进程使用的内存可能会达到上百G,常用的估算公式为1G 对应1百万个块,按缺省块大小计算的话,大概是64T (这个估算比例是有比较大的富裕的,其实,即使是每个文件只有一个块,所有元数据信息也不会有1KB/block)。同时,所有的元数据信息的读取和操作 都需要与NN进行通信,譬如客户端的addBlock、getBlockLocations,还有DataNode的blockRecieved、 sendHeartbeat、blockReport,在集群规模变大后,NN成为了性能的瓶颈。Hadoop 2.0里的HDFS Federation就是为了解决这两个问题而开发的。

三、Hadoop 2.0里HA的实现方式

图片来源: HDFS-1623 设计文档

图片作者: Sanjay Radia, Suresh Srinivas

在这个图里,我们可以看出HA的大致架构,其设计上的考虑包括:

  • 利用共享存储来在两个NN间同步edits信息。
    以前的HDFS是share nothing but NN,现在NN又share storage,这样其实是转移了单点故障的位置,但中高端的存储设备内部都有各种RAID以及冗余硬件包括电源以及网卡等,比服务器的可靠性还是略有提 高。通过NN内部每次元数据变动后的flush操作,加上NFS的close-to-open,数据的一致性得到了保证。社区现在也试图把元数据存储放到BookKeeper上,以去除对共享存储的依赖,Cloudera也提供了Quorum Journal Manager的实现和代码,这篇中文的blog有详尽分析:基于QJM/Qurom Journal Manager/Paxos的HDFS HA原理及代码分析
  • DataNode(以下简称DN)同时向两个NN汇报块信息。
    这是让Standby NN保持集群最新状态的必需步骤,不赘述。
  • 用于监视和控制NN进程的FailoverController进程
    显然,我们不能在NN进程内进行心跳等信息同步,最简单的原因,一次FullGC就可以让NN挂起十几分钟,所以,必须要有一个独立的短小精悍的 watchdog来专门负责监控。这也是一个松耦合的设计,便于扩展或更改,目前版本里是用ZooKeeper(以下简称ZK)来做同步锁,但用户可以方 便的把这个ZooKeeper FailoverController(以下简称ZKFC)替换为其他的HA方案或leader选举方案。
  • 隔离(Fencing)),防止脑裂),就是保证在任何时候只有一个主NN,包括三个方面:
    • 共享存储fencing,确保只有一个NN可以写入edits。
    • 客户端fencing,确保只有一个NN可以响应客户端的请求。
    • DataNode fencing,确保只有一个NN可以向DN下发命令,譬如删除块,复制块,等等。

四、Hadoop 2.0里Federation的实现方式

图片来源: HDFS-1052 设计文档
图片作者: Sanjay Radia, Suresh Srinivas

这个图过于简明,许多设计上的考虑并不那么直观,我们稍微总结一下

  • 多个NN共用一个集群里DN上的存储资源,每个NN都可以单独对外提供服务
  • 每个NN都会定义一个存储池,有单独的id,每个DN都为所有存储池提供存储
  • DN会按照存储池id向其对应的NN汇报块信息,同时,DN会向所有NN汇报本地存储可用资源情况
  • 如果需要在客户端方便的访问若干个NN上的资源,可以使用客户端挂载表,把不同的目录映射到不同的NN,但NN上必须存在相应的目录

这样设计的好处大致有:

  • 改动最小,向前兼容
    • 现有的NN无需任何配置改动.
    • 如果现有的客户端只连某台NN的话,代码和配置也无需改动。
  • 分离命名空间管理和块存储管理
    • 提供良好扩展性的同时允许其他文件系统或应用直接使用块存储池
    • 统一的块存储管理保证了资源利用率
    • 可以只通过防火墙配置达到一定的文件访问隔离,而无需使用复杂的Kerberos认证
  • 客户端挂载表
    • 通过路径自动对应NN
    • 使Federation的配置改动对应用透明

五、测试环境

以上是HA和Federation的简介,对于已经比较熟悉HDFS的朋友,这些信息应该已经可以帮助你快速理解其架构和实现,如果还需要深入了解细节的话,可以去详细阅读设计文档或是代码。这篇文章的主要目的是总结我们的测试结果,所以现在才算是正文开始。

为了彻底搞清HA和Federation的配置,我们直接一步到位,选择了如下的测试场景,结合了HA和Federation:

这张图里有个概念是前面没有说明的,就是NameService。Hadoop 2.0里对NN进行了一层抽象,提供服务的不再是NN本身,而是NameService(以下简称NS)。Federation是由多个NS组成的,每个 NS又是由一个或两个(HA)NN组成的。在接下里的测试配置里会有更直观的例子。

图中DN-1到DN-6是六个DataNode,NN-1到NN-4是四个NameNode,分别组成两个HA的NS,再通过Federation 组合对外提供服务。Storage Pool 1和Storage Pool 2分别对应这两个NS。我们在客户端进行了挂载表的映射,把/share映射到NS1,把/user映射到NS2,这个映射其实不光是要指定NS,还需要 指定到其上的某个目录,稍后的配置中大家可以看到。

下面我们来看看配置文件里需要做哪些改动,为了便于理解,我们先把HA和Federation分别介绍,然后再介绍同时使用HA和Federation时的配置方式,首先我们来看HA的配置:

对于HA中的所有节点,包括NN和DN和客户端,需要做如下更改:

HA,所有节点,hdfs-site.xml

<property>
    <name>dfs.nameservices</name>
    <value>ns1</value>
    <description>提供服务的NS逻辑名称,与core-site.xml里的对应</description>      
</property>

<property>
    <name>dfs.ha.namenodes.${NS_ID}</name>
    <value>nn1,nn3</value>
    <description>列出该逻辑名称下的NameNode逻辑名称</description>      
</property>

<property>
    <name>dfs.namenode.rpc-address.${NS_ID}.${NN_ID}</name>
    <value>host-nn1:9000</value>
    <description>指定NameNode的RPC位置</description>      
</property>

<property>
    <name>dfs.namenode.http-address.${NS_ID}.${NN_ID}</name>
    <value>host-nn1:50070</value>
    <description>指定NameNode的Web Server位置</description>      
</property>

以上的示例里,我们用了${}来表示变量值,其展开后的内容大致如下:

<property> <name>dfs.ha.namenodes.ns1</name> <value>nn1,nn3</value> </property>

<property> <name>dfs.namenode.rpc-address.ns1.nn1</name> <value>host-nn1:9000</value> </property>

<property> <name>dfs.namenode.http-address.ns1.nn1</name> <value>host-nn1:50070</value> </property>

<property> <name>dfs.namenode.rpc-address.ns1.nn3</name> <value>host-nn3:9000</value> </property>

<property> <name>dfs.namenode.http-address.ns1.nn3</name> <value>host-nn3:50070</value> </property>

与此同时,在HA集群的NameNode或客户端还需要做如下配置的改动:

HA,NameNode,hdfs-site.xml

<property>
    <name>dfs.namenode.shared.edits.dir</name>
    <value>file:///nfs/ha-edits</value>
    <description>指定用于HA存放edits的共享存储,通常是NFS挂载点</description>
</property>

<property>
    <name>ha.zookeeper.quorum</name>
    <value>host-zk1:2181,host-zk2:2181,host-zk3:2181,</value>
    <description>指定用于HA的ZooKeeper集群机器列表</description>
</property>

<property>
    <name>ha.zookeeper.session-timeout.ms</name>
    <value>5000</value>
    <description>指定ZooKeeper超时间隔,单位毫秒</description>
</property>

<property>
    <name>dfs.ha.fencing.methods</name>
    <value>sshfence</value>
    <description>指定HA做隔离的方法,缺省是ssh,可设为shell,稍后详述</description>
</property>
 
HA,客户端,hdfs-site.xml

<property>
    <name>dfs.ha.automatic-failover.enabled</name>
    <value>true</value>
    <description>或者false</description>
</property>

<property>
    <name>dfs.client.failover.proxy.provider.${NS_ID}</name>
    <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
    <description>指定客户端用于HA切换的代理类,不同的NS可以用不同的代理类
        以上示例为Hadoop 2.0自带的缺省代理类</description>
</property>

最后,为了方便使用相对路径,而不是每次都使用hdfs://ns1作为文件路径的前缀,我们还需要在各角色节点上修改core-site.xml:

HA,所有节点,core-site.xml

<property>
    <name>fs.defaultFS</name>
    <value>hdfs://ns1</value>
    <description>缺省文件服务的协议和NS逻辑名称,和hdfs-site里的对应
        此配置替代了1.0里的fs.default.name</description>      
</property>

接下来我们看一下如果单独使用Federation,应该如何配置,这里我们假设没有使用HA,而是直接使用nn1和nn2组成了 Federation集群,他们对应的NS的逻辑名称分别是ns1和ns2。为了便于理解,我们从客户端使用的core-site.xml和挂载表入手:

Federation,所有节点,core-site.xml

<xi:include href=“cmt.xml"/>
<property>
    <name>fs.defaultFS</name>
    <value>viewfs://nsX</value>
    <description>整个Federation集群对外提供服务的NS逻辑名称,
        注意,这里的协议不再是hdfs,而是新引入的viewfs
        这个逻辑名称会在下面的挂载表中用到</description>
</property>

我们在上面的core-site中包含了一个cmt.xml文件,也就是Client Mount Table,客户端挂载表,其内容就是虚拟路径到具体某个NS及其物理子目录的映射关系,譬如/share映射到ns1的/real_share, /user映射到ns2的/real_user,示例如下:

Federation,所有节点,cmt.xml

<configuration>
    <property>
        <name>fs.viewfs.mounttable.nsX.link./share</name>
        <value>hdfs://ns1/real_share</value>
    </property>
    <property>
        <name>fs.viewfs.mounttable.nsX.link./user</name>
        <value>hdfs://ns2/real_user</value>
    </property>
</configuration>

注意,这里面的nsX与core-site.xml中的nsX对应。而且对每个NS,你都可以建立多个虚拟路径,映射到不同的物理路径。与此同时,hdfs-site.xml中需要给出每个NS的具体信息:

Federation,所有节点,hdfs-site.xml

<property>
    <name>dfs.nameservices</name>
    <value>ns1,ns2</value>
    <description>提供服务的NS逻辑名称,与core-site.xml或cmt.xml里的对应</description>      
</property>

<property>
    <name>dfs.namenode.rpc-address.ns1</name>
    <value>host-nn1:9000</value>
</property>

<property>
    <name>dfs.namenode.http-address.ns1</name>
    <value>host-nn1:50070</value>
</property>

<property>
    <name>dfs.namenode.rpc-address.ns2</name>
    <value>host-nn2:9000</value>
</property>

<property>
    <name>dfs.namenode.http-address.ns2</name>
    <value>host-nn2:50070</value>
</property>

可以看到,在只有Federation且没有HA的情况下,配置的name里只需要直接给出${NS_ID},然后value就是实际的机器名和端口号,不需要再.${NN_ID}。

这里有一个情况,就是NN本身的配置。从上面的内容里大家可以知道,NN上是需要事先建立好客户端挂载表映射的目标物理路径,譬如 /real_share,之后才能通过以上的映射进行访问,可是,如果不指定全路径,而是通过映射+相对路径的话,客户端只能在挂载点的虚拟目录之下进行 操作,从而无法创建映射目录本身的物理目录。所以,为了在NN上建立挂载点映射目录,我们就必须在命令行里使用hdfs协议和绝对路径:

hdfs dfs -mkdir hdfs://ns1/real_share

上面这个问题,我在EasyHadoop的聚会上没有讲清楚,只是简单的说在NN上不要使用viewfs://来配置,而是使用hdfs://,那样是可以解决问题,但是是并不是最好的方案,也没有把问题的根本说清楚。

最后,我们来组合HA和Federation,真正搭建出和本节开始处的测试环境示意图一样的实例。通过前面的描述,有经验的朋友应该已经猜到了, 其实HA+Federation配置的关键,就是组合hdfs-site.xml里的dfs.nameservices以及 dfs.ha.namenodes.${NS_ID},然后按照${NS_ID}和${NN_ID}来组合name,列出所有NN的信息即可。其余配置一 样。

HA + Federation,所有节点,hdfs-site.xml

<property>
    <name>dfs.nameservices</name>
    <value>ns1, ns2</value>
</property>

<property>
    <name>dfs.ha.namenodes.ns1</name>
    <value>nn1,nn3</value>
</property>

<property>
    <name>dfs.ha.namenodes.ns2</name>
    <value>nn2,nn4</value>
</property>

<property>
    <name>dfs.namenode.rpc-address.ns1.nn1</name>
    <value>host-nn1:9000</value>
</property>

<property>
    <name>dfs.namenode.http-address.ns1.nn1</name>
    <value>host-nn1:50070</value>
</property>

<property>
    <name>dfs.namenode.rpc-address.ns1.nn3</name>
    <value>host-nn3:9000</value>
</property>

<property>
    <name>dfs.namenode.http-address.ns1.nn3</name>
    <value>host-nn3:50070</value>
</property>

<property>
    <name>dfs.namenode.rpc-address.ns2.nn2</name>
    <value>host-nn2:9000</value>
</property>

<property>
    <name>dfs.namenode.http-address.ns2.nn2</name>
    <value>host-nn2:50070</value>
</property>

<property>
    <name>dfs.namenode.rpc-address.ns2.nn4</name>
    <value>host-nn4:9000</value>
</property>

<property>
    <name>dfs.namenode.http-address.ns2.nn4</name>
    <value>host-nn4:50070</value>
</property>
 

对于没有.${NS_ID},也就是未区分NS的项目,需要在每台NN上分别使用不同的值单独配置,尤其是NFS位置 (dfs.namenode.shared.edits.dir),因为不同NS必定要使用不同的NFS目录来做各自内部的HA (除非mount到本地是相同的,只是在NFS服务器端是不同的,但这样是非常不好的实践);而像ZK位置和隔离方式等其实大可使用一样的配置。

除了配置以外,集群的初始化也有一些额外的步骤,譬如,创建HA环境的时候,需要先格式化一台NN,然后同步其name.dir下面的数据到第二 台,然后再启动集群 (我们没有测试从单台升级为HA的情况,但道理应该一样)。在创建Federation环境的时候,需要注意保持${CLUSTER_ID}的值,以确保 所有NN能共享同一个集群的存储资源,具体做法是在格式化第一台NN之后,取得其${CLUSTER_ID}的值,然后用如下命令格式化其他NN:

hadoop namenode -format -clusterid ${CLUSTER_ID}

当然,你也可以从第一台开始就使用自己定义的${CLUSTER_ID}值。

如果是HA + Federation的场景,则需要用Federation的格式化方式初始化两台,每个HA环境一台,保证${CLUSTER_ID}一致,然后分别同步name.dir下的元数据到HA环境里的另一台上,再启动集群。

Hadoop 2.0中的HDFS客户端和API也有些许更改,命令行引入了新的hdfs命令,hdfs dfs就等同于以前的hadoop fs命令。API里引入了新的ViewFileSystem类,可以通过它来获取挂载表的内容,如果你不需要读取挂载表内容,而只是使用文件系统的话,可 以无视挂载表,直接通过路径来打开或创建文件。代码示例如下:

ViewFileSystem fsView = (ViewFileSystem) ViewFileSystem.get(conf);
MountPoint[] m = fsView.getMountPoints();
for (MountPoint m1 : m)
    System.out.println( m1.getSrc() );

// 直接使用/share/test.txt创建文件
// 如果按照之前的配置,客户端会自动根据挂载表找到是ns1
// 然后再通过failover proxy类知道nn1是Active NN并与其通信
Path p = new Path("/share/test.txt");
FSDataOutputStream fos = fsView.create(p);

六、HA测试方案和结果

Federation的测试主要是功能性上的,能用就OK了,这里的测试方案只是针对HA而言。我们设计了两个维度的测试矩阵:系统失效方式,客户端连接模型

系统失效有两种:

  1. 终止NameNode进程:ZKFC主动释放锁
    模拟机器OOM、死锁、硬件性能骤降等故障
  2. NN机器掉电:ZK锁超时
    模拟网络和交换机故障、以及掉电本身

客户端连接也是两种:

  1. 已连接的客户端(持续拷贝96M的文件,1M每块)
    通过增加块的数目,我们希望客户端会不断的向NN去申请新的块;一般是在第一个文件快结束或第二个文件刚开始拷贝的时候使系统失效。
  2. 新发起连接的客户端(持续拷贝96M的文件,100M每块)
    因为只有一个块,所以在实际拷贝过程中失效并不会立刻导致客户端或DN报错,但下一次新发起连接的客户端会一开始就没有NN可连;一般是在第一个文件快结束拷贝时使系统失效。

针对每一种组合,我们反复测试10-30次,每次拷贝5个文件进入HDFS,因为时间不一定掐的很准,所以有时候也会是在第三或第四个文件的时候才使系统失效,不管如何,我们会在结束后从HDFS里取出所有文件,并挨个检查文件MD5,以确保数据的完整性。

测试结果如下:

  • ZKFC主动释放锁
    • 5-8秒切换(需同步edits)
    • 客户端偶尔会有重试(~10%)
    • 但从未失败
  • ZK锁超时
    • 15-20s切换(超时设置为10s)
    • 客户端重试几率变大(~75%)
    • 且偶有失败(~15%),但仅见于已连接客户端
  • 可确保数据完整性
    • MD5校验从未出错 +失败时客户端有Exception

我们的结论是:Hadoop 2.0里的HDFS HA基本可满足高可用性

扩展测试

我们另外还(试图)测试Append时候NN失效的情形,因为Append的代码逻辑非常复杂,所以期望可以有新的发现,但是由于复杂的那一段只是 在补足最尾部块的时候,所以必须在测试程序一运行起来就关掉NN,测了几次,没发现异常情况。另外我们还使用HBase进行了测试,由于WAL只是 append,而且HFile的compaction操作又并不频繁,所以也没有遇到问题。

七、HA推荐配置及其他

HA推荐配置

  • ha.zookeeper.session-timeout.ms = 10000
    • ZK心跳是2000
    • 缺省的5000很容易因为网络拥塞或NN GC等导致误判
    • 为避免电源闪断,不要把start-dfs.sh放在init.d里
  • dfs.ha.fencing.methods = shell(/path/to/the/script)
    • STONITH (Shoot The Other Node In The Head)不一定可行,当没有网络或掉电的时候,是没法shoot的
    • 缺省的隔离手段是sshfence,在掉电情况下就无法成功完成,从而切换失败
    • 唯一能保证不发生脑裂的方案就是确保原Active无法访问NFS
      • 通过script修改NFS上的iptables,禁止另一台NN访问
      • 管理员及时介入,恢复原Active,使其成为Standby。恢复iptables

客户端重试机制

代码可在org.apache.hadoop.io.retry.RetryPolicies.FailoverOnNetworkExceptionRetry里找到。目前的客户端在遇到以下Exception时启动重试:

// 连接失败
ConnectException
NoRouteToHostException
UnKnownHostException
// 连到了Standby而不是Active
StandbyException

其重试时间间隔的计算公式为:

RAND(0.5~1.5) * min (2^retryies * baseMillis, maxMillis)

baseMillis = dfs.client.failover.sleep.base.millis,缺省500

maxMillis = dfs.client.failover.sleep.max.millis,缺省15000

最大重试次数:dfs.client.failover.max.attempts,缺省15

未尽事宜

关于那15%失败的情况,我们从日志和代码分析,基本确认是HA里的问题,就是Standby NN在变为Active NN的过程中,会试图重置文件的lease的owner,从而导致LeaseExpiredException: Lease mismatch,客户端遇到这个异常不会重试,导致操作失败。这是一个非常容易重现的问题,相信作者也知道,可能是为了lease安全性也就是数据完整 性做的一个取舍吧:宁可客户端失败千次,不可lease分配错一次,毕竟,客户端失败再重新创建文件是一个很廉价且安全的过程。另外,与 MapReduce 2.0 (YARN)的整合测试我们也没来得及做,原因是我们觉得YARN本身各个组件的HA还不完善,用它来测HDFS的HA有点本末倒置。

来源:http://www.infoq.com/cn/articles/hadoop-2-0-namenode-ha-federation-practice-zh/

使用SBT构建Scala应用

SBT 简介

SBT是Simple Build Tool的简称,如果读者使用过Maven,那么可以简单将 SBT  看做是Scala世界的Maven,虽然二者各有优劣,但完成的工作基本是类似的。虽然  Maven同样可以管理Scala项目的依赖并进行构建,但SBT的某些特性却让人如此着迷

相关信息参见: http://www.scala-sbt.org/release/tutorial/zh-cn/index.html  里面有详细的介绍, 本文是从网络搜集的一些介绍供参考。

比如:

使用Scala作为DSL来定义build文件(one language rules them all  )  ;

通过触发执行(trigger execution) 特性支持持续的编译与测试;

增量编译;^[SBT的增量编译支持因为如此优秀,已经剥离为Zinc,可被Eclipse,Maven,Gradle  等使用  ]
可以混合构建Java和Scala项目;

并行的任务执行;

可以重用Maven或者ivy的repository进行依赖管理;

等等这些,都是SBT得以在Scala的世界里广受欢迎的印记。

SBT的发展可以分为两个阶段,即SBT_0.7.x时代以及SBT_0.10.x以后的时代。

目前来讲,SBT_0.7.x已经很少使用,大部分公司和项目都已经迁移到0.10.x以后的版本上来,最新的是0.12版本。0.10.x之后的版本build定义采用了新的Settings系统,与最初0.7.x版本采用纯Scala代码来定义build文件大相径庭,虽然笔者在迁移之前很抵触(因为0.7.x中采用Scala定义build文件的做法可以体现很好的统一性),但还是升级并接纳了0.10.x以后的版本,并且也逐渐意识到,  虽然新的版本初看起来很复杂,  但一旦了解了其设计和实现的哲学跟思路,就会明白这种设计可以更便捷的定义build  文件。而且可选的build文件方式也同样运行采用Scala代码来定义,即并未放弃统一性的思想,以上是  SBT  的简单介绍.

sbt使用

build.sbt非常简单,它隐藏了sbt实际是如何工作的。sbt构建是用Scala代码定义的。这些代码自身需要建立。还有比sbt更好的方式吗?

project文件夹是你项目中的另一个子项目,它知道如何构建你的项目。子项目(理论上)可以可以做任何其他项目能做的事情。你的构建定义就是一个sbt项目。

如果你喜欢,你可以通过创建一个project/project/文件夹,来调整构建定义项目的构建定义。

这有一个例证。

hello/                  # your project's base directory

Hello.scala         # a source file in your project (could be in
#   src/main/scala too)

build.sbt           # build.sbt is part of the source code for the
#   build definition project inside project/

project/            # base directory of the build definition project

Build.scala     # a source file in the project/ project,
#   that is, a source file in the build definition

build.sbt       # this is part of a build definition for a project
#   in project/project ; build definition's build
#   definition

project/        # base directory of the build definition project
#   for the build definition

Build.scala # source file in the project/project/ project

别担心!多数时候你并不需要全部。但理解原理是很有帮助的。

顺便说一下:任何时候,以.scala或.sbt结尾的文件都会被用上,把他们命名为build.sbt和Build.scala仅仅是惯例而已。这也意味着多个文件也是允许的。

构建定义项目中的.scala源文件

.sbt文件会合并入它们的兄弟项目文件夹。回顾一下项目布局:

hello/                  # your project's base directory

build.sbt           # build.sbt is part of the source code for the
#   build definition project inside project/

project/            # base directory of the build definition project

Build.scala     # a source file in the project/ project,
#   that is, a source file in the build definition

build.sbt中的Scala表达式会编译并与Build.scala(或project/文件夹中的其他.scala文件)合并。

``.sbt`` files in the base directory for a project become part of the ``project`` build definition project also located in that base directory.(绕晕了)
项目根目录下的`.sbt`文件如果成为构建定义项目的一部分,也要放在相应的根目录下(是这个意思吗?)。

.sbt文件格式是将设置添加到构建定义项目的方便的速记法。

关联build.sbt到Build.scala

为了混合你的构建定义中的.sbt和.scala文件,你需要了解如何关联他们。

以下面两个文件举例。首先,如果你的项目在hello文件夹中,如下方式创建hello/project/Build.scala:
(译注:0.12.x略有不同,见http://www.scala-sbt.org/0.12.3/docs/Getting-Started/Full-Def.html#relating-build-sbt-to-build-scala)

import sbt._ import Keys._ object HelloBuild extends Build { val sampleKeyA = settingKey[String]("demo key A") val sampleKeyB = settingKey[String]("demo key B") val sampleKeyC = settingKey[String]("demo key C") val sampleKeyD = settingKey[String]("demo key D") override lazy val settings = super.settings ++ Seq(sampleKeyA := "A: in Build.settings in Build.scala", resolvers := Seq()) lazy val root = Project(id = "hello", base = file("."), settings = Project.defaultSettings ++ Seq(sampleKeyB := "B: in the root project settings in Build.scala")) }

现在,如下方式创建hello/build.sbt:

sampleKeyC in ThisBuild := "C: in build.sbt scoped to ThisBuild" sampleKeyD := "D: in build.sbt"

启动sbt交互命令窗口。输入inspect sampleKeyA,然后你讲看到(除了别的以外):

[info] Setting: java.lang.String = A: in Build.settings in Build.scala
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}/*:sampleKeyA

然后输入inspect sampleKeyC,你将看到:

[info] Setting: java.lang.String = C: in build.sbt scoped to ThisBuild
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}/*:sampleKeyC

需要注意“Provided by”显示了这两个值的相同范围。也就是,.sbt文件中的sampleKeyC in ThisBuild等价于将setting放在.scala文件的Build.settings列表中。sbt从这两个地方获取构建范围的设置来创建构建 定义。

现在,输入inspect sampleKeyB:

[info] Setting: java.lang.String = B: in the root project settings in Build.scala
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}hello/*:sampleKeyB

要注意sampleKeyB是项目范围的({file:/home/hp/checkout/hello/}hello)而不是整个构建({file:/home/hp/checkout/hello/})。

正如你可能已经猜到的,inspect sampleKeyB和sampleKeyD匹配。

[info] Setting: java.lang.String = D: in build.sbt
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}hello/*:sampleKeyD

sbt将.sbt文件的设置追加到Build.settings和Project.settings中的设置,这意味着.sbt中的设置有优先权。 尝试修改Build.scala中的sampleC或sampleD,它们依然是build.sbt中的值。build.sbt中的设置将胜过 Build.scala中的。

另一个你需要注意的:sampleKeyC和sampleKeyD在build.sbt内部有效。这是因为sbt将你的Build对象的内容导入到.sbt文件。在这个例子中build.sbt文件的import HelloBuild._是隐式完成的。

概括起来就是:

在.scala文件中,你可以将设置添加到Build.settings,sbt可以找到它,而且它们自动是构建范围的。
在.scala文件中,你可以将设置添加到Project.settings,sbt可以找到它,而且它们自动是项目范围的。
.scala文件中任何你写的Build对象,它们的内容将导入到.sbt文件并对它有效。
.sbt文件中的设置会被追加到.scala文件的设置。
.sbt文件中的任何设置都是项目范围的,除非你明确指定了另一个范围。

什么时候该使用.scala文件

在.scala文件中,你可以写任何Scala代码,包括值,对象和方法定义。

一种推荐的方式是,在.sbt文件中定义设置,只在实际需要一个值,对象或方法定义的时候使用.scala文件。

有一个构建定义,它是你主项目的嵌套项目。.sbt和.scala文件将一起被编译来创建单一的定义。

在单一构建中定义多个项目时.scala文件也是必需的。更多内容在Multi-Project Builds中讨论。

(在多项目构建中使用.sbt文件的一个缺点是,它们会在散布不同的文件夹中。因此,如果有子项目,很多人更喜欢把设置放在他们的.scala文件中。在你看完多项目构建是如何工作的之后将更加清晰。)

交互模式中的构建定义项目

你可以切换sbt交互命令行,使包含构建定义项目的project/作为当前项目。只需要这么做,输入reload plugins。

> reload plugins
[info] Set current project to default-a0e8e4 (in build file:/home/hp/checkout/hello/project/)
> show sources
[info] ArrayBuffer(/home/hp/checkout/hello/project/Build.scala)
> reload return
[info] Loading project definition from /home/hp/checkout/hello/project
[info] Set current project to hello (in build file:/home/hp/checkout/hello/)
> show sources
[info] ArrayBuffer(/home/hp/checkout/hello/hw.scala)
>

如上所示,您可以使用reload return离开构建定义项目并返回你的常规项目。
提示:所有都是不可变的

如果你认为build.sbt中的设置是被添加到Build和Project对象的settings域,那就错了。相反,Build和 Project,以及build.sbt中的设置,会被串联入另一个不可变的列表,然后作用于sbt。Build和Project对象只是构成完整构建定 义的一部分。

事实上,还有其他来源的设置。它们按这个顺序来追加:

.scala文件中来自Build.settings和Project.settings的设置。
你的用户全局设置;例如,在~/.sbt/build.sbt中你可以定义设置来影响所有的项目。
通过插件注入的设置,见后续使用插件章节。
来自项目.sbt文件的设置。
构建定义项目(也就是项目中的项目)拥有全局插件(~/.sbt/plugins)中添加的设置。使用插件章节有更详细的说明。

后面的设置会覆盖前面的。设置的完整列表构成了构建定义。

Flume日志收集

来源:http://www.blogjava.net/paulwong/archive/2013/10/31/405860.html

一、Flume介绍

Flume是一个分布式、可靠、和高可用的海量日志聚合的系统,支持在系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。

设计目标:

(1) 可靠性

当节点出现故障时,日志能够被传送到其他节点上而不会丢失。Flume提供了三种级别的可靠性保障,从强到弱依次分别为:end-to-end(收到数据agent首先将event写到磁盘上,当数据传送成功后,再删除;如果数据发送失败,可以重新发送。),Store on failure(这也是scribe采用的策略,当数据接收方crash时,将数据写到本地,待恢复后,继续发送),Best effort(数据发送到接收方后,不会进行确认)。

(2) 可扩展性

Flume采用了三层架构,分别为agent,collector和storage,每一层均可以水平扩展。其中,所有agent和collector由master统一管理,这使得系统容易监控和维护,且master允许有多个(使用ZooKeeper进行管理和负载均衡),这就避免了单点故障问题。

(3) 可管理性

所有agent和colletor由master统一管理, 这使得系统便于维护。多master情况,Flume利用ZooKeeper和gossip,保证动态配置数据的一致性。用户可以在master上查看各 个数据源或者数据流执行情况,且可以对各个数据源配置和动态加载。Flume提供了web 和shell script command两种形式对数据流进行管理。

(4) 功能可扩展性

用户可以根据需要添加自己的agent,collector或者storage。此外,Flume自带了很多组件,包括各种agent(file, syslog等),collector和storage(file,HDFS等)。

 

二、Flume架构

flume的逻辑架构:

正如前面提到的,Flume采用了分层架构:分别为agent,collector和storage。其中,agent和collector均由两部分组成:source和sink,source是数据来源,sink是数据去向

Flume使用两个组件:Master和Node,Node根据在Master shell或web中动态配置,决定其是作为Agent还是Collector。

(1) agent

agent的作用是将数据源的数据发送给collector。

Flume自带了很多直接可用的数据源(source),如:

  • text(“filename”):将文件filename作为数据源,按行发送
  • tail(“filename”):探测filename新产生的数据,按行发送出去
  • fsyslogTcp(5140):监听TCP的5140端口,并且接收到的数据发送出去
  • tailDir("dirname"[, fileregex=".*"[, startFromEnd=false[, recurseDepth=0]]]):监听目录中的文件末尾,使用正则去选定需要监听的文件(不包含目录),recurseDepth为递归监听其下子目录的深度

 

1、Flume’s Tiered Event Sources

collectorSource[(port)]Collector source,监听端口汇聚数据
autoCollectorSource通过master协调物理节点自动汇聚数据
logicalSource逻辑source,由master分配端口并监听rpcSink


2、Flume’s Basic Sources

null
console监听用户编辑历史和快捷键输入,只在node_nowatch模式下可用
stdin监听标准输入,只在node_nowatch模式下可用,每行将作为一个event source
rpcSource(port)由rpc框架(thrift/avro)监听tcp端口
text("filename")一次性读取一个文本,每行为一个event
tail("filename"[, startFromEnd=false])每行为一个event。监听文件尾部的追加行,如果startFromEnd为true,tail将从文件尾读取,如果为false,tail将从文件开始读取全部数据
multitail("filename"[, file2 [,file3… ] ])同上,同时监听多个文件的末尾
tailDir("dirname"[, fileregex=".*"[, startFromEnd=false[, recurseDepth=0]]])监听目录中的文件末尾,使用正则去选定需要监听的文件(不包含目录),recurseDepth为递归监听其下子目录的深度
seqfile("filename")监听hdfs的sequencefile,全路径
syslogUdp(port)监听Udp端口
syslogTcp(port)监听Tcp端口
syslogTcp1(port)只监听Tcp端口的一个链接
execPeriodic("cmdline", ms)周期执行指令,监听指令的输出,整个输出都被作为一个event
execStream("cmdline")执行指令,监听指令的输出,输出的每一行被作为一个event
exec("cmdline"[, aggregate=false[,restart=false[,period=0]]])执行指令,监听指令的输出,aggregate如果为true,整个输出作为一个event如果为false,则每行作为一个event。如果restart为true,则按period为周期重新运行
synth(msgCount,msgSize)随即产生字符串event,msgCount为产生数量,msgSize为串长度
synthrndsize(msgCount,minSize,maxSize)同上,minSize – maxSize
nonlsynth(msgCount,msgSize)
asciisynth(msgCount,msgSize)Ascii码字符
twitter("username","pw"[,"url"])尼玛twitter的插件啊
irc("server",port, "nick","chan")
scribe[(+port)]Scribe插件
report[(periodMillis)]生成所有physical node报告为事件源
标签:

同时提供了很多sink,如:

  • console[("format")] :直接将将数据显示在consolr上
  • text(“txtfile”):将数据写到文件txtfile中
  • dfs(“dfsfile”):将数据写到HDFS上的dfsfile文件中
  • syslogTcp(“host”,port):将数据通过TCP传递给host节点
  • agentSink[("machine"[,port])]:等价于agentE2ESink,如果省略,machine参数,默认使用flume.collector.event.host与flume.collector.event.port作为默认collecotr
  • agentDFOSink[("machine" [,port])]:本地热备agent,agent发现collector节点故障后,不断检查collector的存活状态以便重新发送event,在此间产生的数据将缓存到本地磁盘中
  • agentBESink[("machine"[,port])]:不负责的agent,如果collector故障,将不做任何处理,它发送的数据也将被直接丢弃
  • agentE2EChain:指定多个collector提高可用性。 当向主collector发送event失效后,转向第二个collector发送,当所有的collector失败后,它会非常执着的再来一遍

1、Flume’s Collector Tier Event Sinks

collectorSink( "fsdir","fsfileprefix",rollmillis)collectorSink,数据通过collector汇聚之后发送到hdfs, fsdir 是hdfs目录,fsfileprefix为文件前缀码

 

2、Flume’s Agent Tier Event Sinks

agentSink[("machine"[,port])]Defaults to agentE2ESink,如果省略,machine参数,默认使用flume.collector.event.host与flume.collector.event.port作为默认collecotr(以下同此)
agentE2ESink[("machine"[,port])]执着的agent,如果agent发送event没有收到collector成功写入的状态码,该event将被agent重复发送,直到接到成功写入的状态码
agentDFOSink[("machine" [,port])]本地热备agent,agent发现collector节点故障后,不断检查collector的存活状态以便重新发送event,在此间产生的数据将缓存到本地磁盘中
agentBESink[("machine"[,port])]不负责的agent,如果collector故障,将不做任何处理,它发送的数据也将被直接丢弃
agentE2EChain("m1[:_p1_]" [,"m2[:_p2_]"[,…]])指定多个collector提高可用性。 当向主collector发送event失效后,转向第二个collector发送,当所有的collector失败后,它会非常执着的再来一遍...
agentDFOChain("m1[:_p1_]"[, "m2[:_p2_]"[,…]])同上,当向所有的collector发送事件失效后,他会将event缓存到本地磁盘,并检查collector状态,尝试重新发送
agentBEChain("m1[:_p1_]"[, "m2[:_p2_]"[,…]])同上,当向所有的collector发送事件失效后,他会将event丢弃
autoE2EChain无需指定collector, 由master协调管理event的流向
autoDFOChain同上
autoBEChain同上

 

3、Flume’s Logical Sinks

logicalSink("logicalnode")

 

4、Flume’s Basic Sinks

在不使用collector收集event的情况下,可将source直接发向basic sinks

nullnull
console[("formatter")]转发到控制台
text("txtfile" [,"formatter"])转发到文本文件
seqfile("filename")转发到seqfile
dfs("hdfspath")转发到hdfs
customdfs("hdfspath"[, "format"])自定义格式dfs
+escapedCustomDfs("hdfspath", "file", "format")
rpcSink("host"[, port])Rpc框架
syslogTcp("host"[,port])发向网络地址
irc("host",port, "nick", "chan")

(2) collector

collector的作用是将多个agent的数据汇总后,加载到storage中。

它的source和sink与agent类似。

数据源(source),如:

  • collectorSource[(port)]:Collector source,监听端口汇聚数据
  • autoCollectorSource:通过master协调物理节点自动汇聚数据
  • logicalSource:逻辑source,由master分配端口并监听rpcSink

sink,如:

  • collectorSink( "fsdir","fsfileprefix",rollmillis):collectorSink,数据通过collector汇聚之后发送到hdfs, fsdir 是hdfs目录,fsfileprefix为文件前缀码
  • customdfs("hdfspath"[, "format"]):自定义格式dfs

(3) storage

storage是存储系统,可以是一个普通file,也可以是HDFS,HIVE,HBase,分布式存储等。

(4) Master

Master是管理协调agent和collector的配置等信息,是flume集群的控制器。

 

在Flume中,最重要的抽象是data flow(数据流),data flow描述了数据从产生,传输、处理并最终写入目标的一条路径。

  1. 对于agent数据流配置就是从哪得到数据,把数据发送到哪个collector。
  2. 对于collector是接收agent发过来的数据,把数据发送到指定的目标机器上。

注:Flume框架对hadoop和zookeeper的依赖只是在jar包上,并不要求flume启动时必须将hadoop和zookeeper服务也启动。

 

三、Flume分布式环境部署

1.实验场景

  • 操作系统版本:RedHat 5.6
  • Hadoop版本:0.20.2
  • Jdk版本:jdk1.6.0_26
  • 安装flume版本:flume-distribution-0.9.4-bin

部署flume在集群上,按照如下步骤:

  1. 在集群上的每台机器上安装flume
  2. 选择一个或多个节点当做master
  3. 修改静态配置文件
  4. 在至少一台机器上启动一个master ,所有节点启动flume node
  5. 动态配置

需要在集群的每台机器上部署Flume。

注意:flume集群整个集群的网络环境要保证稳定,可靠,否则会出现一些莫名错误(比如:agent端发送不了数据到collector)。

1.Flume环境安装

$wget http://cloud.github.com/downloads/cloudera/flume/flume-distribution-0.9.4-bin.tar.gz $tar -xzvf flume-distribution-0.9.4-bin.tar.gz $cp -rf flume-distribution-0.9.4-bin /usr/local/flume $vi /etc/profile  #添加环境配置     export FLUME_HOME=/usr/local/flume     export PATH=.:$PATH::$FLUME_HOME/bin $source /etc/profile  $flume #验证安装

 

2.选择一个或多个节点当做master

对于master的选择情况,可以在集群上定义一个master,也可以为了提高可用性选择多个节点做为master。

  • 单点master模式:容易管理,但在系统的容错和扩展性有缺陷
  • 多点master模式:通常是运行3/5个master,能很好的容错

Flume master数量的选择原则

分布式的master能够继续正常工作不会崩溃的前提是正常工作的master数量超过总master数量的一半。

Flume master 的作用主要有两个:

  • 跟踪各节点的配置情况,通知节点配置的改变;
  • 跟踪来自flow的结尾操控在可靠模式下(E2E)的信息,以至于让flow的源头知道什么时候停止传输event。

3.修改静态配置文件

site- specific设置对于flume节点和master通过在每一个集群节点的conf/flume-site.xml是可配置的,如果这个文件不存在, 设置的属性默认的在conf/flume­-conf.xml中,在接下来的例子中,在flume的节点上设置master名,让节点自己去寻找叫 “master”的flume Master。

<?xml version="1.0"?>     <?xml-stylesheet type="text/xsl"  href="configuration.xsl"?>     <configuration>         <property>             <name>flume.master.servers</name>             <value>master</value>          </property>     </configuration>

在多master的情况下需要如下配置:

<property>     <name>flume.master.servers</name>    <value>hadoopmaster.com,hadoopedge.com,datanode4.com</value>     <description>A comma-separated list of hostnames, one for each machine in the Flume Master.</description> </property> <property>     <name>flume.master.store</name>     <value>zookeeper</value>     <description>How the Flume Master stores node configurations. Must be either 'zookeeper' or 'memory'.</description> </property> <property>     <name>flume.master.serverid</name>     <value>2</value>     <description>The unique identifier for a machine in a Flume Master ensemble. Must be different on every master instance.</description> </property>

注意:flume.master.serverid 属性的配置主要是针对master,集群上Master节点的flume.master.serverid 必须是不能相同的,该属性的值以0开始。

当使用agent角色时,你可以通过添加下面的配置文件在flume-conf.xml中,来设置默认的collector主机:

<property>     <name>flume.collector.event.host</name>     <value>collector</value>     <description>This is the host name of the default "remote"  collector.</description> </property> <property>     <name>flume.collector.port</name>     <value>35853</value>     <description>This default tcp port that the collector listens to in order to receive events it is collecting.</description> </property>

Flume配置文件(flume-site.conf)

1、 watchdog

watchdog.restarts.maxwatchdog每分钟重启的最大数???

 

 

2、 common node

flume.config.heartbeat.periodnode发送心跳周期,默认5000(毫秒)
flume.node.status.portnode web端口
flume.node.heartbeat.backoff.ceilingnode向master发送心跳之后等待反馈的最大时长,默认60000(毫秒)
flume.node.http.autofindport如果已有node启动,允许第二个node自动选择一个未使用的端口做web服务。多个node的界面端口从35862、35863向后延续

 

 

3、agent

flume.agent.logdiragent日志路径
flume.agent.logdir.maxage当前处于打开状态agent日志文件收集信息的时长,在这之后该日志文件将会被关闭,并将数据发送到网络,默认10000(毫秒)
flume.agent.logdir.retransmit在end-to-end模式下agent向collector发送数据失败后再次发送的间隔时长,默认60000(毫秒),建议至少是flume.collector.roll.millis的两倍
flume.agent.failover.backoff.initial当primary sink(可以认为是第一collector)故障后,重启primary sink的一个延迟时间,在此期间,agent将把数据发送到secondary sink(可能是第二collector)
flume.agent.failover.backoff.max在一定时限内尝试链接故障节点失败后,agent将把数据转发向备用节点

 

 

4、collector

flume.collector.event.host默认collector地址
flume.collector.port默认collector端口
flume.collector.dfs.dir最终数据发向目录(默认),可以是本地,可以是hdfs,默认是/tmp
flume.collector.dfs.compress.codec压缩格式GzipCodec, DefaultCodec (deflate), BZip2Codec,默认是None
flume.collector.roll.millishdfs文件切换(关闭后新建)的时长
flume.collector.output.formatcollector发送数据格式avro, avrojson(默认), avrodata…

 

 

5、master

flume.master.servers用逗号分隔多个master地址列表
flume.master.storemaster配置存储方式(zookeeper/memory) zookeeper保证master的配置在多master节点之间同步,memory则保存在内存中,其配置随着master宕机而丢失
flume.master.serveridmaster的唯一标识
flume.master.http.porthttp端口
flume.master.heartbeat.missed.max判断节点失效的最大未达心跳数
flume.master.savefile当前flume配置文件的路径,默认conf/current.flume
flume.master.savefile.autoload启动时是否加载current.flume,默认false
flume.master.gossip.periodmaster通信周期(毫秒)
flume.master.heartbeat.rpcTHRIFT/AVRO
flume.event.rpcTHRIFT/AVRO
flume.report.server.rpc.typeTHRIFT/AVRO

 

 

6、zookeeper

flume.master.zk.logdirzookeeper日志路径

 

 

7、thrift

flume.thrift.socket.timeout.msthrift网络连接超时时间(毫秒)

 

4.启动集群

集群上节点启动:

  1. 在命令行输入:flume master 启动master节点
  2. 在命令行输入:flume node –n nodeName 启动其他节点,nodeName最好根据集群逻辑的划分来取名子,这样在 master进行配置的时候比较清晰。

名字规则自己定义,方便记忆和动态配置即可(后续会有介绍动态配置)

 

 5.基于flume shell的动态配置

关于flume shell 中的command参见

command shell(flume command)

help帮助
connect master:port登录master
config logicalnode source sink为逻辑节点配置一个source到sink的映射
getnodestatus获得节点状态(HELLO, CONFIGURING, ACTIVE, IDLE, ERROR, DECOMMISSIONED, LOST )

HELLO, node启动时

CONFIGURING, node被配置后

ACTIVE, 一个event从source送达到sink

IDLE, source中所有evnet发送完毕后

ERROR, 节点故障退出,数据没有flush

DECOMMISSIONED, node被master移除

LOST, master长时间未收到node心跳

getconfigs获得配置
getmappings [physical node]如果physical node参数被省略,将显示所有logical node到physical node的映射关系
exec同步执行命令
Source file执行脚本.
submit异步执行命令
wait ms [cmdid]设定一个时间,周期检查命令进程的状态(success or failure)
waitForNodesActive ms node1 [node2 […]]设定一个时间,检查node是否处于使用(configuring, active)状态
waitForNodesDone ms node1 [node2 […]]设定一个时间,检查node是否处于未用(IDLE, ERROR, LOST)状态
quit退出

 

 

command shell(exec & submit command)

双引号包含转义字符的java string
单引号能引住除单引号之外的所有字符
nooptouch master, 不做操作
config logicalnode source sink为逻辑节点配置source到sink的映射
multiconfig flumespec
unconfig logicalnode取消逻辑节点的配置,影响master调整故障转移列表(failover list)
refreshAll logicalnode刷新
save filename保存current configuration到master硬盘
load filename从master中加载current configuration
map physicalnode logicalnode配置物理节点到逻辑节点的映射关系,master的配置将被同步到logicalnode
spawn physicalnode logicalnode恢复
decommission logicalnode
unmap physicalnode logicalnode取消映射
unmapAll全部取消
purge logicalnode清除状态,类似重启一个logical node, 适用于(DECOMMISSIONED、 LOST)状态
purgeAll清除所有logical node的状态
 假设我们目前部署的Flume集群结构如下:

我们想将A-F所在的机器的系统日志收集到HDFS中,怎么样在flume shell中配置达到我们的目的呢?

1. 设置逻辑节点(logical node)

复制代码
$flume shell >connect localhost >help >exec map 192.168.0.1 agentA >exec map 192.168.0.2 agentB >exec map 192.168.0.3 agentC >exec map 192.168.0.4 agentD >exec map 192.168.0.5 agentE >exec map 192.168.0.6 agentF >getnodestatus         192.168.0.1 --> IDLE         192.168.0.2 --> IDLE         192.168.0.3 --> IDLE         192.168.0.4 --> IDLE         192.168.0.5 --> IDLE         192.168.0.6 --> IDLE         agentA --> IDLE         agentB --> IDLE         agentC --> IDLE         agentD --> IDLE         agentE --> IDLE         agentF --> IDLE >exec map 192.168.0.11 collector

这里你也可以打开web master界面查看。

2.启动Collector的监听端口

>exec config collector 'collectorSource(35853)' 'collectorSink("","")'#collector节点监听35853端口过来的数据,这一部非常重要

登陆到collector服务器进行端口检测

$netstat -nalp|grep 35853

如果在master中未进行上述配置,在collector上检测不到此打开端口

3.设置各节点的source和sink

>exec config collector 'collectorSource(35853)' 'collectorSink("hdfs://namenode/flume/","syslog")'  >exec config agentA 'tail("/tmp/log/message")' 'agentBESink("192.168.0.11")' #经过实验,好像一个逻辑节点,最多只能有一个source和sink.
>...
>exec config agentF 'tail("/tmp/log/message")' 'agentBESink("192.168.0.11")'

这时的配置情况可从master web中一目了然,此时已经可以达到我们最初的目的了。

以上通过flume shell进行的动态配置,在flume master web中都可以进行,在此不做进一步说明。

 

 四、高级动态配置

高级配置其实就是在上述简单配置中增加了以下几个特性来保证系统更好的运行:

  • 多Master(Master节点的高可用)
  • Collector Chain(Collector的高可用)

多Master的情况在上面已经有过介绍,包括用途和master个数等。下面来简单看一下Collector Chain,其实也很简单,就是在动态配置时,使用agent*Chain来指定多个Collector来保证其日志传输的可用性。看一下一般正式环境中flume的逻辑图:

这里agentA和agentB指向collectorA,如果CollectorA crach了,根据配置的可靠性级别agent会有相应的动作,我们很可能为了保障高效传输而没有选择E2E(即使是这种方式,Agent本地日志累积过多依然是一个问题),一般会配置多个Collector,形成collector chain。

>exec config agentC 'tail("/tmp/log/message")' 'agentE2EChain("collectorB:35853","collectorA:35853")' >exec config agentD 'tail("/tmp/log/message")' 'agentE2EChain("collectorB:35853","collectorC:35853")'

这样collectorB在出问题时:

 

五、问题和总结

上述节点有如下几类:master、agent、collector、storage,针对每类节点我们看一下高可用和有没有可能引起性能瓶颈问题。

首先,storage层的失败collector层的失败是一样的,只要数据放不到最终的位置,就认为节点是失败的。我们一定会根据收集数据的可靠性设定合适的传输模式,而且会根据我们的配置,自己控制collector接收数据的情况,collector的性能影响的是整个flume集群的数据吞吐量,所以collector最好单独部署,所以一般不用考虑高可用问题。

然后,agent层的失败,Flume数据安全级别的配置主要Agent的配置上,Agent提供三种级别发送数据到collector:E2E、DFO、BF,在些不赘述。看一下一位大牛的总结:

agent节点监控日志文件夹下的所有文件,每一个agent最多监听1024个文件,每一个文件在agent的都会有一个类似游标的东西,记录监听文件读取的位置,这样每次文件有新的记录产生,那么游标就会读取增量记录,根据agent配置发送到collector的安全层级属性有E2E,DFO。
如果是E2E的情况那么agent节点会首先把文件写入到agent节点的文件夹下,然后发送给collector,如果最终数据最终成功存储到storage层,那么agent删除之前写入的文件,如果没有收到成功的信息,那么就保留信息。 如果agent节点出现问题,那么相当于所有的记录信息都消失了,如果直接重新启动,agent会认为日志文件夹下的所有文件都是没有监听过的,没有文件记录的标示,所以会重新读取文件,这样,日志就会有重复,具体恢复办法如下       将agent节点上监听的日志文件夹下已经发送的日志文件移出,处理完,故障重新启动agent即可。 注:在agent节点失败的情况下,按照失败的时间点,将时间点之前的数据文件移出,将flume.agent.logdir配置的文件夹清空,重新启动agent。

最后,master失败,master宕机,整个集群将不能工作,在重新启动集群,将agent监听的日志文件夹下的所有文件移出,然后重新启动master即可。在多master节点情况下,只要集群上正常工作的master大于总master数量的一半,集群就能正常工作,那么只要恢复其中宕机的master即可。

 

问题总结:

1.Flume在agent端采集数据的时候默认会在/tmp/flume-{user}下生成临时的目录用于存放agent自己截取的日志文件,如果文件过大导致磁盘写满那么agent端会报出    Error closing logicalNode a2-18 sink: No space left on device,所以在配置agent端的时候需要注意   <property>     <name>flume.agent.logdir</name>     <value>/data/tmp/flume-${user.name}/agent</value>   </property> 属性,只要保证flume在7*24小时运行过程agent端不会使该路径flume.agent.logdir磁盘写满即可。
复制代码
2. Flume在启动时候会去寻找hadoop-core-*.jar的文件,需要修改标准版的hadoop核心jar包的名字 将hadoop-*-core.jar改成hadoop-core-*.jar。
3.Flume集群中的flume必须版本一致。否则会出现莫名其妙的错误。
4.Flume集群收集的日志发送到hdfs上建立文件夹的时间依据是根据event的时间,在源代码上是Clock.unixTime(),所以如果想要根据日志生成的时间来生成文件的话,需要对 com.cloudera.flume.core.EventImpl 类的构造函数 public EventImpl(byte[] s, long timestamp, Priority pri, long nanoTime,       String host, Map<String, byte[]> fields)重新写,解析数组s的内容取出时间,赋给timestamp。
注意:flume的框架会构造s内容是空的数组,用来发送类似简单验证的event,所以需要注意s内容为空的时候timestamp的问题。
5.如果collector和agent不在一个网段的话会发生闪断的现象,这样的话,就会造成agent端不能传送数据个collector所以,在部署agent和collector最好在一个网段。
6.如果在启动master时出现:“试着启动hostname,但是hostname不在master列表里的错误“,这是需要检查是否主机地址和hostname配置的正确与否。
7.在源端,有一个比较大的缺陷,在tail类的source,不支持,断点续传功能。因为重启node后没有记录上次文件读到的位置,从而没办法知道,下次再读时,从什么地方开始读。 特别是在日志文件一直在增加的时候。flume的source node挂了。等flume的source再次开启的这段时间内,增加的日志内容,就没办法被source读取到了。 不过flume有一个execStream的扩展,可以自己写一个监控日志增加情况,把增加的日志,通过自己写的工具把增加的内容,传送给flume的node。再传送给sink的node。

 

以前文章中介绍过Scribe方案,给我的最直观感受就是:

  • scribe安装复杂,配置简单
  • flume安装简单,动态配置复杂

下面董的博客中的一副对比图:

几个在线画流程图的工具

目前我们使用的流程图制作软件大体有RFFLOW、FLOW CHARTING、VISIO三种,可是它们的体积和资源占用情况很大,操作复杂,有没有简单易用不需安装的流程图制作软件呢?下面我给大家推荐几款在线流程图制作工具。

第一款:Gliffy

Gliffy支 持中文,其功能满足你正常的需要,有很多流程图实体供你选择。作为一种在线工具,它继承了协同的概念,你可以和你的同事共同编辑,完成流程图的制作;如果 你需要在博客调用,它也可以使你的博客上的图表与其同步更新;如果你需要在VISIO、或矢量制图软件(如Freehand等)中再加工,可以直接导入。

很可爱的主页面哦。。

第2款:mxgraph

下载

非常的简单、轻巧 ,当然 功能也比较少,只能话流程图。

当然后面听说还来了一个新版的,功能有所加强。。。不过地址也换了一个 新版地址入口

第3款:DrawAnywhere

猛击进去

这个我是亲身用过的 ,并且现在有的时候 我还是会用。而且有流程图、网络图等等。

免费的,你只需要注册一下就OK了 30天的试用期。不过,如果你不想付款的话,那么你可以随便注册,你只要随便填一个邮箱就可以了。很方便哦。

重点推荐。。。

第4款:FunnyDraw

打开

中文的。。。没用过。你们可以进去体会体会。

第5款:在线ps

打开

在线PS,没搞错吧?这个真的木有。。。不信你打开来看看,其实这个应该不算这里的,可是忍不住就发过来。。。别见怪哈。

很酷吧。等你哪天要ps的时候,就不用在安装庞大的 那个软件了啦。

 

就这么几个吧。够用就行。。

作者:Lanny☆兰东才
出处:http://www.cnblogs.com/damonlan