什么是protocol buffer呢?
protobuf是Google开发的一种数据描述语言语言,能够将结构化的数据序列化,可用于数据存储,通信协议等方面,官方版本支持C++,Java,Python,社区版本支持更多语言。

比如说程序中生成了一个链表,但是程序退出重启后,还要重新生成链表,有时候,我们很需要上次程序中该链表中记录的数据。这些数据或许是经过很多大量运算生成的,每次都重新生成这些数据的话,需要消耗大量时间。这时候就可以考虑使用protobuf,将其序列化后保存在文件中,下次使用的时候,加载文件,反序列化后就可以直接使用了。

那我们为什么要用protocol buffer呢?

您可以创建一种特别的方法来将数据项编码为单个字符串,例如编码4 ints为“12:3:- 23:67”。这是一种简单而灵活的方法,尽管它确实需要编写一次性的编码和解析代码,而且解析带来了一个小的运行时成本。这对于编码非常简单的数据非常有效。
将数据序列化为XML。这种方法非常有吸引力,因为XML是人类可读的,而且有许多语言的绑定库。如果您想要与其他应用程序/项目共享数据,这将是一个很好的选择。然而,XML是出了名的空间密集型,编码/解码可以对应用程序施加巨大的性能惩罚。而且,导航XML DOM树要比在类中导航简单的字段要复杂得多。
protocol buffer 是解决这个问题的灵活、高效、自动化的解决方案。使用protocol buffer,您可以编写一个.proto文件来描述您希望存储的数据结构。这样,protocol buffer 编译器创建了一个类,该类使用高效的二进制格式实现对protocol buffer 数据的自动编码和解析。生成的类为组成protocol buffer 的字段提供了getter和setter,并负责将protocol buffer 的读取和写入的细节作为一个单元来处理。重要的是,protocol buffer 格式支持将格式扩展到时间的思想,这样代码仍然可以读取用旧格式编码的数据。

值得注意的是,protobuf是以二进制来存储数据的。相对于JSON和XML具有以下优点:

简洁

体积小:消息大小只需要XML的1/10 ~ 1/3

速度快:解析速度比XML快20 ~ 100倍

使用Protobuf的编译器,可以生成更容易在编程中使用的数据访问代码

更好的兼容性,Protobuf设计的一个原则就是要能够很好的支持向下或向上兼容

我们简单来看下用Java如何使用protocol buffer

  • 创建一个.proto的文件
  • 使用protocol buffer
  • 运用java的protocol buffer的api进行读写信息

举个例子
创建一个通讯录的应用,先创建一个.proto文件

package protobuf;

option java_package = "com.edianedi.myprotobuf.protobuf";
option java_outer_classname = "AddressBookProtos";
message Person {
    required string name = 1;
    required int32 id = 2;
    optional string email = 3;

    enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
    }

    message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
    }

    repeated PhoneNumber phone = 4;
}

message AddressBook {
    repeated Person person = 1;
}

让我们浏览一下文件的每个部分,看看它做了什么
.proto文件从一个包声明开始,这有助于防止不同项目之间的命名冲突。在Java中,包名被用作Java包,除非您明确指定了一个java_package,就像我们在这里所做的那样。即使您提供了一个java_package,您仍然应该定义一个正常包,以避免在protocol buffer名称空间和非java语言中发生名称冲突。

在包声明之后,您可以看到两个特定于java的选项:java_package和java_outer_classname。java_package指定您所生成的类应该在哪个Java包中使用。如果您没有显式地指定它,它只匹配包声明给出的包名称,但是这些名称通常不是适当的Java包名(因为它们通常不以域名开头)。java_outer_classname选项定义应该包含该文件中的所有类的类名。如果您不显式地给出java_outer_classname,它将通过将文件名转换为驼峰来生成。例如,“my_proto.proto“在默认情况下,使用“MyProto”作为外部类名。

接下来,有您的消息定义。消息只是包含一组类型字段的聚合。许多标准的简单数据类型可用作字段类型,包括bool、int32、float、double和string。您还可以通过使用其他消息类型作为字段类型添加到消息的进一步结构——在上面的示例中,Person消息包含PhoneNumber消息,而AddressBook消息包含Person消息。您甚至可以定义在其他消息内嵌套的消息类型——正如您所看到的,PhoneNumber类型是在Person内部定义的。您还可以定义enum类型,如果您想要您的字段之一具有预定义的值列表——在这里您可以指定一个电话号码可以是移动、HOME或WORK。

在每个元素上的“= 1”,“= 2”标记标识了在二进制编码中字段使用的唯一“标记”。标记数字1 - 15需要比更高的数字编码更少的字节,因此,作为一种优化,您可以决定使用这些标记来使用常用的或重复的元素,为不常用的可选元素留下16和更高的标签。重复字段中的每个元素都需要重新编码标签号,因此重复字段特别适合进行这种优化。

每个字段必须使用下列修饰符之一进行注释:

需要:必须提供字段的值,否则该消息将被视为“未初始化”。尝试构建未初始化的消息将抛出RuntimeException。解析未初始化的消息将抛出IOException。除此之外,需要的字段与可选字段完全相同。
可选:字段可能设置或不设置。如果一个可选字段值没有设置,则使用默认值。对于简单类型,您可以指定您自己的默认值,就像我们在示例中为电话号码类型所做的那样。否则,将使用系统默认值:数字类型为零,字符串为空字符串,为bools为false。对于嵌入式消息,默认值始终是消息的“默认实例”或“原型”,它没有设置字段。调用访问器以获取可选(或需要的)字段的值,该字段没有显式设置,总是返回该字段的默认值。
repeated:该字段可以重复多次(包括零)。重复值的顺序将保留在protocol buffer中。可以将重复字段看作是动态大小的数组。

编译你的protocol buffer

#运用以下命令来编译你的proto文件
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto

protocol buffer API
让我们看看一些生成的代码,看看编译器为您创建了哪些类和方法。如果你看一下AddressBookProtos.java,您可以看到它定义了一个名为AddressBookProtos的类,它是在addressbook.proto中指定的每个消息的类。每个类都有自己的构建器类,用于创建该类的实例。您可以在下面的构建器和消息部分找到更多关于构建器的信息。

消息和构建器都为消息的每个字段自动生成访问器方法;信息只有getter,而建设者有getter和setter。这里是Person类的一些访问器(为了简洁而省略):

// required string name = 1;
public boolean hasName();
public String getName();

// required int32 id = 2;
public boolean hasId();
public int getId();

// optional string email = 3;
public boolean hasEmail();
public String getEmail();

// repeated .tutorial.Person.PhoneNumber phones = 4;
public List<PhoneNumber> getPhonesList();
public int getPhonesCount();
public PhoneNumber getPhones(int index);

与此同时,Person.Builder也有有相同的getter和setter:

// required string name = 1;
public boolean hasName();
public java.lang.String getName();
public Builder setName(String value);
public Builder clearName();

// required int32 id = 2;
public boolean hasId();
public int getId();
public Builder setId(int value);
public Builder clearId();

// optional string email = 3;
public boolean hasEmail();
public String getEmail();
public Builder setEmail(String value);
public Builder clearEmail();

// repeated .tutorial.Person.PhoneNumber phones = 4;
public List<PhoneNumber> getPhonesList();
public int getPhonesCount();
public PhoneNumber getPhones(int index);
public Builder setPhones(int index, PhoneNumber value);
public Builder addPhones(PhoneNumber value);
public Builder addAllPhones(Iterable<PhoneNumber> value);
public Builder clearPhones();

如您所见,每个字段都有简单的javabean风格的getter和setter。每个字段都有getter,如果该字段被设置,则返回true。最后,每个字段都有一个清除字段返回其空状态的清除方法。

Repeated字段有一些额外的方法——统计方法(也就是缩写列表的大小),getter和setter方法获取或设置一个特定的元素列表的索引,一个方法添加一个新元素添加到列表,以及一个addAll方法将整个容器的元素添加到列表中。

注意,这些访问器方法如何使用camel - case命名,尽管。使用lowercase-with-underscores原型文件。这个转换是由protocol buffer编译器自动完成的,因此生成的类符合标准Java风格的约定。您应该始终使用小写- with -下划线来表示字段名。原型文件;这确保了所有生成的语言都具有良好的命名实践。更多关于优秀的风格指南。典型的风格。

枚举和嵌套类

经常遇到一个问题就是windows中默认的是GBK,而linux是UTF-8。需要对文件进行转换

查看文件编码

file filename
#如 file a.sh

文件编码转换

  1. enconv 转换文件编码,比如要将一个GBK编码的文件转换成UTF-8编码,操作如下
enconv -L zh_CN -x UTF-8 filename
#这个我经常用
  1. iconv 转换,iconv的命令格式如下:
  2. -f encoding -t encoding inputfile

比如将一个UTF-8 编码的文件转换成GBK编码
iconv -f GBK -t UTF-8 file1 -o file2

  1. 在Vim中直接进行转换文件编码,比如将一个文件转换成utf-8格式
    Shell代码
set fileencoding=utf-8  

今天遇到一个问题protobuf之后的java文件太大 导致idea报了File size exceeds configured limit.而且其他java文件显示的都是c 这个文件显示的为j 而且界面上一值带着.java后缀。

原因:

IDEA对能关联的文件大小做了限制,主要是为了保护内存,默认值为2500kb,对于一般的java文件也够用了,只是这里我用protocbuf生成的java文件过大,达到4.2M多 ClientNamenodeProtocolProtos.java

到idea的bin 目录下的idea.properties文件
将idea.max.intellisense.filesize=2500 默认的2500kb改成6000kb

    idea.max.intellisense.filesize=6000

1.安装maven,使用的版本是3.3.9,下载apache-maven-3.3.9-bin.tar,解压:
tar -zxvf apache-maven-3.3.9-bin.tar
进入 Maven 安装根目录 cd apache-maven-3.3.9
进入 Maven配置文件目录 cd conf
编辑settings.xml文件 vi settings.xml
设置Maven本地库的路径:这个路径要创建好。
同时在加入阿里云的Maven库

<mirror>
          <id>nexus-aliyun</id>
          <mirrorOf>*</mirrorOf>
          <name>Nexus aliyun</name>
          <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

设置Maven环境变量:

export MAVEN_HOME=路径
export PATH=${PATH}:${MAVEN_HOME}/bin:
export MAVEN_OPTS="-Xmx2g -XX:MaxPermSize=512M -XX:ReservedCodeCacheSize=512m"
export PATH

更新环境变量 source ~/.bash_profile
mvn -version 能看到相应的版本,则Maven安装成功。

2.安装protobuf,下载protobuf-2.5.0.tar.gz,下载地址: http://pan.baidu.com/s/1pJlZubT
解压:tar -zxvf protobuf-2.5.0.tar.gz
cd protobuf-2.5.0
依次执行

1 ./configure
2 make
3 make check
4 make install

最后输入 protoc --version命令,如显示libprotoc 2.5.0则安装成功
3、编译Hadoop源码
在Hadoop官网下载hadoop-2.6.0-src.tar.gz源码
解压:tar -zxvf hadoop-2.6.0-src.tar.gz

mvn package -Pdist -DskipTests -Dtar

最后输出:[INFO] Reactor Summary:

[INFO] Apache Hadoop Pipes ................................ SUCCESS [  0.040 s]
[INFO] Apache Hadoop OpenStack support .................... SUCCESS [  4.994 s]
[INFO] Apache Hadoop Amazon Web Services support .......... SUCCESS [ 11.060 s]
[INFO] Apache Hadoop Client ............................... SUCCESS [  9.013 s]
[INFO] Apache Hadoop Mini-Cluster ......................... SUCCESS [  0.132 s]
[INFO] Apache Hadoop Scheduler Load Simulator ............. SUCCESS [  4.940 s]
[INFO] Apache Hadoop Tools Dist ........................... SUCCESS [  9.764 s]
[INFO] Apache Hadoop Tools ................................ SUCCESS [  0.025 s]
[INFO] Apache Hadoop Distribution ......................... SUCCESS [ 30.464 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11:29 min
[INFO] Finished at: 2017-09-08T22:45:03+08:00
[INFO] Final Memory: 187M/1529M

  1. 简介

    Yarn是Hadoop集群的资源管理系统
        1.ResourceManager负责整个集群资源管理和调度
        2.ApplicationMaster负责应用程序相关事务,比如任务调度、任务监控和容错等

    这种新的架构设计使得多种应用可以运行在hadoop上面,如hive、storm、spark、pig等 Yarn在系统层面统一资源管理。有了Yarn,各种应用就可以互不干扰的运行在同一个Hadoop系统中,共享整个集群资源,如下图所示:


2.Yarn组件和架构

    1. ResourceManager:Global(全局)的进程 
    2. NodeManager:运行在每个节点上的进程
    3. ApplicationMaster:Application-specific(应用级别)的进程
    - *Scheduler:是ResourceManager的一个组件*
    - *Container:节点上一组CPU和内存资源*

Container是Yarn对计算机计算资源的抽象,它其实就是一组CPU和内存资源,所有的应用都会运行在Container中。ApplicationMaster是对运行在Yarn中某个应用的抽象,它其实就是某个类型应用的实例,ApplicationMaster是应用级别的,它的主要功能就是向ResourceManager(全局的)申请计算资源(Containers)并且和NodeManager交互来执行和监控具体的task。Scheduler是ResourceManager专门进行资源管理的一个组件,负责分配NodeManager上的Container资源,NodeManager也会不断发送自己Container使用情况给ResourceManager。
ResourceManager和NodeManager两个进程主要负责系统管理方面的任务。ResourceManager有一个Scheduler,负责各个集群中应用的资源分配。对于每种类型的每个应用,都会对应一个ApplicationMaster实例,ApplicationMaster通过和ResourceManager沟通获得Container资源来运行具体的job,并跟踪这个job的运行状态、监控运行进度。

3.Yarn组件详解
3.1 Container

Container是Yarn框架的计算单元,是具体执行应用task(如map task、reduce task)的基本单位。Container和集群节点的关系是:一个节点会运行多个Container,但一个Container不会跨节点。

一个Container就是一组分配的系统资源,现阶段只包含两种系统资源(之后可能会增加磁盘、网络等资源):

  1. CPU core
  2. Memory in MB

既然一个Container指的是具体节点上的计算资源,这就意味着Container中必定含有计算资源的位置信息:计算资源位于哪个机架的哪台机器上。所以我们在请求某个Container时,其实是向某台机器发起的请求,请求的是这台机器上的CPU和内存资源。

任何一个job或application必须运行在一个或多个Container中,在Yarn框架中,ResourceManager只负责告诉ApplicationMaster哪些Containers可以用,ApplicationMaster还需要去找NodeManager请求分配具体的Container。
3.2 Node Manager

NodeManager进程运行在集群中的节点上,每个节点都会有自己的NodeManager。NodeManager是一个slave服务:它负责接收ResourceManager的资源分配请求,分配具体的Container给应用。同时,它还负责监控并报告Container使用信息给ResourceManager。通过和ResourceManager配合,NodeManager负责整个Hadoop集群中的资源分配工作。ResourceManager是一个全局的进程,而NodeManager只是每个节点上的进程,管理这个节点上的资源分配和监控运行节点的健康状态。下面是NodeManager的具体任务列表:

  • 接收ResourceManager的请求,分配Container给应用的某个任务
  • 和ResourceManager交换信息以确保整个集群平稳运行。ResourceManager就是通过收集每个NodeManager的报告信息来追踪整个集群健康状态的,而NodeManager负责监控自身的健康状态。
  • 管理每个Container的生命周期
  • 管理每个节点上的日志
  • 执行Yarn上面应用的一些额外的服务,比如MapReduce的shuffle过程

当一个节点启动时,它会向ResourceManager进行注册并告知ResourceManager自己有多少资源可用。在运行期,通过NodeManager和ResourceManager协同工作,这些信息会不断被更新并保障整个集群发挥出最佳状态。

NodeManager只负责管理自身的Container,它并不知道运行在它上面应用的信息。负责管理应用信息的组件是ApplicationMaster,在后面会讲到。
3.3 Resource Manager

ResourceManager主要有两个组件:Scheduler和ApplicationManager。

Scheduler是一个资源调度器,它主要负责协调集群中各个应用的资源分配,保障整个集群的运行效率。Scheduler的角色是一个纯调度器,它只负责调度Containers,不会关心应用程序监控及其运行状态等信息。同样,它也不能重启因应用失败或者硬件错误而运行失败的任务

Scheduler是一个可插拔的插件,它可以调度集群中的各种队列、应用等。在Hadoop的MapReduce框架中主要有两种Scheduler:Capacity Scheduler和Fair Scheduler,关于这两个调度器后面会详细介绍。

另一个组件ApplicationManager主要负责接收job的提交请求,为应用分配第一个Container来运行ApplicationMaster,还有就是负责监控ApplicationMaster,在遇到失败时重启ApplicationMaster运行的Container。
3.4 Application Master

ApplicationMaster的主要作用是向ResourceManager申请资源并和NodeManager协同工作来运行应用的各个任务然后跟踪它们状态及监控各个任务的执行,遇到失败的任务还负责重启它。

在MR1中,JobTracker即负责job的监控,又负责系统资源的分配。而在MR2中,资源的调度分配由ResourceManager专门进行管理,而每个job或应用的管理、监控交由相应的分布在集群中的ApplicationMaster,如果某个ApplicationMaster失败,ResourceManager还可以重启它,这大大提高了集群的拓展性。

在MR1中,Hadoop架构只支持MapReduce类型的job,所以它不是一个通用的框架,因为Hadoop的JobTracker和TaskTracker组件都是专门针对MapReduce开发的,它们之间是深度耦合的。Yarn的出现解决了这个问题,关于Job或应用的管理都是由ApplicationMaster进程负责的,Yarn允许我们自己开发ApplicationMaster,我们可以为自己的应用开发自己的ApplicationMaster。这样每一个类型的应用都会对应一个ApplicationMaster,一个ApplicationMaster其实就是一个类库。这里要区分ApplicationMaster类库和ApplicationMaster实例,一个ApplicationMaster类库何以对应多个实例,就行java语言中的类和类的实例关系一样。总结来说就是,每种类型的应用都会对应着一个ApplicationMaster,每个类型的应用都可以启动多个ApplicationMaster实例。所以,在yarn中,是每个job都会对应一个ApplicationMaster而不是每类。

Yarn 框架相对于老的 MapReduce 框架什么优势呢?

  • 这个设计大大减小了 ResourceManager 的资源消耗,并且让监测每一个 Job 子任务 (tasks) 状态的程序分布式化了,更安全、更优美。
  • 在新的 Yarn 中,ApplicationMaster 是一个可变更的部分,用户可以对不同的编程模型写自己的 AppMst,让更多类型的编程模型能够跑在 Hadoop 集群中,可以参考 hadoop Yarn 官方配置模板中的 mapred-site.xml 配置。
  • 对于资源的表示以内存为单位 ( 在目前版本的 Yarn 中,没有考虑 cpu 的占用 ),比之前以剩余 slot 数目更合理。
  • 老的框架中,JobTracker 一个很大的负担就是监控 job 下的 tasks 的运行状况,现在,这个部分就扔给 ApplicationMaster 做了,而 ResourceManager 中有一个模块叫做 ApplicationsManager,它是监测 ApplicationMaster 的运行状况,如果出问题,会将其在其他机器上重启。
  • Container 是 Yarn 为了将来作资源隔离而提出的一个框架。这一点应该借鉴了 Mesos 的工作,目前是一个框架,仅仅提供 java 虚拟机内存的隔离 ,hadoop 团队的设计思路应该后续能支持更多的资源调度和控制 , 既然资源表示成内存量,那就没有了之前的 map slot/reduce slot 分开造成集群资源闲置的尴尬情况。

4.Yarn request分析
4.1 应用提交过程分析

了解了上面介绍的这些概念,我们有必要看一下Application在Yarn中的执行过程,整个执行过程可以总结为三步:

  1. 应用程序提交
  2. 启动应用的ApplicationMaster实例
  3. ApplicationMaster实例管理应用程序的执行

下面这幅图展示了应用程序的整个执行过程

客户端程序向ResourceManager提交应用并请求一个ApplicationMaster实例

ResourceManager找到可以运行一个Container的NodeManager,并在这个Container中启动ApplicationMaster实例

ApplicationMaster向ResourceManager进行注册,注册之后客户端就可以查询ResourceManager获得自己ApplicationMaster的详细信息,以后就可以和自己的ApplicationMaster直接交互了

在平常的操作过程中,ApplicationMaster根据resource-request协议向ResourceManager发送resource-request请求

当Container被成功分配之后,ApplicationMaster通过向NodeManager发送container-launch-specification信息来启动Container, container-launch-specification信息包含了能够让Container和ApplicationMaster交流所需要的资料

应用程序的代码在启动的Container中运行,并把运行的进度、状态等信息通过application-specific协议发送给ApplicationMaster

在应用程序运行期间,提交应用的客户端主动和ApplicationMaster交流获得应用的运行状态、进度更新等信息,交流的协议也是application-specific协议

一但应用程序执行完成并且所有相关工作也已经完成,ApplicationMaster向ResourceManager取消注册然后关闭,用到所有的Container也归还给系统

4.2 Resource Request和Container
Yarn的设计目标就是允许我们的各种应用以共享、安全、多租户的形式使用整个集群。并且,为了保证集群资源调度和数据访问的高效性,Yarn还必须能够感知整个集群拓扑结构。为了实现这些目标,ResourceManager的调度器Scheduler为应用程序的资源请求定义了一些灵活的协议,通过它就可以对运行在集群中的各个应用做更好的调度,因此,这就诞生了Resource Request和Container。

具体来讲,一个应用先向ApplicationMaster发送一个满足自己需求的资源请求,然后ApplicationMaster把这个资源请求以resource-request的形式发送给ResourceManager的Scheduler,Scheduler再在这个原始的resource-request中返回分配到的资源描述Container。每个ResourceRequest可看做一个可序列化Java对象,包含的字段信息如下:

<resource-name, priority, resource-requirement, number-of-containers>

resource-name:资源名称,现阶段指的是资源所在的host和rack,后期可能还会支持虚拟机或者更复杂的网络结构
priority:资源的优先级
resource-requirement:资源的具体需求,现阶段指内存和cpu需求的数量
number-of-containers:满足需求的Container的集合

number-of-containers中的Containers就是ResourceManager给ApplicationMaster分配资源的结果。Container就是授权给应用程序可以使用某个节点机器上CPU和内存的数量。

ApplicationMaster在得到这些Containers后,还需要与分配Container所在机器上的NodeManager交互来启动Container并运行相关任务。当然Container的分配是需要认证的,以防止ApplicationMaster自己去请求集群资源。