快学Big Data

来源:互联网 发布:单片机最小系统是什么 编辑:程序博客网 时间:2024/05/29 19:21





学习态度:

求知若饥, 虚心若愚

珍惜小的概率,维护链接的范围。

当你的才华撑不起你的野心时,请静下心来好好的读书。

杂谈:

1-1)大数据研发型人才

了解大数据技术人才  --->  能做大数据部署的人才  -->  大数据运维人才-->

大数据开发人才( MR )  -- >  大数据开发( spark + MR ) -->  大数据分析师-- >

大数据挖掘工程师( 算法,数学)

1-2)大数据架构行人才

大数据架构师 --> 大数据数据定义工程师-->  大数据数据编排工程师

 

1-3)大数据科学家

大数据研发型人才  +  大数据架构行人才  +  业务专家

 

 

1-4)计算分析应用

计算密集型应用:机器学习,数据挖掘

IO密集型应用:索引,检索,统计,聚类,数据的解码与解压缩

 

Linux总结

概述:

Linux是一套免费使用和自由传播的类Unix操作系统(主要用在服务器上),接下来详细的介绍一下linux的一些知识。

 

 

Linux 网络配置

 

 

 

 

 

 

 

 

 

 

 

 

 

Linux 常用命令:

Pwd  /  ls / cp / mv / scp / ssh-keygen / ssh-copy-id / setup / vi / date / clock -w / wget / yum /

 

Reboot / Shutdown / clean / echo / mkdir / rm -rf / cat /more /tar / gzip  / who / which / find / histroy /Grep / chmod / useradd / tail -f / chmod / server / top / hostname / ifconfig / netstat  -ntlp / chkconfig  / rpm /make && make install /ping / sh / cut / awk / sed / crotable ........

 

 

Linux 编程

1-1)基本语法

#!/bin/bash

echo "hello world"

 

1-2)常用语法

if语法

if condition 
then 
    statements 
[elif condition 
    then statements. ..] 
[else 
    statements ] 
fi

 

实例:

#!/bin/bash

read -p "please input your name:" NAME

#printf '%s\n' $NAME

if [ $NAME = root ]

        then

                echo "hello ${NAME},  welcome !"

        elif [ $NAME = **** ]

                then

                        echo "hello ${NAME},  welcome !"

        else

                echo "SB, get out here !"

Fi

 

while语法

实例一:

while expression

do

command

done

 

实例二:

i=1

while ((i<=3))

do

  echo $i

  let i++

done

 

case语法

case $1 in

start)

echo "starting"

;;

stop)

echo "stoping"

;;

*)

echo "Usage: {start|stop}"

esac

 

 

for语法

方式一:

 

for N in 1 2 3

do

echo $N

done

for N in 1 2 3; do echo $N; done

for N in {1..3}; do echo $N; done

 

 

方式二:

for ((i = 0; i <= 5; i++))

do

echo "welcome $i times"

done

for ((i = 0; i <= 5; i++)); do echo "welcome $i times"; done

 

 

函数

1-1)函数定义

# func1.sh

hello()    ## 函数定义

{

        echo "Hello there today's date is `date +%Y-%m-%d`"

        # return  2      ###返回值其实是状态码,只能在[0-255]范围内

}       

echo "now going to the function hello"

hello

# echo $?  获取函数的return

echo "back from the function"

 

 

函数调用:

function hello()  

function hello

hello

 

 

1-2)函数参数

#!/bin/bash

# fun1.sh

 

funWithParam(){

    echo "第一个参数为 $1 !"

    echo "第二个参数为 $2 !"

    echo "第十个参数为 $10 !"

    echo "第十个参数为 ${10} !"

    echo "第十一个参数为 ${11} !"

    echo "参数总数有 $#!"

    echo "作为一个字符串输出所有参数 $* !"

}

funWithParam 1 2 3 4 5 6 7 8 9 34 73

 

 

注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

 

 

1-3)函数返回值

#!/bin/bash

# fun2.sh

funWithReturn(){

    echo "这个函数会对输入的两个数字进行相加运算..."

    echo "输入第一个数字: "

    read aNum

    echo "输入第二个数字: "

    read anotherNum

    echo "两个数字分别为 $aNum$anotherNum !"

    return $(($aNum+$anotherNum))

}

funWithReturn

echo "输入的两个数字之和为 $? !"

 

 

1-4)跨脚本调用

 

存在 /root/fun2.sh

可在fun_other.sh中调用

 

#!/bin/bash

# fun_other.sh

. /root/fun2.sh    ## 注:  . / 之间有空格

# 或者 source /root/fun2.sh

funWithParam 11 22 33 44 55 66 77 88 99 100 101

 

准备三台机器以便今后使用:

以此在以下机器上执行命令:

1-1)修改主机名字

 

[root@hadoop1 /]# vi /etc/hosts

Hadoop1

1-2)添加其他的机器配置

[root@hadoop1 /]# vi /etc/hosts

192.168.215.314  hadoop1

192.168.215.135  hadoop2

192.168.215.136  hadoop3

 

1-3)生成秘钥

[root@hadoop1 /]# cd /root/

[root@hadoop1 ~]# ssh-keygen

 

1-4)传送秘钥

[root@hadoop1 /]# ssh-copy-id hadoop2

[root@hadoop1 /]# ssh-copy-id hadoop3

 

1-5)修改时间与日期

[root@hadoop1 /]# date 2016/07/10

[root@hadoop1 /]# date 18:20:00

[root@hadoop1 /]# clock -w

 

 

安装常用软件:

1-1JDK安装

[root@hadoop1 nginx]# tar -zxvf jdk-7u80-linux-x64.tar.gz -C /usr/local/

[root@hadoop1 /]# vi /etc/profile

export JAVA_HOME=/home/jdk1.7

 

1-2)安装tomcat

[root@hadoop3 local]# tar -zxvf apache-tomcat-7.0.69.tar.gz

[root@hadoop3 /]# vi /etc/profile

export  TOMCAT_HOME=/usr/local/apache-tomcat-7.0.69

 

1-3) 安装nginx

[root@hadoop1 nginx]# tar -zxvf nginx-1.8.1.tar.gz -C /usr/local/

export  NGINX_HOME=/usr/local/nginx/sbin

 

1-4)让环境变量生效

source /etc/profile

 

1-5)安装Mysql

[root@hadoop1 /]# service mysqld stop

[root@hadoop1 /]# yum remove mysql mysql-*

[root@hadoop1 /]# yum list installed | grep mysql

[root@hadoop1 /]# rum remove mysqllibs

[root@hadoop1/]# rpm -Uvh http://repo.mysql.com/mysql-community-release-el6-5.noarch.rpm

[root@hadoop1 /]# yum install mysql-community-server

[root@hadoop1 /]# mysql -V

[root@hadoop1 /]# service mysqld start

[root@hadoop1 /]# mysql -uroot -p

 

 

开启mysql的远程登录权限

mysql>GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'%' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;

mysql>FLUSH PRIVILEGES;

 

关闭防火墙

在其他的机器上关闭防火墙

 

[root@hadoop1 /]# service iptables stop

[root@hadoop1 /]# chkconfig iptables off


JVM  总结

概述:

     需要监控服务器上的JAVA或者其他的进程的执行情况,因不能在服务器上直接操作因而需要软件来链接进行监控,并进行优化。下面简单的介绍一下过程。

 

环境:

服务器  Cenos 7

JDK: 1.7

客户端:win  7

 

工具:

1- 1 ) Jconsole   

   

 

 

 

 

 

 

1-2 ) jvisualvm  

 

 

 

 

 

 

 

 

可以看到详细的信息:

 

 

在这里查看详细线程的信息

 

 

内存模型图解

Java程序在运行时会在内存中获取一定大小的内存的大小,虚拟机再把这些区域分成不同的数据区,这些区域管理者不同的功能,有的管理者创建的时间,销毁的时间,有的区域随着虚拟机的创建而存在,还有的区域伴随着用户的而操作而一直执行者,并把这些区域称作为java的区域。

 

1-1) 内存的模拟图

 

 

 

 

 

如上图所示,Java虚拟机运行时数据区域被分为五个区域:

方法区(Method Area):用于存储类结构信息的地方,包括常量池、静态变量、构造函数等。虽然JVM规范把方法区描述为堆的一个逻辑部分,它有个别名non-heap(非堆)。方法区还包含一个运行时常量池。

 

l java(Heap):存储java实例或者对象的地方。这块是GC的主要区域(后面解释)。从存储的内容我们可以很容易知道,方法区和堆是被所有java线程共享的

 

l java(Stack)java栈总是和线程关联在一起,每当创建一个线程时,JVM就会为这个线程创建一个对应的java栈。在这个java栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,用于存储局部变量表、操作栈、方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程。所以java栈是线程私有的。

 

程序计数器(PC Register):用于保存当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方,程序计数器也是线程私有的。 

 

本地方法栈(Native Method Stack):和java栈的作用差不多,只不过是为JVM使用到的native方法服务的。

 

 

 

 

 

1-2

    对于大多数的JAVA程序来说Java Heap实在JAVA虚拟机上管理内存中运行最大的一块

该区域伴随着虚拟机的启动而存在,java  heep可以处理上不连续的内存空间,只要是逻辑上是连续的即可,实际中我们创建的对象与数组都是放在堆里面。

 

    新生代、老生代、永久代的概念就是在堆里面。

 

 

1-3栈(Stack

相对于Java Heap来讲,JavaStack是线程私有的,她的生命周期与线程相同。Java Stack描述的是Java方法执行时的内存模型每个方法执行时都会创建一个栈帧Stack Frame)用语存储局部变量表操作数栈动态链接方法出口等信息。

 

 

 

在上图中可以看出每一个线程在执行时,都意味着有一个栈帧在当前线程中出栈与入栈

 

 

GC算法

1-1) 标记清除算法(Mark-Sweep

1、标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象

2、在标记完成后统一回收所有被标记的对象

 

缺点:标记清除之后会产生大量不连续的内存碎片

空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

 

1-2)复制算法(Copying

1、将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。

2、当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

 

优点:不会产生内存碎片等

缺点:可用内存缩小为了原来的一半。

复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低

 

1-3)标记-整理算法(Mark-Compact

1、标记

2、让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

 

此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。

 

 

垃圾回收器

1-1)分代收集方法论

1、概述

分代收集,是当前商业虚拟机常用的垃圾收集算法。

分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。

 

具体来说?

java程序运行的过程中,会产生大量的对象,因每个对象所能承担的职责不同所具有的功能不同所以也有着不一样的生命周期,有的对象生命周期较长,比如Http请求中的Session对象,线程,Socket连接等;有的对象生命周期较短,比如String对象,由于其不变类的特性,有的在使用一次后即可回收。

 

试想,如果不进行对象存活时间的区分,每次垃圾回收都是对整个堆空间进行回收,那么消耗的时间相对会很长,而且对于存活时间较长的对象进行的扫描工作等都是徒劳。

因此就需要引入分治的思想,所谓分治的思想就是因地制宜,将对象进行代的划分,把不同生命周期的对象放在不同的代上使用不同的垃圾回收方式。

 

如何划分?

将对象按其生命周期的不同划分成:年轻代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation)。其中持久代主要存放的是类信息,所以与java对象的回收关系不大,与回收息息相关的是年轻代年老代

 

 

1-2)分代收集详述

 年轻代<复制算法>:是所有新对象产生的地方。

年轻代被分为3个部分——Enden区和两个Survivor区(Fromto

Eden区被对象填满时,就会执行Minor GC,并把所有存活下来的对象转移到其中一个survivor区(from区或to区)。这样在一段时间内,总会有一个空的survivor区。经过多次GC周期后,仍然存活下来的对象会被转移到年老代内存空间。通常这是在年轻代有资格提升到年老代前通过设定年龄阈值来完成的。

需要注意,Survivor的两个区是对称的,没先后关系,fromto是相对的。

 

年老代<标记-整理(Mark-Sweep>:年轻代中经历了N次回收后仍没被清除的对象

可以说他们都是久经沙场而不亡的一代,都是生命周期较长的对象。对于年老代和永久代,就不能再采用像年轻代中那样搬移腾挪的回收算法。

在老年代内存被占满时通常会触发Full GC,回收整个堆内存。

 

永久代:用于存放静态文件,比如java类、方法等。

持久代对垃圾回收没有显著的影响。  

 

分代回收的效果图如下:

 

 

 

 

 

 

 

1-3)垃圾收集器概览

 

1-4)Serial收集器

1、是一个单线程的收集器,“Stop The World

2、对于运行在Client模式下的虚拟机来说是一个很好的选择

4、简单而高效

 

1-5)ParNew收集器

1Serial串行收集器的多线程版本

2、单CPU不如Serial

3Server模式下新生代首选,目前只有它能与CMS收集器配合工作

4、使用-XX+UseConcMarkSweepGC选项后的默认新生代收集器,也可以使用-XX+UseParNewGC选项来强制指定它。

5-XXParallelGCThreads:限制垃圾收集的线程数。

 

 

 

1-6)Parallel Scavenge收集器

新生代收集器,复制算法,并行的多线程收集器

目标是达到一个可控制的吞吐量(Throughput)。

吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%

两个参数用于精确控制吞吐量:

-XXMaxGCPauseMillis是控制最大垃圾收集停顿时间

-XXGCTimeRatio直接设置吞吐量大小

-XX+UseAdaptiveSizePolicy:动态设置新生代大小、EdenSurvivor区的比例、晋升老年代对象年龄

并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。

并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。

 

1-7)Serial Old收集器

1Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法

2、主要意义也是在于给Client模式下的虚拟机使用。

3、如果在Server模式下,那么它主要还有两大用途:

    一种用途是在JDK 1.5以及之前的版本中与Parallel Scavenge收集器搭配使用[1]

    另一种用途就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

 

 

 

1-8)Parallel Old收集器

1Parallel Scavenge并行收集器的老年代版本,使用多线程和“标记-整理”算法

2、在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel ScavengeParallel Old收集器。

 

 

1-9)CMS(并发GC)收集器

CMS收集器一款优秀的收集器

以获取最短回收停顿时间为目标的收集器。

非常符合互联网站或者B/S系统的服务端上,重视服务的响应速度,希望系统停顿时间最短的应用

基于“标记—清除”算法实现的

CMS收集器的内存回收过程是与用户线程一起并发执行的

 

它的运作过程分为4个步骤,包括:

①初始标记,“Stop The World”,只是标记一下GC Roots能直接关联到的对象,速度很快

②并发标记,并发标记阶段就是进行GC RootsTracing的过程

.重新标记,Stop The World”,是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,但远比并发标记的时间短

.并发清除(CMS concurrent sweep

 

优缺点:

优点——并发收集、低停顿

缺点——对CPU资源非常敏感。

           无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。

       

 

 

1-10)G1(Garbage-First)收集器

1、当今收集器技术发展的最前沿成果之一

2G1是一款面向服务端应用的垃圾收集器。

3、优点:

        并行与并发:充分利用多CPU、多核环境下的硬件优势

        分代收集:不需要其他收集器配合就能独立管理整个GC

        空间整合:“标记—整理”算法实现的收集器,局部上基于“复制”算法不会产生内存空间碎片           

        可预测的停顿:能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒

4G1收集器的运作大致可划分为以下几个步骤:

        初始标记:标记一下GC Roots能直接关联到的对象,需要停顿线程,但耗时很短

        并发标记:是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行

        最终标记:修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录

        筛选回收:对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划

 

 

垃圾收集器参数总结

-XX:+<option> 启用选项

-XX:-<option> 不启用选项

-XX:<option>=<number> 

-XX:<option>=<string>

 

参数

描述

-XX:+UseSerialGC

Jvm运行在Client模式下的默认值,打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收

-XX:+UseParNewGC

打开此开关后,使用ParNew + Serial Old的收集器进行垃圾回收

-XX:+UseConcMarkSweepGC

使用ParNew + CMS +  Serial Old的收集器组合进行内存回收,Serial Old作为CMS出现“Concurrent Mode Failure”失败后的后备收集器使用。

-XX:+UseParallelGC

Jvm运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge +  Serial Old的收集器组合进行回收

-XX:+UseParallelOldGC

使用Parallel Scavenge +  Parallel Old的收集器组合进行回收

-XX:SurvivorRatio

新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Subrvivor = 8:1

-XX:PretenureSizeThreshold

直接晋升到老年代对象的大小,设置这个参数后,大于这个参数的对象将直接在老年代分配

-XX:MaxTenuringThreshold

晋升到老年代的对象年龄,每次Minor GC之后,年龄就加1,当超过这个参数的值时进入老年代

-XX:UseAdaptiveSizePolicy

动态调整java堆中各个区域的大小以及进入老年代的年龄

-XX:+HandlePromotionFailure

是否允许新生代收集担保,进行一次minor gc,另一块Survivor空间不足时,将直接会在老年代中保留

-XX:ParallelGCThreads

设置并行GC进行内存回收的线程数

-XX:GCTimeRatio

GC时间占总时间的比列,默认值为99,即允许1%GC时间,仅在使用Parallel Scavenge 收集器时有效

-XX:MaxGCPauseMillis

设置GC的最大停顿时间,在Parallel Scavenge收集器下有效

-XX:CMSInitiatingOccupancyFraction

设置CMS收集器在老年代空间被使用多少后出发垃圾收集,默认值为68%,仅在CMS收集器时有效,-XX:CMSInitiatingOccupancyFraction=70

-XX:+UseCMSCompactAtFullCollection

由于CMS收集器会产生碎片,此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在CMS收集器时有效

-XX:+CMSFullGCBeforeCompaction

设置CMS收集器在进行若干次垃圾收集后再进行一次内存碎片整理过程,通常与UseCMSCompactAtFullCollection参数一起使用

-XX:+UseFastAccessorMethods

原始类型优化

-XX:+DisableExplicitGC

是否关闭手动System.gc

-XX:+CMSParallelRemarkEnabled

降低标记停顿

-XX:LargePageSizeInBytes

内存页的大小不可设置过大,会影响Perm的大小,-XX:LargePageSizeInBytes=128m

 

ClientServer模式默认GC

 

新生代GC方式

老年代和持久代GC方式

Client

Serial 串行GC

Serial Old 串行GC

Server

Parallel Scavenge  并行回收GC

Parallel Old 并行GC

 

Sun/oracle JDK GC组合方式

 

新生代GC方式

老年代和持久代GC方式

-XX:+UseSerialGC

Serial 串行GC

Serial Old 串行GC

-XX:+UseParallelGC

Parallel Scavenge  并行回收GC

Serial Old  并行GC

-XX:+UseConcMarkSweepGC

ParNew 并行GC

CMS 并发GC 
当出现“Concurrent Mode Failure”
采用Serial Old 串行GC

-XX:+UseParNewGC

ParNew 并行GC

Serial Old 串行GC

-XX:+UseParallelOldGC

Parallel Scavenge  并行回收GC

Parallel Old 并行GC

-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC

Serial 串行GC

CMS 并发GC 
当出现“Concurrent Mode Failure”
采用Serial Old 串行GC

 

 

 

 

 

 

 

收集器设置:

-XX:+UseSerialGC:年轻串行(Serial),老年串行(Serial Old

-XX:+UseParNewGC:年轻并行(ParNew),老年串行(Serial Old

-XX:+UseConcMarkSweepGC:年轻并行(ParNew),老年串行(CMS),备份(Serial Old

-XX:+UseParallelGC:年轻并行吞吐(Parallel Scavenge),老年串行(Serial Old

-XX:+UseParalledlOldGC:年轻并行吞吐(Parallel Scavenge),老年并行吞吐(Parallel Old

收集器参数:

 

-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。

-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间

-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。

-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

 

 

JVM参数列表

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m  -XX:MaxTenuringThreshold=0

-Xmx3550m:最大堆内存为3550M

-Xms3550m:初始堆内存为3550m

此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

-Xmn2g:设置年轻代大小为2G

整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8

-Xss128k:设置每个线程的堆栈大小。

JDK5.0以后每个线程堆栈大小为1M,在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为14,年轻代占整个堆栈的1/5

-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。

设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

-XX:MaxPermSize=16m:设置持久代大小为16m

-XX:MaxTenuringThreshold=15:设置垃圾最大年龄。

如果设置为0的话,则年轻代对象不经过Survivor区,直 接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象 再年轻代的存活时间,增加在年轻代即被回收的概论。

 

收集器设置

-XX:+UseSerialGC:设置串行收集器

-XX:+UseParallelGC:设置并行收集器

-XX:+UseParalledlOldGC:设置并行年老代收集器

-XX:+UseConcMarkSweepGC:设置并发收集器

垃圾回收统计信息

-XX:+PrintGC

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-Xloggc:filename

并行收集器设置

-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。

-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间

-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

并发收集器设置

-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。

-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

Zookeeper 总结

 

 

学习是一种浮躁的事情,要静下心来慢慢的品味。    -- 小徐

概述:

zookeeper 主要负责管理机器的正常运行,如果一台机器突然死掉,利用zookeeper的机制可以快速的启动另一台备份的机器,zookeeper在这一方面做出了杰出的贡献,地从实现的算法是fast paxos baxic paxos算法,当zookeeper失去太多的leader或者太多的follower时会进入回复的状态,进行选举。

角色:

Zookeeper 有两种角色分别是leaderfollower ,在选举时机器超过一般即可存活,一般的机器配置成单个数

 

特性:

1、每台机器上保存的数据一致,用户不管访问那台机器获取的数据信息都是一致的

2、分布式的读写,更细请求转发都是由leader转发的

3、数据更新原子性,一次数据的更新要不成功要不失败

4、每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识

 

选举机制:

那么,初始化的时候,是按照上述的说明进行选举的,但是当zookeeper运行了一段时间之后,有机器down掉,重新选举时,选举过程就相对复杂了。

需要加入数据versionleader id和逻辑时钟。

数据version:数据新的version就大,数据每次更新都会更新version

Leader id:就是我们配置的myid中的值,每个机器一个。

逻辑时钟:这个值从0开始递增,每次选举对应一个值,也就是说:  如果在同一次选举中,那么这个值应该是一致的;  逻辑时钟值越大,说明这一次选举leader的进程更新.

选举的标准就变成:

1、逻辑时钟小的选举结果被忽略,重新投票

2、统一逻辑时钟后,数据id大的胜出

3、数据id相同的情况下,leader id大的胜出

根据这个规则选出leader

 

 

Zookeeper 的部署

官网:http://zookeeper.apache.org/

Zk链接工具:https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip

 

1 -1 ) 安装

Cd   /home

Tar  -zxvf  zookeeper-3.4.5.tar.zg

Cd   zookeeper-3.4.5

Cd   conf/

Cp  zoo_sample.cfg   zoo.cfg

 

1-2 ) 配置环境变量

Vi  /etc/profile

export ZOOKEEPER_NOME=/home/zookeeper/zookeeper-3.4.5

 

1-3 ) 修改配置文件

Vi   zoo.cfg

如果是多台的话,就加入一下配置:

#autopurge.purgeInterval=1

server.1=hadoop1:2888:3888

server.2=hadoop2:2888:3888

server.3=hadoop3:2888:3888

 

并在:

dataDir=/home/zookeeper/zookeeper-3.4.5/conf

 

hadoop1/hadoop2/hadoop3上分别创建myid的文件

Echo   “1” > /home/zookeeper/zookeeper-3.4.5/conf/myid

Echo   “2” > /home/zookeeper/zookeeper-3.4.5/conf/myid

Echo   “3” > /home/zookeeper/zookeeper-3.4.5/conf/myid

 

 

把资料传送到hadoop2/hadoop3的而机器上

 

 

1-4 ) 启动zookeeper

  hadoop1/hadoop2/hadoop3 机器上分别启动 zkServer.sh start

1-5 ) 查看进程状态

 Jps  -->  查看一下进程信息

 zkServer.sh status --> 查看集群状态,主从信息

 hadoop1/hadoop2/hadoop3 机器上分别查看角色:

  

 必有一台:leader

 其他的:follower

 

[root@hadoop2 bin]# jps

28658 Jps

26957 QuorumPeerMain

 

 

[root@hadoop3 bin]# ./zkServer.sh  status

ZooKeeper JMX enabled by default

Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg

Mode: follower

 

[root@hadoop2 bin]# ./zkServer.sh status

ZooKeeper JMX enabled by default

Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg

Mode: leader

 

[root@hadoop1 bin]# ./zkServer.sh status

ZooKeeper JMX enabled by default

Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg

Mode: follower

 

1-6) 节点类型

1、znode 有两种类型

短暂(ephemeral)(断开连接自己删除)

持久(persistent)(断开连接不删除)

2Znode有四种形式的目录节点(默认是persistent

PERSISTENT

PERSISTENT_SEQUENTIAL(持久序列/test0000000019

EPHEMERAL

EPHEMERAL_SEQUENTIAL

 

1-7 ) zookerper 的客户端连接

运行 zkCli.sh server <ip>进入命令行工具

 

 

 

 

Zookeeper 的客户端的API的使用

1 -1 ) 基本方法

功能

描述

create

在本地目录树中创建一个节点

delete

删除一个节点

exists

测试本地是否存在目标节点

get/set data

从目标节点上读取 / 写数据

get/set ACL

获取 / 设置目标节点访问控制列表信息

get children

检索一个子节点上的列表

sync

等待要被传送的数据

 

 

 

 

 

 

 

 

 

 

 

 

 

1-2 ) 增删改查znode数据

先把需要的JAR导入

 

public class SimpleDemo {

// 会话超时时间,设置为与系统默认时间一致

private static final int SESSION_TIMEOUT = 30000;

// 创建 ZooKeeper 实例

ZooKeeper zk;

// 创建 Watcher 实例

Watcher wh = new Watcher() {

public void process(org.apache.zookeeper.WatchedEvent event) {

System.out.println(event.toString());

}

};

 

// 初始化 ZooKeeper 实例

private void createZKInstance()throws IOException {

zk = new ZooKeeper("weekend01:2181", SimpleDemo.SESSION_TIMEOUT,

this.wh);

}

 

private void ZKOperations()throws IOException, InterruptedException,

KeeperException {

System.out

.println("/n1. 创建 ZooKeeper 节点 (znode : zoo2, 数据: myData2 ,权限: OPEN_ACL_UNSAFE ,节点类型: Persistent");

zk.create("/zoo2","myData2".getBytes(),Ids.OPEN_ACL_UNSAFE,

CreateMode.PERSISTENT);

System.out.println("/n2. 查看是否创建成功: ");

System.out.println(new String(zk.getData("/zoo2",false,null)));

System.out.println("/n3. 修改节点数据 ");

zk.setData("/zoo2","shenlan211314".getBytes(), -1);

System.out.println("/n4. 查看是否修改成功: ");

System.out.println(new String(zk.getData("/zoo2",false,null)));

System.out.println("/n5. 删除节点 ");

zk.delete("/zoo2", -1);

System.out.println("/n6. 查看节点是否被删除: ");

System.out.println(" 节点状态: [" +zk.exists("/zoo2",false) + "]");

}

 

private void ZKClose()throws InterruptedException {

zk.close();

}

 

public static void main(String[] args)throws IOException,

InterruptedException, KeeperException {

SimpleDemo dm = new SimpleDemo();

dm.createZKInstance();

dm.ZKOperations();

dm.ZKClose();

}

}

 

 

1-3)服务器上下线动态感知

 

需求描述

某分布式系统中,主节点可以有多台,可以动态上下线

任意一台客户端都能实时感知到主节点服务器的上下线

 

设计思路:

 

 

 

客户端代码实现:

public class AppClient {

private String groupNode = "sgroup";

private ZooKeeper zk;

private Stat stat =new Stat();

private volatile List<String>serverList;

 

/**

 * 连接zookeeper

 */

public void connectZookeeper()throws Exception {

zk 

= new ZooKeeper("localhost:4180,localhost:4181,localhost:4182", 5000,new Watcher() {

public void process(WatchedEvent event) {

// 如果发生了"/sgroup"节点下的子节点变化事件, 更新server列表, 并重新注册监听

if (event.getType() ==EventType.NodeChildrenChanged

&& ("/" + groupNode).equals(event.getPath())) {

try {

updateServerList();

} catch (Exception e) {

e.printStackTrace();

}

}

}

});

 

updateServerList();

}

 

/**

 * 更新server列表

 */

private void updateServerList()throws Exception {

List<String> newServerList = new ArrayList<String>();

 

// 获取并监听groupNode的子节点变化

// watch参数为true, 表示监听子节点变化事件.

// 每次都需要重新注册监听, 因为一次注册, 只能监听一次事件, 如果还想继续保持监听, 必须重新注册

List<String> subList = zk.getChildren("/" +groupNode,true);

for (String subNode : subList) {

// 获取每个子节点下关联的server地址

byte[] data = zk.getData("/" +groupNode +"/" + subNode, false,stat);

newServerList.add(new String(data,"utf-8"));

}

 

// 替换server列表

serverList = newServerList;

 

System.out.println("server list updated: " +serverList);

}

 

/**

 * client的工作逻辑写在这个方法中

 * 此处不做任何处理, 只让client sleep

 */

public void handle()throws InterruptedException {

Thread.sleep(Long.MAX_VALUE);

}

 

public static void main(String[] args)throws Exception {

AppClient ac = new AppClient();

ac.connectZookeeper();

 

ac.handle();

}

}

 

 

服务器端实现:

public class AppServer {

private String groupNode = "sgroup";

private String subNode = "sub";

 

/**

 * 连接zookeeper

 * @param address server的地址

 */

public void connectZookeeper(String address)throws Exception {

ZooKeeper zk = new ZooKeeper(

"localhost:4180,localhost:4181,localhost:4182",

5000, new Watcher() {

public void process(WatchedEvent event) {

// 不做处理

}

});

// 在"/sgroup"下创建子节点

// 子节点的类型设置为EPHEMERAL_SEQUENTIAL, 表明这是一个临时节点, 且在子节点的名称后面加上一串数字后缀

// 将server的地址数据关联到新创建的子节点上

String createdPath = zk.create("/" + groupNode + "/" + subNode, address.getBytes("utf-8"),

Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

System.out.println("create: " + createdPath);

}

/**

 * server的工作逻辑写在这个方法中

 * 此处不做任何处理, 只让server sleep

 */

public void handle()throws InterruptedException {

Thread.sleep(Long.MAX_VALUE);

}

public static void main(String[] args)throws Exception {

// 在参数中指定server的地址

if (args.length == 0) {

System.err.println("The first argument must be server address");

System.exit(1);

}

AppServer as = new AppServer();

as.connectZookeeper(args[0]);

as.handle();

}

}

 

 

 

 

1-4) 分布式共享锁

需求描述:

在我们自己的分布式业务系统中,可能会存在某种资源,需要被整个系统的各台服务器共享访问,但是只允许一台服务器同时访问

 

设计思路:

 

 

代码开发:

public class DistributedClientMy {

// 超时时间

private static final int SESSION_TIMEOUT = 5000;

// zookeeper server列表

private String hosts = "spark01:2181,spark02:2181,spark03:2181";

private String groupNode = "locks";

private String subNode = "sub";

private boolean haveLock =false;

 

private ZooKeeper zk;

// 当前client创建的子节点

private volatile StringthisPath;

 

/**

 * 连接zookeeper

 */

public void connectZookeeper()throws Exception {

zk = new ZooKeeper("spark01:2181",SESSION_TIMEOUT,new Watcher() {

public void process(WatchedEvent event) {

try {

 

// 子节点发生变化

if (event.getType() ==EventType.NodeChildrenChanged

&& event.getPath().equals("/" + groupNode)) {

// thisPath是否是列表中的最小节点

List<String> childrenNodes = zk.getChildren("/"

+ groupNode, true);

String thisNode = thisPath

.substring(("/" + groupNode + "/").length());

// 排序

Collections.sort(childrenNodes);

if (childrenNodes.indexOf(thisNode) == 0) {

doSomething();

thisPath = zk.create("/" +groupNode +"/"

+ subNode, null,Ids.OPEN_ACL_UNSAFE,

CreateMode.EPHEMERAL_SEQUENTIAL);

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

});

 

// 创建子节点

thisPath = zk.create("/" +groupNode +"/" +subNode,null,

Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

 

// wait一小会, 让结果更清晰一些

Thread.sleep(new Random().nextInt(1000));

 

// 监听子节点的变化

List<String> childrenNodes = zk.getChildren("/" +groupNode,true);

 

// 列表中只有一个子节点, 那肯定就是thisPath, 说明client获得锁

if (childrenNodes.size() == 1) {

doSomething();

thisPath = zk.create("/" +groupNode +"/" +subNode,null,

Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

}

}

 

/**

 * 共享资源的访问逻辑写在这个方法中

 */

private void doSomething()throws Exception {

try {

System.out.println("gain lock: " +thisPath);

Thread.sleep(2000);

// do something

} finally {

System.out.println("finished: " +thisPath);

// 将thisPath删除, 监听thisPath的client将获得通知

// 相当于释放锁

zk.delete(this.thisPath, -1);

}

}

 

public static void main(String[] args)throws Exception {

DistributedClientMy dl = new DistributedClientMy();

dl.connectZookeeper();

Thread.sleep(Long.MAX_VALUE);

}

 

}


Redis 总结

 

 

概述:

Redis 是一种高性能数据库,储存结构为key-value方式,redis中的value可以储存很多类型,而却储存的数据特别大,实现在市场上用的比较多的一种非关系型数据库。

 

Redis的特点:

1、redis 访问的速度快,数据保存在内存中

2、Redis有持久化的机制,可以定期的把数据dump到磁盘中

3、Redis为每一条的数据记录了一个更新操作,一旦发生事故可以在日志中获取数据的信息。

4、Redis 支持分布式储存,更大大的提高了储存能力

5、Redis 支持更多的储存结构。

 

 

Redis 的安装

1-1)安装

官网:http://redis.io/download

 

[root@hadoop1 redis]# tar -zxvf redis-3.0.7.tar.gz

[root@hadoop1 redis-3.0.7]# ls

00-RELEASENOTES  BUGS  CONTRIBUTING  COPYING  deps  INSTALL  Makefile  MANIFESTO  README  redis.conf  runtest  runtest-cluster  runtest-sentinel  sentinel.conf  src  tests  utils

[root@hadoop1 redis-3.0.7]# cd src/

[root@hadoop1 src]# make

[root@hadoop1 src]# make  install

 

 

1-2)查看配置文件

Cd   /home/redis/redis-3.0.7

Vi   redis.conf

 

 

参数:

#   save ""

 

save 900 1

save 300 10

save 60 10000

 

 

 

1-3) 启动

启动服务器端:

 

[root@hadoop1 src]# ./redis-server &

[1] 4065

[root@hadoop1 src]# 4065:C 22 Aug 13:13:03.290 # Warning: no config file specified, using the default config. In order to specify a config file use ./redis-server /path/to/redis.conf

4065:M 22 Aug 13:13:03.290 * Increased maximum number of open files to 10032 (it was originally set to 1024).

                _._                                                  

           _.-``__ ''-._                                             

      _.-``    `.  `_.  ''-._           Redis 3.0.7 (00000000/0) 64 bit

  .-`` .-```.  ```\/    _.,_ ''-._                                   

 (    '      ,       .-`  | `,    )     Running in standalone mode

 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379

 |    `-._   `._    /     _.-'    |     PID: 4065

  `-._    `-._  `-./  _.-'    _.-'                                   

 |`-._`-._    `-.__.-'    _.-'_.-'|                                  

 |    `-._`-._        _.-'_.-'    |           http://redis.io        

  `-._    `-._`-.__.-'_.-'    _.-'                                   

 |`-._`-._    `-.__.-'    _.-'_.-'|                                  

 |    `-._`-._        _.-'_.-'    |                                  

  `-._    `-._`-.__.-'_.-'    _.-'                                   

      `-._    `-.__.-'    _.-'                                       

          `-._        _.-'                                           

              `-.__.-'                                               

 

4065:M 22 Aug 13:13:03.301 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.

4065:M 22 Aug 13:13:03.301 # Server started, Redis version 3.0.7

4065:M 22 Aug 13:13:03.302 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.

4065:M 22 Aug 13:13:03.302 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.

4065:M 22 Aug 13:13:03.302 * The server is now ready to accept connections on port 6379

 

 

 

启动客户端:

[root@hadoop1 src]# ./redis-cli

127.0.0.1:6379> set aas dff

OK

127.0.0.1:6379> get aas

"dff"

127.0.0.1:6379>

 

 

启动方式:

1-1)前台启动

[root@hadoop1 bin]# redis-server

1-2)后台启动

[root@hadoop1 bin]# redis-server &. > /dev/null 2>&1  &

 

方式一:

1>/dev/null : 把程序的“1”——标准输出,重定向到文件/dev/null

2>&1      : 把程序的“2”——错误输出,重定向到“1”所去的文件

&         :   把程序放到后台运行  

 

方式二:

 

修改配置文件,

vi  redis.conf

修改其中一个配置

 

保存文件后再用普通命令启动,也可以启动为后台模式

[root@notrue-centos redis]# bin/redis-server  ./redis.conf

 

 

客户端连接测试:

public class JedisClientTest {

public static void main(String[]args) {

// 创建一个jedis客户端对象(redis的客户端连接)

Jedis client = new Jedis("mini1", 6379);

// 测试服务器是否连通

String resp = client.ping();

System.out.println(resp);

}

}

  

Redis的数据功能

1-1)常用的数据类型

String  /  list   /  lpush  /  rpush /  lange / keys / hash / set  

 

1-2 ) 常用的命令

Del  /  dump  / exists / keys / move  /  type  /  append /  decr /  get  /  getbit  /  incr  /  mget  /  set  /  hdel  /  hget  /  hkeys /  hset  / blpop  /  llen  /  lpop / lpush  /  list   /  rpop  /  sadd  /  sdiff  /  smove  /  

 

 

代码示例

1-1String存放到redis

 

        Jedis jedis = new Jedis();

jedis.set("redis1","xiaozhang");

jedis.set("redis2","xiaowang ");

String u02 = jedis.get("redis1");

String u03 = jedis.get("redis2");

System.out.println(u02);

System.out.println(u03);Jedisjedis =new Jedis();

jedis.set("redis1","xiaozhang");

jedis.set("redis2","xiaowang ");

String u02 = jedis.get("redis1");

String u03 = jedis.get("redis2");

System.out.println(u02);

       System.out.println(u03);

 

 

1-2) Redis储存独享信息

ProductInfo p = new ProductInfo();

p.setName("test1");

p.setDescription("小张");

p.setPrice(10.8);

// 利用gson将对象转成json

Gson gson = new Gson();

String pJson = gson.toJson(p);

// 将json串存入redis

jedis.set("test1",pJson);

// 从redis中取出对象的json

String pJsonResp = jedis.get("test1");

// 将返回的json解析成对象

ProductInfo pResponse = gson.fromJson(pJsonResp, ProductInfo.class);

// 显示对象的属性

System.out.println(pResponse);

 

 

 

对象信息如下:

package cn.****.jedis.bean;

 

import java.io.Serializable;

 

public class ProductInfoimplements Serializable {

 

/**

 *

 */

private static final long serialVersionUID = 3002512957989050750L;

private String name;

private String description;

private double price;

private String catelog;

 

public String getName() {

return name;

}

 

public void setName(Stringname) {

this.name =name;

}

 

public String getDescription() {

return description;

}

 

public void setDescription(Stringdescription) {

this.description =description;

}

 

public double getPrice() {

return price;

}

 

public void setPrice(double price) {

this.price =price;

}

 

public String getCatelog() {

return catelog;

}

 

public void setCatelog(Stringcatelog) {

this.catelog =catelog;

}

 

@Override

public String toString() {

 

return name +" " +description +" " +catelog +" " +price;

}

 

}

 

1-3Redis购物车的使用

package cn.****.jedis.hashcart;

 

import java.util.Map;

import java.util.Set;

import java.util.Map.Entry;

 

import org.junit.Before;

import org.junit.Test;

 

import redis.clients.jedis.Jedis;

 

public class BuyCartService {

private Jedis jedis = null;

private static final StringCART_PRIFIX ="test";

 

@Before

public void init() {

jedis = new Jedis("localhost", 6379);

}

 

/**

 * 添加商品到购物车

 */

@Test

public void testAddItemToCart() {

jedis.hset(CART_PRIFIX +"1","肥皂","2");

jedis.hset(CART_PRIFIX +"2","手铐","1");

jedis.hset(CART_PRIFIX +"3","皮鞭","3");

jedis.hset(CART_PRIFIX +"4","蜡烛","4");

jedis.close();

}

 

/**

 * 查询购物车信息

 */

@Test

public void testGetCartInfo() {

Map<String, String> cart = jedis.hgetAll(CART_PRIFIX +"1");

Set<Entry<String, String>> entrySet =cart.entrySet();

for (Entry<String, String>ent :entrySet) {

System.out.println(ent.getKey() +":" +ent.getValue());

}

jedis.close();

}

 

/**

 * 更改购物车

 */

@Test

public void editCart() {

// 给蜡烛商品项的数量加1

jedis.hincrBy(CART_PRIFIX +"1","蜡烛", 1);

jedis.close();

}

 

/**

 * 从购物车中删除商品项

 */

@Test

public void delItemFromCart() {

jedis.hdel(CART_PRIFIX +"1","手铐");

jedis.close();

}

}

 

 

1-4Redis集合的使用

package cn.****.jedis.set;

 

import java.util.Set;

 

import redis.clients.jedis.Jedis;

 

public class RedisSet {

public static void main(String[]args) {

Jedis jedis = new Jedis("localhost", 6379);

jedis.sadd("name1","1","2","3");

jedis.sadd("sex1","1","3455","56789");

// 差集

Set<String> sdiff2 = jedis.sdiff("name1", "sex1");

// 并集

Set<String> sunion = jedis.sunion("name1", "sex1");

// 交集

Set<String> sdiff = jedis.sinter("name1", "sex1");

for (String string : sdiff) {

System.out.println("sdiff:" +string);

}

}

}

 

 

1-5zincrby 方法的使用

package cn.****.jedis.sortedset.lol;

 

import java.util.Random;

 

import redis.clients.jedis.Jedis;

 

public class LolBoxPlayer {

public static void main(String[]args)throws Exception {

@SuppressWarnings("resource")

Jedis jedis = new Jedis("localhost", 6379);

Random random = new Random();

String[] heros = { "易大师", "德邦", "剑姬", "盖伦", "阿卡丽", "金克斯", "提莫", "猴子",

"亚索" };

while (true) {

int index =random.nextInt(heros.length);

// 选择一个英雄

String hero = heros[index];

// 开始玩游戏

Thread.sleep(1000);

// 给集合中的该英雄的出场次数加1

// 第一次添加的时候,集合不存在,zincrby方法会创建

jedis.zincrby("hero:ccl:phb", 1,hero);

System.out.println(hero +" 出场了.......");

}

}

 

}

 

 

 

Hadoop 是一个性能、可靠性、可扩展性、可管理性的软件,为以后的分布式打下了基础,接下来咱们好好的深刨一下这个有意思的框架。

 

CDH:

CDH (Cloudera's Distribution, including Apache Hadoop),是Hadoop众多分支中的一种,由Cloudera维护,基于稳定版本的Apache Hadoop构建,并集成了很多补丁,可直接用于生产环境。

 

Cloudera Manager则是为了便于在集群中进行Hadoop等大数据处理相关的服务安装和监控管理的组件,对集群中主机、HadoopHiveSpark等服务的安装配置管理做了极大简化。

 

安装:

[root@hadoop1 hadoop]# tar -zxvf hadoop-2.6.4.tar.gz

[root@hadoop1 hadoop-2.6.4]# cd etc/hadoop/

[root@hadoop1 hadoop]# vi  core-site.xml

加入以下配置:

 <configuration>

<property>

<name>fs.defaultFS</name>

<value>hdfs://hadoop1:9000</value>

</property>

<property>

<name>hadoop.tmp.dir</name>

<value>/home/tmpe</value>

</property>

</configuration>

 

[root@hadoop1 hadoop]# vi hadoop-env.sh

加入以下配置:

export JAVA_HOME=/home/jdk/jdk1.7.0_76

 

 

[root@hadoop1 hadoop]#vi  hdfs-site.xml

加入以下配置:

<configuration>

<property>

<name>dfs.namenode.name.dir</name>

<value>/home/hadoop/data/name</value>

</property>

<property>

<name>dfs.datanode.data.dir</name>

<value>/home/hadoop/data/data</value>

</property>

 

<property>

<name>dfs.replication</name>

<value>3</value>

</property>

 

<property>

<name>dfs.secondary.http.address</name>

<value>hadoop1:50090</value>

</property>

</configuration>

 

 

[root@hadoop1 hadoop]# cat slaves

加入以下配置:

hadoop1

hadoop2

hadoop3

 

 

[root@hadoop1 hadoop]# cat mapred-env.sh

加入以下配置:

export JAVA_HOME=/home/jdk/jdk1.7.0_76

 

 

[root@hadoop1 hadoop]# vi  yarn-site.xml

加入以下配置:

<configuration>

<property>

<name>yarn.resourcemanager.hostname</name>

<value>hadoop1</value>

</property>

 

<property>

<name>yarn.nodemanager.aux-services</name>

<value>mapreduce_shuffle</value>

</property>

</configuration>

 

 

[root@hadoop1 hadoop]# vi yarn-env.sh

添加JAVA路径

export JAVA_HOME=/home/jdk1.7

 

 

 

创建文件夹:

[root@hadoop1 hadoop]#  mkdir - p  /home/tmpe

[root@hadoop1 hadoop]#  mkdir -p  /home/hadoop/data/name

[root@hadoop1 hadoop]#  mkdir -p  /home/hadoop/data/data

 

 

以上标红的可以在其他的机器上

 

启动hadoop

在集群的机器上格式化数据

[root@hadoop1 bin]# hadoop  namenode  -fromat

 

 

启动全部的进程:

[root@hadoop1 sbin]# ./start-all.sh

 

 

或者单独的启动程序:

[root@hadoop1 sbin]# ./start-dfs.sh

[root@hadoop1 sbin]# ./start-yarn.sh

 

 

查看进程:

[root@hadoop1 ~]# jps

7996 DataNode

8366 NodeManager

8733 Jps

8251 ResourceManager

 

 

[root@hadoop2 ~]# jps

5575 NodeManager

5783 Jps

5466 SecondaryNameNode

5369 DataNode

 

 

 

[root@hadoop3 ~]# jps

4615 Jps

4298 DataNode

4441 NodeManager

 

 

 

Hadoop  HDFS 系统详解:

概述:

1HDFS分为两大角色:Namenode  , datanode , Secondary Name

2nameNode保存数据的元数据

3DataNode负责管理用户的文件的数据块,文件会按照块的大小保存到不同的DataNode ,每一个数据都有很多块的副本,保存在不同的机器上。

4dataNode会定期的向namenode虎豹闲杂保存到数据的情况

5HDFS的内部工作机制对客户端保持透明,客户端请求访问HDFS都是通过向namenode申请来进行

 

用户上传文件思路:

1、客户端向nameNode发送要上传文件的请求,并把文件分为128M的块

2、nameNode 返回给用户是否能上传数据的状态

3、加入用户端需要上传一个120M的文件,客户端会通过Rpc请求NameNode,并返回需要上传给那些DataNode(分配机器的距离以及空间的大小等),会选择离namenode比较近的机器分配,同机架的有限.

4、客户端请求建立block传输管道chnnel上传数据

5、在上传是datanode会与其他的机器建立连接并把数据块传送到其他的机器上

6、dataNodenamenode汇报自己的储存情况以及自己的信息

7、档第一个快上传完后再去执行其他的快的上传

 

 

图例:

 

 

 

 

 

 

补充:

     它分为两个部分:NameNode和DateNode,NameNode相当于一个领导,它管理集群内的DataNode,当客户发送请求过来后,NameNode会根据情况指定存储到哪些DataNode上,而其本身自己并不存储真实的数据。那NameNode怎么知道集群内DataNode的信息呢?DataNode发送心跳信息给NameNode。

     Namenode具备机架感知的能力,可以配置.

 

用户读取数据的流程:

 

客户端将需要读取的文件的信息发送给namenode,namenode会把文件的元数据返回给用户,用户根据返回的储存block的机器上去请求datanode的信息,最后客户端再把数据库合并成整个数据。

 

图例:

 

 

 

 

 

元数据的Checkpoint 过程

概念:

每隔一段时间secondary  namenode  namenode  上积累的所有的edits和一个最新的fsimage下载到本地,并加载到内存进行merge的过程叫做checkpoit

 

 

图例:

 

 

 

checkpoint的附带作用

namenodesecondary namenode的工作目录存储结构完全相同,所以,当namenode故障退出需要重新恢复时,可以从secondary namenode的工作目录中将fsimage拷贝到namenode的工作目录,以恢复namenode的元数据

 

 

DataNode 工作机制:

1-1dataNode工作机制

Data会定期的向namenode汇报自己的block储存的信息,被称为心跳,因为储存的信息特别重要,配置现象如下:

<property>

<name>dfs.blockreport.intervalMsec</name>

<value>3600000</value>

<description>Determines block reporting interval in milliseconds.</description>

</property>

 

1-2namenode故障判断

Datanode 进程死亡或者网络的原因导致无法与namenode进行通信,namenome并不会马上任务datanome是死亡的,需要经过一点时间,那么这段时间被称为超时时长,HDFS默认的时间为10分钟+30秒,定义超时的时间为timeout,可以通过hdfs.site.xml进行配置,时间的单位为毫秒,如下:

 

<property>

        <name>heartbeat.recheck.interval</name>

        <value>2000</value>

</property>

<property>

        <name>dfs.heartbeat.interval</name>

        <value>1</value>

</property>

Hadoop & hadoop fs 常用命令

1-1) hadoop  常用参数列表:

[root@hadoop1 hadoop]# hadoop -help

Usage: hadoop [--config confdir] COMMAND

       where COMMAND is one of:

  fs                   run a generic filesystem user client

  version              print the version

  jar <jar>            run a jar file

  checknative [-a|-h]  check native hadoop and compression libraries availability

  distcp <srcurl> <desturl> copy file or directories recursively

  archive -archiveName NAME -p <parent path> <src>* <dest> create a hadoop archive

  classpath            prints the class path needed to get the

  credential           interact with credential providers

                       Hadoop jar and the required libraries

  daemonlog            get/set the log level for each daemon

  trace                view and modify Hadoop tracing settings

 or

  CLASSNAME            run the class named CLASSNAME

 

Most commands print help when invoked w/o parameters.

 

 

1-2) Hadoop  fs 的参数列表:

 

[root@hadoop1 hadoop]# hadoop fs -help

Usage: hadoop fs [generic options]

[-appendToFile <localsrc> ... <dst>]

[-cat [-ignoreCrc] <src> ...]

[-checksum <src> ...]

[-chgrp [-R] GROUP PATH...]

[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]

[-chown [-R] [OWNER][:[GROUP]] PATH...]

[-copyFromLocal [-f] [-p] [-l] <localsrc> ... <dst>]

[-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]

[-count [-q] [-h] <path> ...]

[-cp [-f] [-p | -p[topax]] <src> ... <dst>]

[-createSnapshot <snapshotDir> [<snapshotName>]]

[-deleteSnapshot <snapshotDir> <snapshotName>]

[-df [-h] [<path> ...]]

[-du [-s] [-h] <path> ...]

[-expunge]

[-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]

[-getfacl [-R] <path>]

[-getfattr [-R] {-n name | -d} [-e en] <path>]

[-getmerge [-nl] <src> <localdst>]

[-help [cmd ...]]

[-ls [-d] [-h] [-R] [<path> ...]]

[-mkdir [-p] <path> ...]

[-moveFromLocal <localsrc> ... <dst>]

[-moveToLocal <src> <localdst>]

[-mv <src> ... <dst>]

[-put [-f] [-p] [-l] <localsrc> ... <dst>]

[-renameSnapshot <snapshotDir> <oldName> <newName>]

[-rm [-f] [-r|-R] [-skipTrash] <src> ...]

[-rmdir [--ignore-fail-on-non-empty] <dir> ...]

[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]

[-setfattr {-n name [-v value] | -x name} <path>]

[-setrep [-R] [-w] <rep> <path> ...]

[-stat [format] <path> ...]

[-tail [-f] <file>]

[-test -[defsz] <path>]

[-text [-ignoreCrc] <src> ...]

[-touchz <path> ...]

[-usage [cmd ...]]

 

 

 

图实例:

1-1Datanode

http://hadoop1:50070/

 

 

 

 

1-2Nodes

 

http://hadoop1:8088/

 

 

 

 

 

1-3SecondaryNameNode

http://hadoop1:50090/

 

 

 

JAVA  操作HADOOP

1-1) 环境的准备

Pom.xml导入一下信息:

<dependency>

    <groupId>org.apache.hadoop</groupId>

    <artifactId>hadoop-client</artifactId>

    <version>2.6.4</version>

</dependency>

 

1-2) 代码实现

或者在JAVA项目中导入hadoop安装包中的share下的包

 

文件的增删改查

 

package cn.****.bigdata.mymr.test;

 

public class HdfsClient {

FileSystem fs = null;

 

@Before

public void init()throws Exception {

 

// 构造一个配置参数对象,设置一个参数:我们要访问的hdfsURI

// 从而FileSystem.get()方法就知道应该是去构造一个访问hdfs文件系统的客户端,以及hdfs的访问地址

// new Configuration();的时候,它就会去加载jar包中的hdfs-default.xml

// 然后再加载classpath下的hdfs-site.xml

Configuration conf = new Configuration();

conf.set("fs.defaultFS","hdfs://hdp-node01:9000");

/**

 * 参数优先级: 1、客户端代码中设置的值 2、classpath下的用户自定义配置文件 3、然后是服务器的默认配置

 */

conf.set("dfs.replication","3");

 

// 获取一个hdfs的访问客户端,根据参数,这个实例应该是DistributedFileSystem的实例

// fs = FileSystem.get(conf);

 

// 如果这样去获取,那conf里面就可以不要配"fs.defaultFS"参数,而且,这个客户端的身份标识已经是hadoop用户

fs = FileSystem.get(new URI("hdfs://hdp-node01:9000"),conf,"hadoop");

 

}

 

/**

 * 往hdfs上传文件

 *

 * @throws Exception

 */

@Test

public void testAddFileToHdfs()throws Exception {

 

// 要上传的文件所在的本地路径

Path src = new Path("g:/redis-recommend.zip");

// 要上传到hdfs的目标路径

Path dst = new Path("/aaa");

fs.copyFromLocalFile(src,dst);

fs.close();

}

 

/**

 * 从hdfs中复制文件到本地文件系统

 *

 * @throws IOException

 * @throws IllegalArgumentException

 */

@Test

public void testDownloadFileToLocal()throws IllegalArgumentException,

IOException {

fs.copyToLocalFile(new Path("/jdk-7u65-linux-i586.tar.gz"),new Path(

"d:/"));

fs.close();

}

 

@Test

public void testMkdirAndDeleteAndRename()throws IllegalArgumentException,

IOException {

 

// 创建目录

fs.mkdirs(new Path("/a1/b1/c1"));

 

// 删除文件夹 ,如果是非空文件夹,参数2必须给值true

fs.delete(new Path("/aaa"),true);

 

// 重命名文件或文件夹

fs.rename(new Path("/a1"),new Path("/a2"));

 

}

 

/**

 * 查看目录信息,只显示文件

 *

 * @throws IOException

 * @throws IllegalArgumentException

 * @throws FileNotFoundException

 */

@Test

public void testListFiles()throws FileNotFoundException,

IllegalArgumentException, IOException {

 

// 思考:为什么返回迭代器,而不是List之类的容器

RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(

new Path("/"),true);

 

while (listFiles.hasNext()) {

LocatedFileStatus fileStatus =listFiles.next();

System.out.println(fileStatus.getPath().getName());

System.out.println(fileStatus.getBlockSize());

System.out.println(fileStatus.getPermission());

System.out.println(fileStatus.getLen());

BlockLocation[] blockLocations =fileStatus.getBlockLocations();

for (BlockLocation bl :blockLocations) {

System.out.println("block-length:" +bl.getLength() +"--"

+ "block-offset:" + bl.getOffset());

String[] hosts = bl.getHosts();

for (String host : hosts) {

System.out.println(host);

}

}

System.out.println("--------------为angelababy打印的分割线--------------");

}

}

 

/**

 * 查看文件及文件夹信息

 *

 * @throws IOException

 * @throws IllegalArgumentException

 * @throws FileNotFoundException

 */

@Test

public void testListAll()throws FileNotFoundException,

IllegalArgumentException, IOException {

 

FileStatus[] listStatus = fs.listStatus(new Path("/"));

 

String flag = "d--             ";

for (FileStatus fstatus :listStatus) {

if (fstatus.isFile())

flag = "f--         ";

System.out.println(flag +fstatus.getPath().getName());

}

}

}

 

 

通过流的方式访问hdfs

 

package cn.****.bigdata.mymr.test;

 

public class StreamAccess {

FileSystem fs = null;

 

@Before

public void init()throws Exception {

 

Configuration conf = new Configuration();

fs = FileSystem.get(new URI("hdfs://hdp-node01:9000"),conf,"hadoop");

 

}

 

/**

 * 通过流的方式上传文件到hdfs

 *

 * @throws Exception

 */

@Test

public void testUpload()throws Exception {

 

FSDataOutputStream outputStream =fs.create(

new Path("/angelababy.love"),true);

FileInputStream inputStream =new FileInputStream("c:/angelababy.love");

 

IOUtils.copy(inputStream, outputStream);

 

}

 

@Test

public void testDownLoadFileToLocal()throws IllegalArgumentException,

IOException {

 

// 先获取一个文件的输入流----针对hdfs上的

FSDataInputStream in = fs.open(new Path("/jdk-7u65-linux-i586.tar.gz"));

 

// 再构造一个文件的输出流----针对本地的

FileOutputStream out = new FileOutputStream(new File("c:/jdk.tar.gz"));

 

// 再将输入流中数据传输到输出流

IOUtils.copyBytes(in, out, 4096);

 

}

 

/**

 * hdfs支持随机定位进行文件读取,而且可以方便地读取指定长度用于上层分布式运算框架并发处理数据

 *

 * @throws IllegalArgumentException

 * @throws IOException

 */

@Test

public void testRandomAccess()throws IllegalArgumentException,IOException {

// 先获取一个文件的输入流----针对hdfs上的

FSDataInputStream in = fs.open(new Path("/iloveyou.txt"));

 

// 可以将流的起始偏移量进行自定义

in.seek(22);

 

// 再构造一个文件的输出流----针对本地的

FileOutputStream out = new FileOutputStream(new File(

"c:/iloveyou.line.2.txt"));

 

IOUtils.copyBytes(in, out, 19L, true);

 

}

 

/**

 * 显示hdfs上文件的内容

 *

 * @throws IOException

 * @throws IllegalArgumentException

 */

@Test

public void testCat()throws IllegalArgumentException,IOException {

 

FSDataInputStream in = fs.open(new Path("/iloveyou.txt"));

 

IOUtils.copyBytes(in, System.out, 1024);

}

}

 

 

HADOOP  MapReduce

代码实例:

Job端:

 

package cn..bigdata.mr.wc;

 

import java.io.IOException;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

 

/**

 * 本类是客户端用来指定wordcount job程序运行时所需要的很多参数: 比如,指定用哪个组件作为数据读取器、数据结果输出器

 * 指定用哪个类作为map阶段的业务逻辑类,哪个类作为reduce阶段的业务逻辑类 指定wordcount job程序的jar包所在路径 ....

 * 以及其他各种需要的参数

 *

 * @author

 *

 */

public class WordCountDriver {

public static void main(String[]args)throws Exception {

 

Configuration conf = new Configuration();

 

conf.set("fs.defaultFS","hdfs://mini1:9000");

/*

 * conf.set("mapreduce.framework.name", "yarn");

 * conf.set("yarn.resourcemanager.hostname", "mini1");

 */

Job job = Job.getInstance(conf);

// 告诉框架,我们的程序所在jar包的路径

// job.setJar("c:/wordcount.jar");

job.setJarByClass(WordCountDriver.class);

 

// 告诉框架,我们的程序所用的mapper类和reducer类

job.setMapperClass(WordCountMapper.class);

job.setReducerClass(WordCountReducer.class);

 

// 告诉框架,我们的mapperreducer输出的数据类型

job.setMapOutputKeyClass(Text.class);

job.setMapOutputValueClass(IntWritable.class);

 

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(IntWritable.class);

 

// 告诉框架,我们的数据读取、结果输出所用的format组件

// TextInputFormat是mapreduce框架中内置的一种读取文本文件的输入组件

job.setInputFormatClass(TextInputFormat.class);

job.setOutputFormatClass(TextOutputFormat.class);

 

// 告诉框架,我们要处理的文件在哪个路径下

FileInputFormat.setInputPaths(job,new Path("/wordcount/input/"));

 

// 告诉框架,我们的处理结果要输出到哪里去

FileOutputFormat.setOutputPath(job,new Path("/wordcount/output/"));

 

boolean res =job.waitForCompletion(true);

 

System.exit(res ? 0 : 1);

 

}

 

}

 

 

 

 

Map端:

package cn..bigdata.mr.wc;

 

import java.io.IOException;

 

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.LongWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Mapper;

 

/**

 * Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> KEYIN 是指框架读取到的数据的key的类型,

 * 在默认的InputFormat下,读到的key是一行文本的起始偏移量,所以key的类型是Long VALUEIN 是指框架读取到的数据的value的类型

 * , 在默认的InputFormat下,读到的value是一行文本的内容,所以value的类型是String

 *

 *

 * KEYOUT 是指用户自定义逻辑方法返回的数据中key的类型,由用户业务逻辑决定,

 * 在此wordcount程序中,我们输出的key是单词,所以是String

 *

 * VALUEOUT 是指用户自定义逻辑方法返回的数据中value的类型,由用户业务逻辑决定

 * 在此wordcount程序中,我们输出的value是单词的数量,所以是Integer

 *

 * 但是,String ,Long等jdk中自带的数据类型,在序列化时,效率比较低,hadoop为了提高序列化效率,自定义了一套序列化框架

 * 所以,在hadoop的程序中,如果该数据需要进行序列化(写磁盘,或者网络传输),就一定要用实现了hadoop序列化框架的数据类型

 *

 * Long ----> LongWritable String----> Text Integer----> IntWritable Null

 * ----> NullWritable

 *

 * @author

 *

 */

 

public class WordCountMapperextends

Mapper<LongWritable, Text, Text, IntWritable> {

 

// 这就是mapreduce框架中一个主体运行程序MapTask所要调用的用户业务逻辑方法

// MapTask会驱动InputFormat去读取数据(keyIN,VALUEIN),每读到一个KV对,就传入这个用户写的map方法中调用一次

// 在默认的inputformat实现中,此处的一个key就是一行的起始偏移量,value就是一行的内容

@Override

protected void map(LongWritable key,Text value,Context context)

throws IOException, InterruptedException {

String line = value.toString();

String[] words = line.split(" ");

for (String word : words) {

context.write(new Text(word),new IntWritable(1));

}

}

}

 

 

 

Reduce 端:

 

package cn..bigdata.mr.wc;

 

import java.io.IOException;

 

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Reducer;

 

public class WordCountReducerextends

Reducer<Text, IntWritable, Text, IntWritable> {

 

// reducetask在调我们写的reduce方法

// reducetask应该收到了前一阶段(map阶段)中所有maptask输出的数据中的一部分

// (数据的key.hashcode%reducetask==本reductask号)

// reducetask将这些收到kv数据拿来处理时,是这样调用我们的reduce方法的:

// 先将自己收到的所有的kv对按照k分组(根据k是否相同)

// 将某一组kv中的第一个kv中的k传给reduce方法的key变量,把这一组kv中所有的v用一个迭代器传给reduce方法的变量values

@Override

protected void reduce(Text key, Iterable<IntWritable>values,

Context context) throws IOException, InterruptedException {

 

int count = 0;

for (IntWritable v :values) {

count += v.get();

 

}

 

context.write(key,new IntWritable(count));

 

}

 

}

 

 

 

 

Hadoop 阶段总结:

1-1hadoop优化思路

A、所有能减少网络的尽量减少网络,所有能减少IO的尽量减少IO

B、在挂在磁盘时要注意各个目录的划分大小,例如:boot目录的大小,swap目录的大小

以及其他挂在的磁盘的大小

C、注意修改linux上文件打开的大小

[root@hadoop1 /]# vi /etc/security/limits.conf  添加一下配置

加上:* 代表搜有的用户都生效
* soft nofile 10240
* hard nofile 20480

 

[root@hadoop1 /]# vi /etc/pam.d/login

 session required pam_limits.so

与nginx 有相同的优化配置

 

D、配置网卡的MTU

[root@hadoop1 /]# cat /sys/class/net/eth0/mtu

[root@hadoop1 eth0]# echo "10000" > /sys/class/net/eth0/mtu

 

要根据网卡的配置来设置,适当的调节参数的大小

 

1-2)集群典型的节点分配

角色                      描述                      节点数目

HDFS Namenode     分布式文件系统用以储存文件        1个独立节点

                   系统以及数据块的元数据

 

HDFS Seccondary     NameNode的影子点                小规模集群可以和namenode

NameNode                                        共享节点,大规模集群用独立节点

 

HDFS DataNode       HDFS数据储存                    多个独立节点

 

MapReduce          MapReduce调度程序             一个独立节点,小规模集群可以

JobTracker                                          NameNode共享,大规模集群

                                                  使用独立节点

   

Hive                Hive 元数据的驱动程序           独立配置的话可以与namenode

                                                   共享节点,或者将元数据存放在

                                                   客户端

 

Zookeeper         可以提供集群高可用性的所服务      3个或3个以上的奇数的独立

                                                    节点(小规模可以和其他的角色

                                                    共享节点)

 

Hbase HMaster   HBase用以调度RegionServr的主服务    与其他角色共享节点的多个节点

 

Management Node   CRH特有的管理节点                一般为一个独立的节点如果小

                                                集群的话可以与其他角色共享

Hive的使用

 

概念

Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能。

主要用途:用来做离线数据分析,比直接用mapreduce开发效率更高,里哟摩纳哥HDFS作为储存系统,利用mapreduce作为运算的一个工具。

 

特性

1-1) 可扩展

Hive可以自由的扩展集群的规模,一般情况下不需要重启服务。

 

1-2) 延展性

Hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。

 

1-3) 容错

良好的容错性,节点出现问题SQL仍可完成执行。

 

 

A、前提是需要安装HADOOPMysql

B、在/etc/profile中配置路径

C、在/home/hive/conf中新建vi  hive-site.xml并加入以下内容:

 <configuration>

<property>

<name>javax.jdo.option.ConnectionURL</name>

<value>jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true</value>

<description>JDBC connect string for a JDBC metastore</description>

</property>

 

<property>

<name>javax.jdo.option.ConnectionDriverName</name>

<value>com.mysql.jdbc.Driver</value>

<description>Driver class name for a JDBC metastore</description>

</property>

 

<property>

<name>javax.jdo.option.ConnectionUserName</name>

<value>root</value>

<description>username to use against metastore database</description>

</property>

 

<property>

<name>javax.jdo.option.ConnectionPassword</name>

<value>root</value>

<description>password to use against metastore database</description>

</property>

</configuration>

 

D、解决包冲突问题:

       hivelib目录中jline.2.12.jar复制到/home/hadoop-2.6.4/share/hadoop/yarn/lib

E、运行HIVE测试

       Hive (环境变量配置好,即可使用这个命令)或使用/home/hive/bin/hive

2、常用HQL

 

F、启动hiveserver2

  

  [root@hadoop2 hadoop]#  hiveserver2  (前提是环境变量配置好)

 

  beeline> !connect jdbc:hive2//mini1:10000

hadoop01hiveserver2所启动的那台主机名,端口默认是10000

 

 

    beeline连接,提示root用户权限错误                  

    可在/home/hadoop-2.6.4/etc/hadoop/core-site.xml中加入如下配置:                  

   <property>                               

<name>hadoop.proxyuser.root.hosts</name>

<value>*</value>                         

   </property>                              

   <property>                               

<name>hadoop.proxyuser.root.groups</name>

<value>*</value>                         

   </property>  

                            

重启hdfs                                   

 

 

G、外部表与内部表的区别

1、先来说下Hive中内部表与外部表的区别: 

Hive 创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。这样外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据。

 

     需要注意的是传统数据库对表数据验证是 schema on write(写时模式),而 Hive 在load时是不检查数据是否符合schema的,hive 遵循的是 schema on read(读时模式),只有在读的时候hive才检查、解析具体的数据字段、schema。读时模式的优势是load data 非常迅速,因为它不需要读取数据进行解析,仅仅进行文件的复制或者移动。写时模式的优势是提升了查询性能,因为预先解析之后可以对列建立索引,并压缩,但这样也会花费要多的加载时间。 

1插入数据

 1-1) 插入普通数据
hive> create table t_test(id int ,name string, password string) row format delimited fields terminated by ',';

hive> insert into t_test(id,name,password) values('1','323','sfdf');

 

hive> select * from t_test;

 

[root@hadoop2 tempDate]# hadoop fs -cat /user/hive/warehouse/test.db/t_test/000000_0

 1,323,sfdf

 

 

注意:在默认的情况下会把表,数据库看做一个文件夹处理,只有数据才会看做文件,默认的事000000——0如果有多个文件则会一次累加。

 

2 执行HQL的方式

   2-1) 执行隐藏的客户端

  [root@hadoop2 tempDate]# hive -e 'show databases';

 

Logging initialized using configuration in jar:file:/home/hive/lib/hive-common-1.2.1.jar!/hive-log4j.properties

OK

default

Time taken: 2.137 seconds, Fetched: 1 row(s)

3、使用hdfs上传数据

3-1)查看字段

hive> desc t_test;

OK

id                   int                                      

name                 string                                   

password             string

 

3-2)查看执行结果

 

 [root@hadoop2 alertDate]# hadoop fs -put test.dat /user/hive/warehouse/test.db/t_test

 

   hive> select * from t_test;

OK

1 323 sfdf

NULL 2015-01 5

NULL 2015-01 15

NULL 2015-01 5

NULL 2015-01 8

NULL 2015-01 25

NULL 2015-01 5

NULL 2015-02 4

NULL 2015-02 6

NULL NULL NULL

 

[root@hadoop2 alertDate]# hadoop fs -cat /user/hive/warehouse/test.db/t_test/test.dat

A,2015-01,5

A,2015-01,15

B,2015-01,5

A,2015-01,8

B,2015-01,25

A,2015-01,5

A,2015-02,4

A,2015-02,6

 

注意:要上传的数据后缀必须是任意格式的,如果字段的类型与数据库的类型不一致,则会当做NULL来处理,以及多的字段会自动去除,不足的字段也会用NULL来补齐

4使用hive的命令导入与导出数据

   

hive> desc t_test5;

OK

id                   int                                      

name                 string                                   

password             string

 

4-1)数据上传到表:

  1)、上传之前

hive> load  data local inpath  '/home/alertDate' into table t_test5;

hive> load data local inpath '/home/alertDate/test.dat' into table t_test5;

Loading data to table test.t_test5

Table test.t_test5 stats: [numFiles=1, totalSize=105]

OK

Time taken: 0.905 seconds

 

  2)、上传之后

   [root@hadoop2 alertDate]# ls

    test.text

 

 

HDFS上传到表:

hive> load  data  inpath  '/test.dat' into table t_test5;

 

 

查看本地的文件:

[root@hadoop2 alertDate]# ls

test.dat

 

查看HDFS上的文件:

   1)、上传之前

[root@hadoop2 alertDate]# hadoop fs -ls /

Found 4 items

-rw-r--r--   3 root supergroup        105 2016-07-28 20:26 /test.dat

drwx-wx-wx   - root supergroup          0 2016-07-28 19:26 /tmp

drwxr-xr-x   - root supergroup          0 2016-07-28 19:17 /user

drwxr-xr-x   - root supergroup          0 2016-07-28 15:02 /wordcount

    2)、上传之后

[root@hadoop2 alertDate]# hadoop fs -ls /

Found 3 items

drwx-wx-wx   - root supergroup          0 2016-07-28 19:26 /tmp

drwxr-xr-x   - root supergroup          0 2016-07-28 19:17 /user

drwxr-xr-x   - root supergroup          0 2016-07-28 15:02 /wordcount

 

 

注意: 在使用本地上传数据时,会先把数据临时复制一份到HDFS上,目录是有上传的目录一直,等到上传完再把数据移动到制定的目录,在HDFS中则是直接移动文件。

 

4-2)把数据导出的本地

hive> insert overwrite local directory '/home/hadoop/student.txt'

    > select * from a;

Query ID = root_20160729153701_ec3d9a9c-f06f-45ea-93e9-617b81a8f677

Total jobs = 1

Launching Job 1 out of 1

Number of reduce tasks is set to 0 since there's no reduce operator

Starting Job = job_1469693904965_0022, Tracking URL = http://hadoop2:8088/proxy/application_1469693904965_0022/

Kill Command = /home/hadoop-2.6.4/bin/hadoop job  -kill job_1469693904965_0022

Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 0

2016-07-29 15:37:16,129 Stage-1 map = 0%,  reduce = 0%

2016-07-29 15:37:31,975 Stage-1 map = 100%,  reduce = 0%, Cumulative CPU 2.43 sec

 

 

 

[root@hadoop2 student.txt]# cat 000000_0

12222243434

2sdffsdfff

3sdffsdfff

 

 

 

5 HIVE 高级应用

5-1)表分区

      hive> create table t_part(id int,name string,password string) partitioned by(country string) row format delimited  fields terminated by ',';

      

     hive> load data local inpath '/home/alertDate/test.text' into table t_part  partition(country='CHA');

      hive> select * from t_part_1;

      OK

       A 2015-01 5 CHA

       A 2015-01 15 CHA

       B 2015-01 5 CHA

       A 2015-01 8 CHA

       B 2015-01 25 CHA

       A 2015-01 5 CHA

       A 2015-02 4 CHA

       A 2015-02 6 CHA

    

    

hive> load data local inpath '/home/alertDate/test.text' into table t_part_1  partition(country='USA');

Loading data to table test.t_part_1 partition (country=USA)

 

 hive> select * from t_part_1;

OK

A 2015-01 5 CHA

A 2015-01 15 CHA

B 2015-01 5 CHA

A 2015-01 8 CHA

B 2015-01 25 CHA

A 2015-01 5 CHA

A 2015-02 4 CHA

A 2015-02 6 CHA

 

A 2015-01 5 USA

A 2015-01 15 USA

B 2015-01 5 USA

A 2015-01 8 USA

B 2015-01 25 USA

A 2015-01 5 USA

A 2015-02 4 USA

A 2015-02 6 USA

 

 

查看hdfs结果:

[root@hadoop2 alertDate]# hadoop fs -ls  /user/hive/warehouse/test.db/t_part_1

Found 2 items

drwxr-xr-x   - root supergroup  0 2016-07-29 00:12 /user/hive/warehouse/test.db/t_part_1/country=CHA

drwxr-xr-x   - root supergroup  0 2016-07-29 00:13 /user/hive/warehouse/test.db/t_part_1/country=USA

 

[root@hadoop2 alertDate]# hadoop fs -ls  /user/hive/warehouse/test.db/t_part_1/country=CHA

Found 1 items

-rwxr-xr-x 3 root supergroup 105 2016-07-29 00:12 /user/hive/warehouse/test.db/t_part_1/country=CHA/test.text

 

 

[root@hadoop2 alertDate]# hadoop fs -cat  /user/hive/warehouse/test.db/t_part_1/country=CHA/test.text

A,2015-01,5

A,2015-01,15

B,2015-01,5

A,2015-01,8

B,2015-01,25

A,2015-01,5

A,2015-02,4

A,2015-02,6

 

 

Count 已经走了mapreduce

hive> select count(*) from t_part_1 where country='CHA';

Query ID = root_20160729002554_d9c3f70b-7d8a-4d4b-a3fd-512c904a0fd8

Total jobs = 1

Launching Job 1 out of 1

Number of reduce tasks determined at compile time: 1

In order to change the average load for a reducer (in bytes):

  set hive.exec.reducers.bytes.per.reducer=<number>

In order to limit the maximum number of reducers:

  set hive.exec.reducers.max=<number>

In order to set a constant number of reducers:

  set mapreduce.job.reduces=<number>

Starting Job = job_1469693904965_0002, Tracking URL = http://hadoop2:8088/proxy/application_1469693904965_0002/

Kill Command = /home/hadoop-2.6.4/bin/hadoop job  -kill job_1469693904965_0002

Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 1

2016-07-29 00:26:18,867 Stage-1 map = 0%,  reduce = 0%

2016-07-29 00:26:42,034 Stage-1 map = 100%,  reduce = 0%, Cumulative CPU 3.1 sec

2016-07-29 00:27:04,794 Stage-1 map = 100%,  reduce = 100%, Cumulative CPU 6.24 sec

MapReduce Total cumulative CPU time: 6 seconds 240 msec

Ended Job = job_1469693904965_0002

MapReduce Jobs Launched:

Stage-Stage-1: Map: 1  Reduce: 1   Cumulative CPU: 6.24 sec   HDFS Read: 7256 HDFS Write: 2 SUCCESS

Total MapReduce CPU Time Spent: 6 seconds 240 msec

OK

9

Time taken: 72.791 seconds, Fetched: 1 row(s)

 

 

如果用*的话就没有走mapreduce

 

hive> select *  from t_part_1 where country='CHA';

OK

A 2015-01 5 CHA

A 2015-01 15 CHA

B 2015-01 5 CHA

A 2015-01 8 CHA

B 2015-01 25 CHA

A 2015-01 5 CHA

A 2015-02 4 CHA

A 2015-02 6 CHA

NULL NULL CHA

Time taken: 0.218 seconds, Fetched: 9 row(s)

 

 

注意:分区在于隐藏于一个字段,便于查询数据,可以把较大的数据分为不同的小块,查询起来比较快。重要性比较大。

 

 

5-2)创建外部表

[root@hadoop2 alertDate]# hadoop fs -mkdir  /data/exp

       

 

hive> create external table td_ext_a(id string, name string,password string) row format

    > delimited fields terminated by ',' location '/data/exp';

OK

Time taken: 0.083 seconds

 

 

[root@hadoop2 alertDate]# hadoop fs -put /home/alertDate/text.text /data/exp

 

[root@hadoop2 alertDate]# hadoop fs -cat /data/exp/text.text

A,2015-01,5

A,2015-01,15

B,2015-01,5

A,2015-01,8

B,2015-01,25

A,2015-01,5

A,2015-02,4

A,2015-02,6

 

hive> select * from td_ext_a;

OK

A 2015-01 5

A 2015-01 15

B 2015-01 5

A 2015-01 8

B 2015-01 25

A 2015-01 5

A 2015-02 4

A 2015-02 6

Time taken: 0.094 seconds, Fetched: 8 row(s)

 

注意:location 指向的是HDFS的文件夹的路径,创建表后需要上传数据,然后再从hive中查询数据即可。

5-3)数据的压缩

  hive> create external table td_ext_b(id string, name string,password string) row format  

    delimited fields terminated by ',' STORED AS textfile;

     OK

Time taken: 0.096 seconds

 

 

 hive> load data local inpath '/home/alertDate/text.text' into table td_ext_b;

Loading data to table t_test.td_ext_b

Table t_test.td_ext_b stats: [numFiles=1, totalSize=104]

OK

Time taken: 0.357 seconds

 

hive> select * from td_ext_b;

OK

A 2015-01 5

A 2015-01 15

B 2015-01 5

A 2015-01 8

B 2015-01 25

A 2015-01 5

A 2015-02 4

A 2015-02 6

Time taken: 0.081 seconds, Fetched: 8 row(s)

 

 

5-4)复制表

hive> create table td_ext_c as select * from td_ext_b;

Query ID = root_20160729121935_5ed3bfa8-3439-4a22-b097-680caa318208

Total jobs = 3 Launching Job 1 out of 3 Number of reduce tasks is set to 0 since there's no reduce operator Starting Job = job_1469693904965_0004, Tracking URL = http://hadoop2:8088/proxy/application_1469693904965_0004/

Kill Command = /home/hadoop-2.6.4/bin/hadoop job  -kill job_1469693904965_0004

********

可以看出及时复制一个表结构也要走mapreduce,是多么耗时间啊,谁能告诉我原理,我感激不尽

hive> select * from td_ext_c;

OK

A 2015-01 5

A 2015-01 15

B 2015-01 5

A 2015-01 8

B 2015-01 25

A 2015-01 5

A 2015-02 4

A 2015-02 6

Time taken: 0.085 seconds, Fetched: 8 row(s)

 

5-5)表的分桶

 hive> create table stu__buck(id string,name string,password string) clustered  by (id) into 4 buckets row format delimited  fields terminated by ',';

OK

Time taken: 0.111 seconds

 

执行插入数据时又在走mapreduce,这又是什么情况????????

hive>  insert overwrite table stu__buck  select * from td_ext_a cluster by (id);

Query ID = root_20160729131451_439a5278-c3fe-4034-9150-35b275ad18f2

Total jobs = 1

Launching Job 1 out of 1

Number of reduce tasks not specified. Estimated from input data size: 1

In order to change the average load for a reducer (in bytes):

  set hive.exec.reducers.bytes.per.reducer=<number>

In order to limit the maximum number of reducers:

  set hive.exec.reducers.max=<number>

In order to set a constant number of reducers:

  set mapreduce.job.reduces=<number>

Starting Job = job_1469693904965_0005, Tracking URL = http://hadoop2:8088/proxy/application_1469693904965_0005/

Kill Command = /home/hadoop-2.6.4/bin/hadoop job  -kill job_1469693904965_0005

 

 

查询有再走mapreduce,原因在于对标分了桶,cluster去查询通需要走mapreduce

hive> select * from stu__buck  cluster by(id);

Query ID = root_20160729131803_ba429cab-b435-4be6-a5f0-b1be17cfd6e0

Total jobs = 1

Launching Job 1 out of 1

Number of reduce tasks not specified. Estimated from input data size: 1

In order to change the average load for a reducer (in bytes):

  set hive.exec.reducers.bytes.per.reducer=<number>

In order to limit the maximum number of reducers:

  set hive.exec.reducers.max=<number>

In order to set a constant number of reducers:

  set mapreduce.job.reduces=<number>

Starting Job = job_1469693904965_0006, Tracking URL = http://hadoop2:8088/proxy/application_1469693904965_0006/

Kill Command = /home/hadoop-2.6.4/bin/hadoop job  -kill job_1469693904965_0006

Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 1

2016-07-29 13:18:20,361 Stage-1 map = 0%,  reduce = 0%

2016-07-29 13:18:32,746 Stage-1 map = 100%,  reduce = 0%, Cumulative CPU 1.7 sec

2016-07-29 13:18:49,805 Stage-1 map = 100%,  reduce = 100%, Cumulative CPU 4.25 sec

MapReduce Total cumulative CPU time: 4 seconds 250 msec

Ended Job = job_1469693904965_0006

MapReduce Jobs Launched:

Stage-Stage-1: Map: 1  Reduce: 1   Cumulative CPU: 4.25 sec   HDFS Read: 6352 HDFS Write: 104 SUCCESS

Total MapReduce CPU Time Spent: 4 seconds 250 msec

OK

A 2015-01 5

A 2015-01 15

A 2015-01 8

A 2015-01 5

A 2015-02 4

A 2015-02 6

B 2015-01 5

B 2015-01 25

Time taken: 47.716 seconds, Fetched: 8 row(s)

 

5-6) 创建表详细讲解

CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name

   [(col_name data_type [COMMENT col_comment], ...)]

   [COMMENT table_comment]

   [PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]

   [CLUSTERED BY (col_name, col_name, ...)

   [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]

   [ROW FORMAT row_format]

   [STORED AS file_format]

   [LOCATION hdfs_path]

 

说明:

1、 CREATE TABLE 创建一个指定名字的表。如果相同名字的表已经存在,则抛出异常;用户可以用 IF NOT EXISTS 选项来忽略这个异常

2、 EXTERNAL关键字可以让用户创建一个外部表,在建表的同时指定一个指向实际数据的路径(LOCATION),Hive 创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。

3、 LIKE 允许用户复制现有的表结构,但是不复制数据。

4、 ROW FORMAT

DELIMITED [FIELDS TERMINATED BY char] [COLLECTION ITEMS TERMINATED BY char]

        [MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char]

   | SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value, property_name=property_value, ...)]

用户在建表的时候可以自定义 SerDe 或者使用自带的 SerDe。如果没有指定 ROW FORMAT 或者 ROW FORMAT DELIMITED,将会使用自带的 SerDe。在建表的时候,用户还需要为表指定列,用户在指定表的列的同时也会指定自定义的 SerDe,Hive通过 SerDe 确定表的具体的列的数据。

5、 STORED AS

SEQUENCEFILE|TEXTFILE|RCFILE

如果文件数据是纯文本,可以使用 STORED AS TEXTFILE。如果数据需要压缩,使用 STORED AS SEQUENCEFILE。

 

6、CLUSTERED BY

对于每一个表(table)或者分区, Hive可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。Hive也是 针对某一列进行桶的组织。Hive采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中。

把表(或者分区)组织成桶(Bucket)有两个理由:

1)获得更高的查询处理效率。桶为表加上了额外的结构,Hive 在处理有些查询时能利用这个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接 (Map-side join)高效的实现。比如JOIN操作。对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行JOIN操作就可以,可以大大较少JOIN的数据量。

2)使取样(sampling)更高效。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。

 

5-7DDL其他操作

 1增加/删除分区

 语法结构

ALTER TABLE table_name ADD [IF NOT EXISTS] partition_spec [ LOCATION 'location1' ] partition_spec [ LOCATION 'location2' ] ...

partition_spec:

: PARTITION (partition_col = partition_col_value, partition_col = partiton_col_value, ...)

 

ALTER TABLE table_name DROP partition_spec, partition_spec,...

ALTER TABLE table_name ADD partition(part=’a’) partition(part=’a’)

 2、重新命名

ALTER TABLE table_name RENAME TO new_table_name

 

hive> alter table a rename to a_a;

OK

Time taken: 0.76 seconds

 3增加/更新列

   语法结构

ALTER TABLE table_name ADD|REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...)

 

注:ADD是代表新增一字段,字段位置在所有列后面(partition列前)REPLACE则是表示替换表中所有字段

 

ALTER TABLE table_name CHANGE [COLUMN] col_old_name col_new_name column_type [COMMENT col_comment] [FIRST|AFTER column_name]

 

 

5-8DML其他操作

  1Load 的使用

    LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO

TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)]

 

    hive> load data local inpath '/home/alertDate/text.dat' into table b;

    Loading data to table t_test.b

    Table t_test.b stats: [numFiles=4, numRows=0, totalSize=143, rawDataSize=0]

    OK

    Time taken: 0.586 seconds   

 

     hive> load  data local inpath  '/home/alertDate' into table t_test5;

      hive> load data local inpath '/home/alertDate/test.dat' into table t_test5;

      Loading data to table test.t_test5

      Table test.t_test5 stats: [numFiles=1, totalSize=105]

      OK

      Time taken: 0.905 seconds

 

  2INSERT的使用

 

 

利用查询语句,将查询结果插入新的表

INSERT OVERWRITE [INTO] TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 FROM from_statement

 

插入一条数据

INSERT INTO TABLE VALUES(XX,YY,ZZ);

 

  3SELECT的使用

ü      语法结构

SELECT [ALL | DISTINCT] select_expr, select_expr, ...

FROM table_reference

[WHERE where_condition]

[GROUP BY col_list [HAVING condition]]

[CLUSTER BY col_list 

  | [DISTRIBUTE BY col_list] [SORT BY| ORDER BY col_list]

]

[LIMIT number]

 

注:1order by会对输入做全局排序,因此只有一个reducer,会导致当输入规模较大时,需要较长的计算时间。

2sort by不是全局排序,其在数据进入reducer前完成排序。因此,如果用sort by进行排序,并且设置mapred.reduce.tasks>1,则sort by只保证每个reducer的输出有序,不保证全局有序。

3distribute by(字段)根据指定的字段将数据分到不同的reducer,且分发算法是hash散列。

4Cluster by(字段)除了具有Distribute by的功能外,还会对该字段进行排序。

 

 

因此,如果分桶和sort字段是同一个时,此时,cluster by = distribute by + sort by

 

分桶表的作用:最大的作用是用来提高join操作的效率;

(思考这个问题:

select a.id,a.name,b.addr from a join b on a.id = b.id;

如果a表和b表已经是分桶表,而且分桶的字段是id字段

做这个join操作时,还需要全表做笛卡尔积吗?)

 

 

5-9)查看命令

  hive> show databases;

  OK

  default

  t_test

  Time taken: 0.016 seconds, Fetched: 2 row(s)

   

 

hive> show tables;

OK

a_a

b

stu__buck

t_table

t_test

td_ext

td_ext_a

td_ext_b

td_ext_c

Time taken: 0.056 seconds, Fetched: 9 row(s)  

 

hive> show partitions t_table;

 

hive> show functions;

OK

!

!=

%

&

*

+

-

/

<

<=

<=>

<>

=

==

>

>=

^

Abs   ***********

 

 

hive> desc extended a_a;

OK

id                   int                                      

name                 string                                   

pss                  string                                   

   

Detailed Table Information Table(tableName:a_a, dbName:t_test, owner:root, createTime:1469771112, lastAccessTime:0, retention:0, sd:StorageDescriptor(cols:[FieldSchema(name:id, type:int, comment:null), FieldSchema(name:name, type:string, comment:null), FieldSchema(name:pss, type:string, comment:null)], location:hdfs://hadoop2:9000/user/hive/warehouse/t_test.db/a_a, inputFormat:org.apache.hadoop.mapred.TextInputFormat, outputFormat:org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat, compressed:false, numBuckets:-1, serdeInfo:SerDeInfo(name:null, serializationLib:org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, parameters:{serialization.format=1}), bucketCols:[], sortCols:[], parameters:{}, skewedInfo:SkewedInfo(skewedColNames:[], skewedColValues:[], skewedColValueLocationMaps:{}), storedAsSubDirectories:false), partitionKeys:[], parameters:{numFiles=4, last_modified_by=root, last_modified_time=1469783976, COLUMN_STATS_ACCURATE=true, transient_lastDdlTime=1469783976, numRows=4, totalSize=68, rawDataSize=64}, viewOriginalText:null, viewExpandedText:null, tableType:MANAGED_TABLE)

Time taken: 0.197 seconds, Fetched: 5 row(s)

 

 

hive> desc formatted a_a;

OK

# col_name             data_type            comment             

   

id                   int                                      

name                 string                                   

pss                  string                                   

   

# Detailed Table Information    

Database:            t_test                

Owner:               root                  

CreateTime:          Fri Jul 29 13:45:12 CST 2016  

LastAccessTime:      UNKNOWN               

Protect Mode:        None                  

Retention:           0                     

Location:            hdfs://hadoop2:9000/user/hive/warehouse/t_test.db/a_a  

Table Type:          MANAGED_TABLE         

Table Parameters:    

COLUMN_STATS_ACCURATE true                

last_modified_by     root                

last_modified_time   1469783976          

numFiles             4                   

numRows              4                   

rawDataSize          64                  

totalSize            68                  

transient_lastDdlTime 1469783976          

   

# Storage Information    

SerDe Library:       org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe  

InputFormat:         org.apache.hadoop.mapred.TextInputFormat  

OutputFormat:        org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat  

Compressed:          No                    

Num Buckets:         -1                    

Bucket Columns:      []                    

Sort Columns:        []                    

Storage Desc Params:    

serialization.format 1                   

Time taken: 0.183 seconds, Fetched: 35 row(s)

 

 

 

注意:show  partitions  table_name  需要表创建了分区,desc extended t_name;

以及desc formatted table_name;可以查看更详细表的信息

 

 

 

 

6保存select查询结果的几种方式

6-1)将查询结果保存到一张新的hive表中

hive> create table t_table as select * from td_ext_a;

Query ID = root_20160729132825_745c6f57-93da-4563-bed3-788fd781598e

Total jobs = 3

Launching Job 1 out of 3

Number of reduce tasks is set to 0 since there's no reduce operator

Starting Job = job_1469693904965_0008, Tracking URL = http://hadoop2:8088/proxy/application_1469693904965_0008/

Kill Command = /home/hadoop-2.6.4/bin/hadoop job  -kill job_1469693904965_0008

Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 0

2016-07-29 13:28:40,662 Stage-1 map = 0%,  reduce = 0%

 

 

hive> select * from t_table;

OK

A 2015-01 5

A 2015-01 15

B 2015-01 5

A 2015-01 8

B 2015-01 25

A 2015-01 5

A 2015-02 4

A 2015-02 6

Time taken: 0.093 seconds, Fetched: 8 row(s)

 

6-2)将查询结果保存到一张已经存在的hive表中

 

hive> insert into table t_table select * from t_table;

Query ID = root_20160729133109_7e7e140c-13e5-470c-828e-e789b8e410f0

Total jobs = 3

Launching Job 1 out of 3

Number of reduce tasks is set to 0 since there's no reduce operator

Starting Job = job_1469693904965_0009, Tracking URL = http://hadoop2:8088/proxy/application_1469693904965_0009/

Kill Command = /home/hadoop-2.6.4/bin/hadoop job  -kill job_1469693904965_0009

 

 

 

hive> select * from t_table;

OK

A 2015-01 5

A 2015-01 15

B 2015-01 5

A 2015-01 8

B 2015-01 25

A 2015-01 5

A 2015-02 4

A 2015-02 6

A 2015-01 5

A 2015-01 15

B 2015-01 5

A 2015-01 8

B 2015-01 25

A 2015-01 5

A 2015-02 4

A 2015-02 6

Time taken: 0.079 seconds, Fetched: 16 row(s)

 

 

做快速查询与快速插入数据时都是比较耗时的,走mapreduce

6-3)将查询结果保存到指定的文件目录(可以是本地,也可以是hdfs

   

hive> insert overwrite local directory '/data/test.text' select * from t_table;

Query ID = root_20160729133824_dff25a98-38af-4a0a-b93e-02aa42356a45

Total jobs = 1

Launching Job 1 out of 1

Number of reduce tasks is set to 0 since there's no reduce operator

Starting Job = job_1469693904965_0011, Tracking URL = http://hadoop2:8088/proxy/application_1469693904965_0011/

Kill Command = /home/hadoop-2.6.4/bin/hadoop job  -kill job_1469693904965_0011

Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 0

2016-07-29 13:38:39,662 Stage-1 map = 0%,  reduce = 0%

 

 

[root@hadoop2 alertDate]# hadoop fs -ls /data/testFile

[root@hadoop2 alertDate]#

 

 

注意:将查询结果保存到hdfs的目录上也是要走mapreduce的,发现在这几个都要走mapreduce,看来需要注意一下操作了。

 

7  JOIN的使用

7-1)数据的准备  

hive> select * from a;                         

OK                                             

1 2222 243434                                 

2 sdff sdfff                                  

3 sdff sdfff                                  

Time taken: 0.1 seconds, Fetched: 3 row(s)     

                                               

                                               

hive> select * from b;                         

OK                                             

3 sdff sdfff                                  

5 sdff sdfff                                  

1 sdff sdfff                                  

Time taken: 0.078 seconds, Fetched: 3 row(s)   

 

 

7-2)数据的查询

Inner  的查询

hive> select * from a inner join b on a.id = b.id;                                                                                                                                                                                                   

Query ID = root_20160729141536_fe7d3f21-e31f-49b2-9401-6c2685151cd0                                                                                                                                                                                  

Total jobs = 1                                                                                                                                                                                                                                       

Execution log at: /tmp/root/root_20160729141536_fe7d3f21-e31f-49b2-9401-6c2685151cd0.log                                                                                                                                                             

2016-07-29 14:15:50 Starting to launch local task to process map join; maximum memory = 518979584                                                                                                                                                   

2016-07-29 14:15:53 Dump the side-table for tag: 1 with group count: 3 into file: file:/tmp/root/6143ddd7-42f9-4958-bbdc-bd3a22a640b1/hive_2016-07-29_14-15-36_256_2258338164921734285-1/-local-10003/HashTable-Stage-3/MapJoin-mapfile11--.hashtable

2016-07-29 14:15:53 Uploaded 1 File to: file:/tmp/root/6143ddd7-42f9-4958-bbdc-bd3a22a640b1/hive_2016-07-29_14-15-36_256_2258338164921734285-1/-local-10003/HashTable-Stage-3/MapJoin-mapfile11--.hashtable (350 bytes)                              

2016-07-29 14:15:53 End of local task; Time Taken: 2.802 sec.                                                                                                                                                                                        

Execution completed successfully                                                                                                                                                                                                                     

MapredLocal task succeeded                                                                                                                                                                                                                           

Launching Job 1 out of 1                                                                                                                                                                                                                             

OK                                                                                                                                                                                                                                                   

1 2222 243434 1 sdff sdfff                                                                                                                                                                                                                        

3 sdff sdfff 3 sdff sdfff                                                                                                                                                                                                                          

 

 

Left 查询

 

hive> select * from a left join b on a.id = b.id;                                                                                                                                                                                                      

Query ID = root_20160729141725_8b017579-c074-4128-90ea-7d9a424b44b6                                                                                                                                                                                    

Total jobs = 1                                                                                                                                                                                                                                         

Execution log at: /tmp/root/root_20160729141725_8b017579-c074-4128-90ea-7d9a424b44b6.log                                                                                                                                                               

2016-07-29 14:17:37 Starting to launch local task to process map join; maximum memory = 518979584                                                                                                                                                     

2016-07-29 14:17:39 Dump the side-table for tag: 1 with group count: 3 into file: file:/tmp/root/6143ddd7-42f9-4958-bbdc-bd3a22a640b1/hive_2016-07-29_14-17-25_723_6872206778159417490-1/-local-10003/HashTable-Stage-3/MapJoin-mapfile21--.hashtable  

2016-07-29 14:17:39 Uploaded 1 File to: file:/tmp/root/6143ddd7-42f9-4958-bbdc-bd3a22a640b1/hive_2016-07-29_14-17-25_723_6872206778159417490-1/-local-10003/HashTable-Stage-3/MapJoin-mapfile21--.hashtable (350 bytes)                                

2016-07-29 14:17:39 End of local task; Time Taken: 1.933 sec.                                                                                                                                                                                          

OK                                                                                                                                                                                                                                                     

1 2222 243434 1 sdff sdfff                                                                                                                                                                                                                          

2 sdff sdfff  NULL NULL NULL                                                                                                                                                                                                                         

3 sdff sdfff 3 sdff sdfff

 

 

right  查询

 

hive> select * from a right join b where a.id=b.id;

Query ID = root_20160729154358_38dd1b7a-b8ac-49b5-9a56-37f22636fc11

Total jobs = 1

Execution log at: /tmp/root/root_20160729154358_38dd1b7a-b8ac-49b5-9a56-37f22636fc11.log

2016-07-29 15:44:10 Starting to launch local task to process map join; maximum memory = 518979584

2016-07-29 15:44:13 Dump the side-table for tag: 0 with group count: 3 into file: file:/tmp/root/f4a99bd2-d473-48ac-bb84-4321ff29e9c5/hive_2016-07-29_15-43-58_345_9096767528169264921-1/-local-10003/HashTable-Stage-3/MapJoin-mapfile00--.hashtable

******    

Total MapReduce CPU Time Spent: 3 seconds 430 msec

OK

3 sdff sdfff 3 sdff sdfff

1 2222 243434 1 sdff sdfff

Time taken: 53.886 seconds, Fetched: 2 row(s)

 

 

8 HIVE 常用函数的使用

8-1split的使用

 hive> select * from a;

OK

1 2222 243434

2 sdff sdfff

3 sdff sdfff

Time taken: 0.095 seconds, Fetched: 3 row(s)

 

 

hive> select split(name,2) as  name from a;

OK

["","","","",""]

["sdff"]

["sdff"]

Time taken: 0.099 seconds, Fetched: 3 row(s)

 

hive> select split(name,1) as  name from a;

OK

["2222"]

["sdff"]

["sdff"]

Time taken: 0.33 seconds, Fetched: 3 row(s)

 

hive> select split(name,0) as  name from a;

OK

["2222"]

["sdff"]

["sdff"]

Time taken: 0.13 seconds, Fetched: 3 row(s)

 

注意split的切的索引的问题,01是相同的结果

 

8-2get_json_object的使用

hive> select * from a;

OK

1 2222 243434

2 sdff sdfff

3 sdff sdfff

6 {"name":"xiaozhang"} sdff

 

 

hive> select get_json_object(name,'$.name') as name from a;

OK

NULL

NULL

NULL

xiaozhang

Time taken: 0.366 seconds, Fetched: 4 row(s)

 

 

8-3)更多函数的使用

https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF

 

 

 

9 HIVE SHELL 参数的使用

  9-1HIVE的参数

  语法结构

hive [-hiveconf x=y]* [<-i filename>]* [<-f filename>|<-e query-string>] [-S]

说明:

1、 -i 从文件初始化HQL

2、 -e从命令行执行指定的HQL

3、 -f 执行HQL脚本

4、 -v 输出执行的HQL语句到控制台

5、 -p <port> connect to Hive Server on port number

6、 -hiveconf x=y Use this to set hive/hadoop configuration variables.

 

10  UDF开发实例

10-1java开发实例

   UDF  作用于单个数据行,产生一个数据行作为输出。(数学函数,字符串函数)

UDAF(用户定义聚集函数):接收多个输入数据行,并产生一个输出数据行。(countmax

 

简单UDF示例

1、先开发一个java类,继承UDF,并重载evaluate方法

package cn.****.bigdata.udf

import org.apache.hadoop.hive.ql.exec.UDF;

import org.apache.hadoop.io.Text;

 

public final class Lower extends UDF{

public Text evaluate(final Text s){

if(s==null){return null;}

return new Text(s.toString().toLowerCase());

}

}

 

2、打成jar包上传到服务器

3、将jar包添加到hiveclasspath

hive>add JAR /home/hadoop/udf.jar;

4、创建临时函数与开发好的java class关联

Hive>create temporary function tolowercase as 'cn.****.bigdata.udf.ToProvince';

 

5、即可在hql中使用自定义的函数tolowercaseip 

Select tolowercase(name),age from t_test;

 

l Json数据解析UDF开发

作业:

有原始json数据如下:

{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}

{"movie":"661","rate":"3","timeStamp":"978302109","uid":"1"}

{"movie":"914","rate":"3","timeStamp":"978301968","uid":"1"}

{"movie":"3408","rate":"4","timeStamp":"978300275","uid":"1"}

{"movie":"2355","rate":"5","timeStamp":"978824291","uid":"1"}

{"movie":"1197","rate":"3","timeStamp":"978302268","uid":"1"}

{"movie":"1287","rate":"5","timeStamp":"978302039","uid":"1"}

 

需要将数据导入到hive数据仓库中

我不管你中间用几个表,最终我要得到一个结果表:

movie

rate

timestamp

uid

1197

3

978302268

1

 

 

注:全在hive中完成,可以用自定义函数


Flume 学习总结

 

概述:

u Flume是一个分布式、可靠、和高可用的海量日志采集、聚合和传输的系统。

Flume可以采集文件,socket数据包等各种形式源数据,又可以将采集到的数据输出到HDFShbasehive、kafka等众多外部存储系统中

一般的采集需求,通过对flume的简单配置即可实现

u Flume针对特殊场景也具备良好的自定义扩展能力,因此,flume可以适用于大部分的日常数据采集场景

 

运行机制:

1、 Flume分布式系统中最核心的角色是agentflume采集系统就是由一个个agent所连接起来形成

2、 每一个agent相当于一个数据传递员,内部有三个组件:

a) Source:采集源,用于跟数据源对接,以获取数据

b) Sink:下沉地,采集数据的传送目的,用于往下一级agent传递数据或者往最终存储系统传递数据

c) Channelangent内部的数据传输通道,用于从source将数据传递到sink

 

注:

      Source ChannelSink之间传递数据的形式是Event事件;Event事件是一个数据流单元。

Source  +  sink   +  channel   =  Agent  

 

单个Agent采集

 

 

 

 

多个Agent的实例

 

 

 

多路(Multiplexing)Agent

 

架构设计要点

 

Flume的架构主要有一下几个核心概念:

Event:一个数据单元,带有一个可选的消息头

Flow:Event从源点到达目的点的迁移的抽象

Client:操作位于源点处的Event,将其发送到Flume Agent

Agent:一个独立的Flume进程,包含组件Source、Channel、Sink

Source:用来消费传递到该组件的Event

Channel:中转Event的一个临时存储,保存有Source组件传递过来的Event

Sink:从Channel中读取并移除Event,将Event传递到Flow Pipeline中的下一个Agent(如果有的话)

 

安装:

 tar -zxvf apache-flume-1.6.0-bin.tar.gz

 mv apache-flume-1.6.0-bin flume

 cd /flume/conf/

 mv flume-env.sh.template  flume-env.sh

 vi flume-env.sh     ----修改JAVA_HOME的路径

 Vi /etc/profile    配置环境变量:

 

 

 

 

常见的命令提示:

 

[root@hadoop2 conf]# flume-ng -help

Error: Unknown or unspecified command '-help'

 

Usage: /home/flume/flume//bin/flume-ng <command> [options]...

 

commands:

help                      display this help text

agent                     run a Flume agent

avro-client               run an avro Flume client

version                   show Flume version info

**************

启动程序

 

flume-ng agent -c conf -f  netcat-logger.conf -n a1  -Dflume.root.logger=INFO,console

-flume-ng agent    run a Flume agent

-c conf   指定flume自身的配置文件所在目录

-f conf/netcat-logger.con  指定我们所描述的采集方案

-n a1  指定我们这个agent的名字

-Dflume.root.logger=INFO,console 显示日志打印的方式

 

 

1 本地控制台案例:

Flume支持众多的sourcesink类型,详细手册可参考官方文档

http://flume.apache.org/FlumeUserGuide.html

1-1) 配置

 可以现在conf的目录下新建文件nicate-logger.conf

 vi  nicate-logger.conf

 

# 定义这个agent中各组件的名字

a1.sources = r1  

a1.sinks = k1    

a1.channels = c1  

 

# 描述和配置source组件:r1

a1.sources.r1.type = netcat    

a1.sources.r1.bind = localhost

a1.sources.r1.port = 44444

 

# 描述和配置sink组件:k1

a1.sinks.k1.type = logger

 

# 描述和配置channel组件,此处使用是内存缓存的方式

a1.channels.c1.type = memory

a1.channels.c1.capacity = 1000

a1.channels.c1.transactionCapacity = 100

 

# 描述和配置source  channel   sink之间的连接关系

a1.sources.r1.channels = c1

a1.sinks.k1.channel = c1

 

 

 

1-2) 测试

[root@hadoop2 ~]# telnet127.0.0.1 44444

Trying 127.0.0.1...

Connected to 127.0.0.1.

Escape character is '^]'.

df

OK

dhfgyigfi

 

 

服务器端打印的日志信息:

16/07/30 13:39:31 INFO source.NetcatSource: Created serverSocket:sun.nio.ch.ServerSocketChannelImpl[/127.0.0.1:44444]

16/07/30 14:04:18 INFO sink.LoggerSink: Event: { headers:{} body: 64 66 0D                                        df. }

16/07/30 14:04:24 INFO sink.LoggerSink: Event: { headers:{} body: 64 68 66 67 79 69 67 66 69 0D                   dhfgyigfi. }

16/07/30 14:04:26 INFO sink.LoggerSink: Event: { headers:{} body: 64 75 67 66 75 67 0D                            dugfug. }

16/07/30 14:04:27 INFO sink.LoggerSink: Event: { headers:{} body: 6A 66 72 68 67 72 0D                            jfrhgr. }

16/07/30 14:04:28 INFO sink.LoggerSink: Event: { headers:{} body: 64 66 68 69 67 66 75 72 69 67 0D                dfhigfurig. }

 

 

 

注意空格问题:

 

在配置时要有空格如:

a1.sources = r1  

连接时:[root@hadoop2 ~]# telnet 127.0.0.1 44444也要有空格

在打印日志是显示headers /  body (分为编码与内容)

 

2 本地单机HDFS测试案例:

2-1)配置

      # tail-hdfs.conf

# tail命令获取数据,下沉到hdfs

# 启动命令:

# bin/flume-ng agent -c conf -f conf/tail-hdfs.conf -n a1

 

# Name the components on this agent

a1.sources = r1

a1.sinks = k1

a1.channels = c1

 

# Describe/configure the source

a1.sources.r1.type = exec

a1.sources.r1.command = tail -F /root/logs/test.log

a1.sources.r1.channels = c1

 

# Describe the sink

a1.sinks.k1.type = hdfs

a1.sinks.k1.channel = c1

a1.sinks.k1.hdfs.path = /flume/tailout/%y-%m-%d/%H%M/

a1.sinks.k1.hdfs.filePrefix = events-

a1.sinks.k1.hdfs.round = true

a1.sinks.k1.hdfs.roundValue = 10

a1.sinks.k1.hdfs.roundUnit = minute

a1.sinks.k1.hdfs.rollInterval = 3

a1.sinks.k1.hdfs.rollSize = 20

a1.sinks.k1.hdfs.rollCount = 5

a1.sinks.k1.hdfs.batchSize = 1

a1.sinks.k1.hdfs.useLocalTimeStamp = true

#生成的文件类型,默认是Sequencefile,可用DataStream,则为普通文本

a1.sinks.k1.hdfs.fileType = DataStream

 

# Use a channel which buffers events in memory

a1.channels.c1.type = memory

a1.channels.c1.capacity = 1000

a1.channels.c1.transactionCapacity = 100

 

# Bind the source and sink to the channel

a1.sources.r1.channels = c1

a1.sinks.k1.channel = c1

 

 

 

2-2)测试

  flume-ng agent -c conf -f /home/flume/flume/conf/tail-hdfs.conf   -n agnet1  -Dflume.root.logger=INFO,console

 

 

16/07/30 20:29:28 INFO node.Application: Starting Channel c1

16/07/30 20:29:28 INFO node.Application: Waiting for channel: c1 to start. Sleeping for 500 ms

16/07/30 20:29:28 INFO instrumentation.MonitoredCounterGroup: Monitored counter group for type: CHANNEL, name: c1: Successfully registered new MBean.

16/07/30 20:29:28 INFO instrumentation.MonitoredCounterGroup: Component type: CHANNEL, name: c1 started

16/07/30 20:29:28 INFO node.Application: Starting Sink k1

16/07/30 20:29:28 INFO node.Application: Starting Source r1

16/07/30 20:29:28 INFO source.ExecSource: Exec source starting with command:tail -F /home/flumeLog/test.log

16/07/30 20:29:28 INFO instrumentation.MonitoredCounterGroup: Monitored counter group for type: SINK, name: k1: Successfully registered new MBean.

16/07/30 20:29:28 INFO instrumentation.MonitoredCounterGroup: Component type: SINK, name: k1 started

16/07/30 20:29:28 INFO instrumentation.MonitoredCounterGroup: Monitored counter group for type: SOURCE, name: r1: Successfully registered new MBean.

16/07/30 20:29:28 INFO instrumentation.MonitoredCounterGroup: Component type: SOURCE, name: r1 started

16/07/30 20:29:32 INFO hdfs.HDFSDataStream: Serializer = TEXT, UseRawLocalFileSystem = false

16/07/30 20:29:33 INFO hdfs.BucketWriter: Creating /flume/tailout/16-07-30/2020//events-.1469881772754.tmp

16/07/30 20:29:38 WARN hdfs.BucketWriter: Block Under-replication detected. Rotating file.

16/07/30 20:29:38 INFO hdfs.BucketWriter: Closing /flume/tailout/16-07-30/2020//events-.1469881772754.tmp

16/07/30 20:29:38 INFO hdfs.BucketWriter: Renaming /flume/tailout/16-07-30/2020/events-.1469881772754.tmp to /flume/tailout/16-07-30/2020/events-.1469881772754

16/07/30 20:29:38 INFO hdfs.BucketWriter: Creating /flume/tailout/16-07-30/2020//events-.1469881772755.tmp

  16/07/30 20:50:22 INFO hdfs.BucketWriter: Closing /flume/tailout/16-07-30/2050//events-.1469883004544.tmp

16/07/30 20:50:23 INFO hdfs.BucketWriter: Renaming /flume/tailout/16-07-30/2050/events-.1469883004544.tmp to /flume/tailout/16-07-30/2050/events-.1469883004544

16/07/30 20:50:25 INFO hdfs.BucketWriter: Creating /flume/tailout/16-07-30/2050//events-.1469883004545.tmp

查看HDFS的动态信息:

[root@hadoop2 ~]# hadoop fs -ls -R   /flume/tailout/16-07-30/

-rw-r--r--   3 root supergroup          0 2016-07-30 20:50 /flume/tailout/16-07-30/2050/events-.1469883004544.tmp

 

 

HDFS 文件信息:

[root@hadoop2 ~]# hadoop fs -cat  /flume/tailout/16-07-30/2020/events-.1469881772754

Sat Jul 30 20:23:09 CST 2016

 

[root@hadoop2 ~]# hadoop fs -cat  /flume/tailout/16-07-30/2030/events-.1469882131920

Sat Jul 30 20:35:45 CST 2016

 

注意标红的信息:

1 观察日志会发现同时会处理三条信息,神不知道,哪位大神指导,求指教(我靠发现是先往文件中写入数据,再上传到HDFS上只不过时间太短了,有做好了下一个写入的准备,下一个的写入准备正好是上一个的准备的事件)。

22050 会截取上一个整数的事件点,来获取数据

3、HDFS写入数据需要在运行在HDFS环境中运行,也就是卓只要运行在HDFS红就可以啦。多方便啊!!!!!!

 

 

3 多机测试实例:

  hadoop2 sources的采集     实现hadoop3hadoop4sinks(实现原理与NGINX差不多)

  

 

  

3-1)配置

  Hadoop2 配置:

 

   # tail命令获取数据发送到avro端口

# 另一个节点可配置一个avro源来中继数据,发送外部存储

 

#agent1 name

agent1.channels = c1

agent1.sources = r1

agent1.sinks = k1 k2

 

#set gruop

agent1.sinkgroups = g1

 

#set channel

agent1.channels.c1.type = memory

agent1.channels.c1.capacity = 1000

agent1.channels.c1.transactionCapacity = 100

 

agent1.sources.r1.channels = c1

agent1.sources.r1.type = exec

agent1.sources.r1.command = tail -F /root/log/test.log

 

# set  interceptors

agent1.sources.r1.interceptors = i1 i2

agent1.sources.r1.interceptors.i1.type = static

agent1.sources.r1.interceptors.i1.key = Type

agent1.sources.r1.interceptors.i1.value = LOGIN

agent1.sources.r1.interceptors.i2.type = timestamp

 

# set sink1

agent1.sinks.k1.channel = c1

agent1.sinks.k1.type = avro

agent1.sinks.k1.hostname = hadoop2

agent1.sinks.k1.port = 52020

 

# set sink2

agent1.sinks.k2.channel = c1

agent1.sinks.k2.type = avro

agent1.sinks.k2.hostname = mini3

agent1.sinks.k2.port = 52020

 

#set sink group

agent1.sinkgroups.g1.sinks = k1 k2

 

#set failover

agent1.sinkgroups.g1.processor.type = failover

agent1.sinkgroups.g1.processor.priority.k1 = 10

agent1.sinkgroups.g1.processor.priority.k2 = 1

agent1.sinkgroups.g1.processor.maxpenalty = 10000

 

Hadoop3 hadoop4的配置(注意修改名字)

 

# avro端口接收数据,下沉到logger

# bin/flume-ng agent -c conf -f conf/avro-logger.conf -n a1 -Dflume.root.logger=INFO,console

 

# 采集配置文件,avro-hdfs.conf

 

#set Agent name

a1.sources = r1

a1.channels = c1

a1.sinks = k1

 

#set channel

a1.channels.c1.type = memory

a1.channels.c1.capacity = 1000

a1.channels.c1.transactionCapacity = 100

 

# other node,nna to nns

a1.sources.r1.type = avro

a1.sources.r1.bind = hadoop3

a1.sources.r1.port = 52020

a1.sources.r1.interceptors = i1

a1.sources.r1.interceptors.i1.type = static

a1.sources.r1.interceptors.i1.key = Collector

a1.sources.r1.interceptors.i1.value = hadoop3

a1.sources.r1.channels = c1

 

#set sink to hdfs

a1.sinks.k1.type=hdfs

a1.sinks.k1.hdfs.path=/home/hdfs/flume/logdfs

a1.sinks.k1.hdfs.fileType=DataStream

a1.sinks.k1.hdfs.writeFormat=TEXT

a1.sinks.k1.hdfs.rollInterval=10

a1.sinks.k1.channel=c1

a1.sinks.k1.hdfs.filePrefix=%Y-%m-%d

# 发送数据:

# $ bin/flume-ng avro-client -H localhost -p 4141 -F /usr/logs/log.10

 

 

Hadoop3的启动日志:

mapreduce/sources:/home/hadoop-2.6.4/contrib/capacity-scheduler/*.jar:/home/hive/lib/*' -Djava.library.path=:/home/hadoop-2.6.4/lib/native org.apache.flume.node.Application -n agent1 -f avro-hdfs.con

16/07/31 10:52:26 INFO node.PollingPropertiesFileConfigurationProvider: Configuration provider starting

16/07/31 10:52:26 INFO node.PollingPropertiesFileConfigurationProvider: Reloading configuration file:avro-hdfs.con

16/07/31 10:52:26 INFO conf.FlumeConfiguration: Added sinks: k1 Agent: a1

16/07/31 10:52:26 INFO conf.FlumeConfiguration: Processing:k1

16/07/31 10:52:26 INFO conf.FlumeConfiguration: Processing:k1

16/07/31 10:52:27 INFO conf.FlumeConfiguration: Post-validation flume configuration contains configuration for agents: [a1]

16/07/31 10:52:27 WARN node.AbstractConfigurationProvider: No configuration found for this host:agent1

16/07/31 10:52:27 INFO node.Application: Starting new configuration:{ sourceRunners:{} sinkRunners:{} channels:{} }

 

 

Hadoop2的启动日志

mapreduce/sources:/home/hadoop-2.6.4/contrib/capacity-scheduler/*.jar:/home/hive/lib/*' -Djava.library.path=:/home/hadoop-2.6.4/lib/native org.apache.flume.node.Application -n agent1 -f tail-avro-avro-logger.con

16/07/31 10:54:56 INFO node.PollingPropertiesFileConfigurationProvider: Configuration provider starting

16/07/31 10:54:56 INFO node.PollingPropertiesFileConfigurationProvider: Reloading configuration file:tail-avro-avro-logger.con

16/07/31 10:54:56 INFO conf.FlumeConfiguration: Processing:k1

16/07/31 10:54:56 INFO conf.FlumeConfiguration: Added sinks: k1 Agent: a1

16/07/31 10:54:56 INFO conf.FlumeConfiguration: Processing:k1

16/07/31 10:54:56 INFO conf.FlumeConfiguration: Processing:k1

16/07/31 10:54:56 INFO conf.FlumeConfiguration: Processing:k1

16/07/31 10:54:56 INFO conf.FlumeConfiguration: Processing:k1

16/07/31 10:54:57 INFO conf.FlumeConfiguration: Post-validation flume configuration contains configuration for agents: [a1]

16/07/31 10:54:57 WARN node.AbstractConfigurationProvider: No configuration found for this host:agent1

16/07/31 10:54:57 INFO node.Application: Starting new configuration:{ sourceRunners:{} sinkRunners:{} channels:{} }

 

3-2)测试

 [root@hadoop2 flumeLog]# while true

> do

>  date >> test.log

> sleep 1

> done;

 

 

 

 

Azkaban 工作流调度器

介绍:

Azkaban是由Linkedin开源的一个批量工作流任务调度器。用于在一个工作流内以一个特定的顺序运行一组工作和流程。

Azkaban定义了一种KV文件(properties)格式来建立任务之间的依赖关系,并提供一个易于使用的web用户界面维护和跟踪你的工作流。

它有如下功能特点:

² Web用户界面

² 方便上传工作流

² 方便设置任务之间的关系

² 调度工作流

² 认证/授权(权限的工作)

² 能够杀死并重新启动工作流

² 模块化和可插拔的插件机制

² 项目工作区

² 工作流和任务的日志记录和审计

为什么需要工作流调度系统:

l  一个完整的数据分析系统通常都是由大量任务单元组成:

shell脚本程序,java程序,mapreduce程序、hive脚本等

l 各任务单元之间存在时间先后及前后依赖关系

l 为了很好地组织起这样的复杂执行计划,需要一个工作流调度系统来调度执行;

 

实现的方式 :

简单的任务调度:直接使用linuxcrontab来定义;

复杂的任务调度:开发调度平台或使用现成的开源调度系统,比如ooizeazkaban

 

 

下载地址:http://azkaban.github.io/downloads.html

 

Azkaban安装部署

 

[root@hadoop2 azkaban]# ls

azkaban-2.5.0  azkaban-executor-2.5.0  azkaban-web-2.5.0

 

进入mysql

mysql> create database azkaban;

mysql> use azkaban;

Database changed

mysql> source /home/hadoop/azkaban-2.5.0/create-all-sql-2.5.0.sql;

 

创建SSL配置

参考地址: http://docs.codehaus.org/display/JETTY/How+to+configure+SSL

命令: keytool -keystore keystore -alias jetty -genkey -keyalg RSA

运行此命令后,会提示输入当前生成 keystor的密码及相应信息,输入的密码请劳记,信息如下:

 

输入keystore密码: 

再次输入新密码:

您的名字与姓氏是什么?

  [Unknown]: 

您的组织单位名称是什么?

  [Unknown]: 

您的组织名称是什么?

  [Unknown]: 

您所在的城市或区域名称是什么?

  [Unknown]: 

您所在的州或省份名称是什么?

  [Unknown]: 

该单位的两字母国家代码是什么

  [Unknown]:  CN

CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CN 正确吗?

  [否]:  y

 

输入<jetty>的主密码

        (如果和 keystore 密码相同,按回车): 

再次输入新密码:

完成上述工作后,将在当前目录生成 keystore 证书文件,将keystore 拷贝到 azkaban web服务器根目录中.如:cp keystore azkaban/server

 

 

提示界面:

[root@hadoop2 azkaban-2.5.0]# keytool -keystore keystore -alias jetty -genkey -keyalg RSA

Enter keystore password:  

Re-enter new password:

What is your first and last name?

  [Unknown]:  

What is the name of your organizational unit?

  [Unknown]:  

What is the name of your organization?

  [Unknown]:  

What is the name of your City or Locality?

  [Unknown]:  

What is the name of your State or Province?

  [Unknown]:  

What is the two-letter country code for this unit?

  [Unknown]:  CN

Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CN correct?

  [no]:  y

 

Enter key password for <jetty>

(RETURN if same as keystore password):  

Re-enter new password:

 

 

配置文件

注:先配置好服务器节点上的时区

1、先生成时区配置文件Asia/Shanghai,用交互式命令tzselect 即可

2、拷贝该时区文件,覆盖系统本地时区配置

cp /usr/share/zoneinfo/Asia/Shanghai   /etc/localtime  

 

azkaban web服务器配置

进入azkaban web服务器安装目录 conf目录

 

1-1 ) 数据库

命令vi azkaban.properties

内容说明如下:

 

[root@hadoop2 azkaban]# cd azkaban-web-2.5.0/

[root@hadoop2 azkaban-web-2.5.0]# cd conf/

 

[root@hadoop2 conf]# vi azkaban.properties

 

#Azkaban Personalization Settings

azkaban.name=Test                           #服务器UI名称,用于服务器上方显示的名字

azkaban.label=My Local Azkaban                               #描述

azkaban.color=#FF3601                                                 #UI颜色

azkaban.default.servlet.path=/index                         #

web.resource.dir=web/                                                 #默认根web目录

default.timezone.id=Asia/Shanghai                           #默认时区,已改为亚洲/上海 默认为美国

 

#Azkaban UserManager class

user.manager.class=azkaban.user.XmlUserManager   #用户权限管理默认类

user.manager.xml.file=conf/azkaban-users.xml              #用户配置,具体配置参加下文

 

#Loader for projects

executor.global.properties=conf/global.properties    # global配置文件所在位置

azkaban.project.dir=projects                                                #

 

database.type=mysql                                                              #数据库类型

mysql.port=3306                                                                       #端口号

mysql.host=localhost                                                      #数据库连接IP

mysql.database=azkaban                                                       #数据库实例名

mysql.user=root                                                                #数据库用户名

mysql.password=root                                                          #数据库密码

mysql.numconnections=100                                                  #最大连接数

 

# Velocity dev mode

velocity.dev.mode=false

# Jetty服务器属性.

jetty.maxThreads=25                                                               #最大线程数

jetty.ssl.port=8443                                                                   #Jetty SSL端口

jetty.port=8081                                                                         #Jetty端口

jetty.keystore=keystore                                                          #SSL文件名

jetty.password=hadoop                                                             #SSL文件密码

jetty.keypassword=hadoop                                                      #Jetty主密码 与 keystore文件相同

jetty.truststore=keystore                                                                #SSL文件名

jetty.trustpassword=hadoop                                                   # SSL文件密码

 

# 执行服务器属性

executor.port=12321                                                               #执行服务器端口

 

# 邮件设置

mail.sender=xxxxxxxx@163.com                                       #发送邮箱

mail.host=smtp.163.com                                                       #发送邮箱smtp地址

mail.user=xxxxxxxx                                       #发送邮件时显示的名称

mail.password=**********                                                 #邮箱密码

job.failure.email=xxxxxxxx@163.com                              #任务失败时发送邮件的地址

job.success.email=xxxxxxxx@163.com                            #任务成功时发送邮件的地址

lockdown.create.projects=false                                           #

cache.directory=cache                                                            #缓存目录

 

 

1-2web服务器配置

[root@hadoop2 conf]# vi azkaban-users.xml

<azkaban-users>

        <user username="azkaban" password="azkaban" roles="admin" groups="azkaban" />

        <user username="metrics" password="metrics" roles="metrics"/>

        <user username="admin" password="admin" roles="admin,metrics" />

        <role name="admin" permissions="ADMIN" />

        <role name="metrics" permissions="METRICS"/>

</azkaban-users>

 

 

1-3azkaban 执行服务器executor配置

[root@hadoop2 conf]# vi azkaban.properties

 

#Azkaban

default.timezone.id=America/Los_Angeles

 

# Azkaban JobTypes Plugins

azkaban.jobtype.plugin.dir=Asia/Shanghai

 

#Loader for projects

executor.global.properties=conf/global.properties

azkaban.project.dir=projects

 

database.type=mysql

mysql.port=3306

mysql.host=localhost

mysql.database=azkaban

mysql.user=root

mysql.password=root

mysql.numconnections=100

 

# Azkaban Executor settings

executor.maxThreads=50

executor.port=12321

executor.flow.threads=30

 

 

 

 

启动

1-1web服务器

配置环境变量

vi /etc/profile

export AZKABAN=/home/azkaban/azkaban-web-2.5.0

 

启动

azkaban-web-start.sh

 

或者启动到后台

nohup  bin/azkaban-web-start.sh  1>/tmp/azstd.out  2>/tmp/azerr.out &

 

1-2执行服务器

在执行服务器目录下执行启动命令

azkaban-executor-start.sh

注:只能要执行服务器根目录运行

 

启动完成后,在浏览器(建议使用谷歌浏览器)中输入https://服务器IP地址:8443 ,即可访问azkaban服务了.在登录中输入刚才新的户用名及密码,点击 login.

1-3)  azkaban实战

Azkaba内置的任务类型支持commandjava

 

 

 

案例

1-1创建job描述文件建议在WIN上压缩有再上传文件,在linux压缩太麻烦

[root@hadoop2 /]# cd home/azkaban/

[root@hadoop2 azkaban]# mkdir commDir

[root@hadoop2 azkaban]# cd commDir/

[root@hadoop2 commDir]# vi command.job

#command.job

type=command                                                    

command=echo 'hello'

 

 

通过azkabanweb管理平台创建project并上传job压缩包

首先创建project,见下图:

 

 

 

 

 

1-2Command类型多job工作流flow

创建有依赖关系的多个job描述

第一个jobfoo.job

# foo.job

type=command

command=echo foo

第二个jobbar.job依赖foo.job

# bar.job

type=command

dependencies=foo

command=echo bar

 

1、将所有job资源文件打到一个zip包中

 

 

2、azkabanweb管理界面创建工程并上传zip

3、启动工作流flow

 

 

1-3HDFS操作任务

1、创建job描述文件

# fs.job

type=command

command=/home/hadoop/apps/hadoop-2.6.1/bin/hadoop fs -mkdir /azaz

 

2、job资源文件打包成zip文件

 

3、通过azkabanweb管理平台创建project并上传job压缩包

4、启动执行该job

 

1-4MAPREDUCE任务

Mr任务依然可以使用commandjob类型来执行

1、创建job描述文件,及mr程序jar包(示例中直接使用hadoop自带的example jar

# mrwc.job

type=command

command=/home/hadoop/apps/hadoop-2.6.1/bin/hadoop  jar hadoop-mapreduce-examples-2.6.1.jar wordcount /wordcount/input /wordcount/azout

 

 

2、将所有job资源文件打到一个zip包中

 

 

3、在azkabanweb管理界面创建工程并上传zip

4、启动job

 

1-5HIVE脚本任务

创建job描述文件和hive脚本

Hive脚本: test.sql

use default;

drop table aztest;

create table aztest(id int,name string) row format delimited fields terminated by ',';

load data inpath '/aztest/hiveinput' into table aztest;

create table azres as select * from aztest;

insert overwrite directory '/aztest/hiveoutput' select count(1) from aztest;

Job描述文件:hivef.job

# hivef.job

type=command

command=/home/hadoop/apps/hive/bin/hive -f 'test.sql'

 

2、将所有job资源文件打到一个zip包中

3、在azkabanweb管理界面创建工程并上传zip

4、启动job


Sqoop数据迁移

 

3.1 概述

sqoopapache旗下一款Hadoop和关系数据库服务器之间传送数据”的工具。

导入数据MySQLOracle导入数据到HadoopHDFSHIVEHBASE等数据存储系统;

导出数据:Hadoop的文件系统中导出数据到关系数据库mysql

 

 

3.2 工作机制

将导入或导出命令翻译成mapreduce程序来实现

在翻译出的mapreduce中主要是对inputformatoutputformat进行定制

 

 

 

3.3 sqoop实战及原理

3.3.1 sqoop安装

安装sqoop的前提是已经具备javahadoop的环境

1、下载并解压

最新版下载地址http://ftp.wayne.edu/apache/sqoop/1.4.6/

 

 

2、修改配置文件

$ cd $SQOOP_HOME/conf

$ mv sqoop-env-template.sh sqoop-env.sh

打开sqoop-env.sh并编辑下面几行:

export HADOOP_COMMON_HOME=/home/hadoop/apps/hadoop-2.6.1/

export HADOOP_MAPRED_HOME=/home/hadoop/apps/hadoop-2.6.1/

export HIVE_HOME=/home/hadoop/apps/hive-1.2.1

 

 

3、加入mysqljdbc驱动包

cp  ~/app/hive/lib/mysql-connector-java-5.1.28.jar   $SQOOP_HOME/lib/

4、验证启动

$ cd $SQOOP_HOME/bin

$ sqoop-version

预期的输出:

15/12/17 14:52:32 INFO sqoop.Sqoop: Running Sqoop version: 1.4.6

Sqoop 1.4.6 git commit id 5b34accaca7de251fc91161733f906af2eddbe83

Compiled by abe on Fri Aug 1 11:19:26 PDT 2015

到这里,整个Sqoop安装工作完成。

 

 

 

 

3.4 Sqoop的数据导入

导入工具导入单个表从RDBMSHDFS。表中的每一行被视为HDFS的记录。所有记录都存储为文本文件的文本数据或者Avrosequence文件二进制数据) 

3.4.1 语法

下面的语法用于将数据导入HDFS

$ sqoop import (generic-args) (import-args)

 

3.4.2 示例

表数据

mysql中有一个库userdb中三个表:emp, emp_addemp_conn

emp:

id

name

deg

salary

dept

1201

gopal

manager

50,000

TP

1202

manisha

Proof reader

50,000

TP

1203

khalil

php dev

30,000

AC

1204

prasanth

php dev

30,000

AC

1205

kranthi

admin

20,000

TP

emp_add:

id

hno

street

city

1201

288A

vgiri

jublee

1202

108I

aoc

sec-bad

1203

144Z

pgutta

hyd

1204

78B

old city

sec-bad

1205

720X

hitec

sec-bad

emp_conn:

 

id

phno

email

1201

2356742

gopal@tp.com

1202

1661663

manisha@tp.com

1203

8887776

khalil@ac.com

1204

9988774

prasanth@ac.com

1205

1231231

kranthi@tp.com

导入表表数据到HDFS

下面的命令用于从MySQL数据库服务器中的emp表导入HDFS

bin/sqoop import   \

--connect jdbc:mysql://hdp-node-01:3306/test   \

--username root  \

--password root   \

--table emp   \

--m 1  

 

如果成功执行,那么会得到下面的输出。

14/12/22 15:24:54 INFO sqoop.Sqoop: Running Sqoop version: 1.4.5

14/12/22 15:24:56 INFO manager.MySQLManager: Preparing to use a MySQL streaming resultset.

INFO orm.CompilationManager: Writing jar file: /tmp/sqoop-hadoop/compile/cebe706d23ebb1fd99c1f063ad51ebd7/emp.jar

-----------------------------------------------------

O mapreduce.Job: map 0% reduce 0%

14/12/22 15:28:08 INFO mapreduce.Job: map 100% reduce 0%

14/12/22 15:28:16 INFO mapreduce.Job: Job job_1419242001831_0001 completed successfully

-----------------------------------------------------

-----------------------------------------------------

14/12/22 15:28:17 INFO mapreduce.ImportJobBase: Transferred 145 bytes in 177.5849 seconds (0.8165 bytes/sec)

14/12/22 15:28:17 INFO mapreduce.ImportJobBase: Retrieved 5 records.

 

 

为了验证在HDFS导入的数据,请使用以下命令查看导入的数据

$ $HADOOP_HOME/bin/hadoop fs -cat /user/hadoop/emp/part-m-00000

 

emp表的数据和字段之间用逗号(,)表示。

1201, gopal,    manager, 50000, TP

1202, manisha,  preader, 50000, TP

1203, kalil,    php dev, 30000, AC

1204, prasanth, php dev, 30000, AC

1205, kranthi,  admin,   20000, TP

 

导入到HDFS指定目录

在导入表数据到HDFS使用Sqoop导入工具,我们可以指定目标目录。

以下是指定目标目录选项的Sqoop导入命令的语法。

--target-dir <new or exist directory in HDFS>

 

下面的命令是用来导入emp_add表数据到'/queryresult'目录。

bin/sqoop import \

--connect jdbc:mysql://hdp-node-01:3306/test \

--username root \

--password root \

--target-dir /queryresult \

--table emp --m 1

 

 

下面的命令是用来验证 /queryresult 目录中emp_add表导入的数据形式。

 $HADOOP_HOME/bin/hadoop fs -cat /queryresult/part-m-*

 

 

它会用逗号(,)分隔emp_add表的数据和字段。

1201, 288A, vgiri,   jublee

1202, 108I, aoc,     sec-bad

1203, 144Z, pgutta,  hyd

1204, 78B,  oldcity, sec-bad

1205, 720C, hitech,  sec-bad

 

 

导入关系表到HIVE

bin/sqoop import --connect jdbc:mysql://hdp-node-01:3306/test --username root --password root --table emp --hive-import --m 1

 

 

 

导入表数据子集

我们可以导入表的使用Sqoop导入工具,"where"子句的一个子集。它执行在各自的数据库服务器相应的SQL查询,并将结果存储在HDFS的目标目录。

where子句的语法如下。

--where <condition>

 

下面的命令用来导入emp_add表数据的子集。子集查询检索员工ID和地址,居住城市为:Secunderabad

bin/sqoop import \

--connect jdbc:mysql://hdp-node-01:3306/test \

--username root \

--password root \

--where "city ='sec-bad'" \

--target-dir /wherequery \

--table emp_add --m 1

 

按需导入

bin/sqoop import \

--connect jdbc:mysql://hdp-node-01:3306/test \

--username root \

--password root \

--target-dir /wherequery2 \

--query 'select id,name,deg from emp WHERE  id>1207 and $CONDITIONS' \

--split-by id \

--fields-terminated-by '\t' \

--m 1

 

 

 

下面的命令用来验证数据从emp_add表导入/wherequery目录

$HADOOP_HOME/bin/hadoop fs -cat /wherequery/part-m-*

 

 

它用逗号(,)分隔 emp_add表数据和字段。

1202, 108I, aoc, sec-bad

1204, 78B, oldcity, sec-bad

1205, 720C, hitech, sec-bad

 

 

 

增量导入

增量导入是仅导入新添加的表中的行的技术。

它需要添加‘incremental’, ‘check-column’, ‘last-value’选项来执行增量导入。

下面的语法用于Sqoop导入命令增量选项。

--incremental <mode>

--check-column <column name>

--last value <last check column value>

 

 

假设新添加的数据转换成emp表如下:

1206, satish p, grp des, 20000, GR

下面的命令用于在EMP表执行增量导入。

bin/sqoop import \

--connect jdbc:mysql://hdp-node-01:3306/test \

--username root \

--password root \

--table emp --m 1 \

--incremental append \

--check-column id \

--last-value 1208

 

 

以下命令用于从emp表导入HDFS emp/ 目录的数据验证。

$ $HADOOP_HOME/bin/hadoop fs -cat /user/hadoop/emp/part-m-*

它用逗号(,)分隔 emp_add表数据和字段。

1201, gopal,    manager, 50000, TP

1202, manisha,  preader, 50000, TP

1203, kalil,    php dev, 30000, AC

1204, prasanth, php dev, 30000, AC

1205, kranthi,  admin,   20000, TP

1206, satish p, grp des, 20000, GR

 

下面的命令是从表emp 用来查看修改或新添加的行

$ $HADOOP_HOME/bin/hadoop fs -cat /emp/part-m-*1

这表示新添加的行用逗号(,)分隔emp表的字段。

1206, satish p, grp des, 20000, GR

 

 

 

3.5 Sqoop的数据导出

1/ 将数据从HDFS把文件导出到RDBMS数据库

导出前,目标表必须存在于目标数据库中。

默认操作是从将文件中的数据使用INSERT语句插入到表中

更新模式是生成UPDATE语句更新表数据

语法

以下是export命令语法。

$ sqoop export (generic-args) (export-args)

 

 

示例

数据是在HDFS “EMP/”目录的emp_data文件中。所述emp_data如下

1201, gopal,     manager, 50000, TP

1202, manisha,   preader, 50000, TP

1203, kalil,     php dev, 30000, AC

1204, prasanth,  php dev, 30000, AC

1205, kranthi,   admin,   20000, TP

1206, satish p,  grp des, 20000, GR

 

1、首先需要手动创建mysql中的目标表

$ mysql

mysql> USE db;

mysql> CREATE TABLE employee (

   id INT NOT NULL PRIMARY KEY,

   name VARCHAR(20),

   deg VARCHAR(20),

   salary INT,

   dept VARCHAR(10));

 

2、然后执行导出命令

bin/sqoop export \

--connect jdbc:mysql://hdp-node-01:3306/test \

--username root \

--password root \

--table employee \

--export-dir /user/hadoop/emp/

 

3验证表mysql命令行。

mysql>select * from employee;

如果给定的数据存储成功,那么可以找到数据在如下的employee表。

+------+--------------+-------------+-------------------+--------+

| Id   | Name         | Designation | Salary            | Dept   |

+------+--------------+-------------+-------------------+--------+

| 1201 | gopal        | manager     | 50000             | TP     |

| 1202 | manisha      | preader     | 50000             | TP     |

| 1203 | kalil        | php dev     | 30000               | AC     |

| 1204 | prasanth     | php dev     | 30000             | AC     |

| 1205 | kranthi      | admin       | 20000             | TP     |

| 1206 | satish p     | grp des     | 20000             | GR     |

+------+--------------+-------------+-------------------+--------+

 

 

 

 

3.6 Sqoop作业

注:Sqoop作业——将事先定义好的数据导入导出任务按照指定流程运行

语法

以下是创建Sqoop作业的语法。

$ sqoop job (generic-args) (job-args)

   [-- [subtool-name] (subtool-args)]

 

$ sqoop-job (generic-args) (job-args)

   [-- [subtool-name] (subtool-args)]

 

 

 

 

创建作业(--create)

在这里,我们创建一个名为myjob,这可以从RDBMS表的数据导入到HDFS作业。

 

bin/sqoop job --create myimportjob -- import --connect jdbc:mysql://hdp-node-01:3306/test --username root --password root --table emp --m 1

命令创建一个从db库的employee表导入到HDFS文件的作业。

 

 

验证作业 (--list)

‘--list’ 参数是用来验证保存的作业。下面的命令用来验证保存Sqoop作业的列表。

$ sqoop job --list

它显示了保存作业列表。

Available jobs:

   myimportjob

检查作业(--show)

‘--show’ 参数用于检查或验证特定的工作,及其详细信息。以下命令和样本输出用来验证一个名为myjob的作业。

$ sqoop job --show myjob

它显示了工具和它们的选择,这是使用在myjob中作业情况。

Job: myjob

 Tool: import Options:

 ----------------------------

 direct.import = true

 codegen.input.delimiters.record = 0

 hdfs.append.dir = false

 db.table = employee

 ...

 incremental.last.value = 1206

 ...

 

 

 

 

执行作业 (--exec)

‘--exec’ 选项用于执行保存的作业。下面的命令用于执行保存的作业称为myjob

$ sqoop job --exec myjob

它会显示下面的输出。

10/08/19 13:08:45 INFO tool.CodeGenTool: Beginning code generation

...

Storm 实时数据流的处理

 

概括:

storm可以实时的提供数据。处理的方式为流方式。

各个组件的介绍:

 

Storm是什么:

 1低延迟:高廷时意味着实时性的缺失。

 2高可用:处理速度快。

 3分布式:可支持分布式的配置,使计算效果大大提高。

 4可扩展:当某一级处理单元的速度不够,可以直接配置并发数,即可现行的扩展性能。

 5、容错性:Storm的守护线程都是无状态的,状态保存在zookeeper中,可以任意重启,当worker生效或者机器出现故障时,storm自动分配新的worker替换失效的worker

 6、数据的不丢失:时丢失的数据减少到最小。

 7提供简单容易理解的接口,便于开发:

 

实时计算与离线计算的区别:

最大的区别在于:

离线计算:一次处理很多条数据

实时计算:一次处理一条数据。

最大的特点在于实时收集,实时计算,实时展示

 

Storm 常用的接口

1、Spout   

2、Bolt

 

相当于mapreducemapreduce

 

Storm 的分组:

1、Shuffer 分组:Task自由的分配,可以保证同一级Bolt上的每一个Task处理的Tuple数据一致。

2、Filed分组:根据Tuple中的某一个Filed或者多个Filed的值进行划分。

3、ALL分组:所有的Tuple都会分发到所有的Task。如图:

 

 

4、Global 分组:整个starem会选择一个Task作为分发地的目的,通常最新的IDTask

 

 

实时平台架构介绍

  当网站或者APP的用户达到一定的用户时,一般需要一套Tracker系统进行手机用户的行为,从而构造出用户画像,以及界面的相应,容错等信息,并把这些信息上报给日志服务器,给开发团队分析这些信息。

 

 

日志服务器也可以通过Flme系统SinkKafka等队列中,供Stome实时处理消息:

 

 

 

 

流式计算整体结构

 

flume用来获取数据

Kafka用来临时保存数据

Strom用来计算数据

Redis是个内存数据库,用来保存数据

 

Storm通信机制 Disruptor

   Storm 使用ZeroMQ与Netty (0.9版本之后默认的使用)作为进程间通信的消息框架,woker之间的内部通信:不同wokerthread通信使用LMAXDisruptor 来完成的。不同topology之间的通信strom不负责,需要自己想办法实现,比如使用工具KAFKAworker执行executor,executor有自己的incoming-queueoutgoing-queue的配置。Disruptor能每秒处理6百万个订单,是一个有界队列。而队列的应用场景自然就是“生产者-消费者”模型。可以实现的原理是环形缓冲区。

   Disruptor 的特点:

     1没有竞争=没有锁=非常快

     2、是一个有限队列

 3所有访问者都记录自己的序号的实现方式,允许多个生产者与多个消费者共享相同的数据结构。

      4在每个对象中都能跟踪序列号(ring bufferclaim Strategy,生产者和消费者),加上神奇的cache line padding,就意味着没有为伪共享和非预期的竞争。

 

 

ACK 的总结

1-1ACK是什么?

 ack 机制是storm整个技术体系中非常闪亮的一个创新点。

 通过Ack机制,spout发送出去的每一条消息,都可以确定是被成功处理或失败处理, 从而可以让开发者采取动作。比如在Meta中,成功被处理,即可更新偏移量,当失败时,重复发送数据。

  另外Ack机制还常用于限流作用: 为了避免spout发送数据太快,而bolt处理太慢,常常设置pending数,当spout有等于或超过pending数的tuple没有收到ackfail响应时,跳过执行nextTuple, 从而限制spout发送数据。

 stromack主要做一些异或的运算,来判断在spoutbout中数据是否处理成功!

 

 

Storm  常见问题总结

1-1)为什么有Storm

     stome解决了不能处理实时数据流的缺点,使数据的信息能及时的展现出来。

1-2Storm有什么特点

    1、容错性

    2、数据不丢失

    3、分布式

    4、低延迟

    5、可扩展

    6、高可用

1-3)离线计算与实时计算的区别

     离线时处理多条(成吨)的数据,而实时处理一行(细细)的数据。

1-4Storm架构中的核心组件

     Sport (数据源)与Bolt(数据操作)

1-5Storm编程模型是什么

    spoutBoltStream Groupings

1-6)为什么有StreamGrouping,常用分组策略

    Streamgrouping控制着在eventTopology中如何流动,或者说成定义一个流在Blot任务间该如何被切分。

    ShuffergroupingFieldsgroupingallgroupingGlobal grouping、None grouping、Direct grouping、CustomStreamGroupimg等分组的策略。

1-7Wordcount中都用到什么技术点

    (我靠这个好难总结啊!!)不搞了、、、

1-8Tuple是什么

     说白了就是收集数据的,不过还的专业点吧!是一个topology中产生数据源的组件,通常情况下会从外部获取数据,然后转换为topology的数据的组件。

1-9Storm的并行度是什么

     说白了就是在物理机上同时能运行多少个Task的实体的单元:

     关系:

     1storm集群里的物理机会启动一个或者多个Woeker,所有的topology都在woker中运行,那么一个woker又会运行一个或者多个executor线程,

     每个executor只会运行一个topology的一个componentdetask的实例

     2、一个task是最终完成数据处理的实体单元

1-10)梳理实时业务指标项目

        (我靠这个不写了,的写好多啊!)

背景:

业务:

架构:

技术点:

 

1-11redis数据结构的运用:

         1. String——字符串

         2Hash——字典

         3. List——列表

         4. Set——集合

         5. Sorted Set——有序集合

1-12redisKey如何设计?

        按照实际需求来设计呗!!(不想回答了,困了、、、、)

1-13)参照文档搭建storm集群

详见下文、、、、、、、、、

安装Strom

[root@hadoop2 storm]# tar -zxvf apache-storm-0.9.6.tar.gz

[root@hadoop2 storm]# cd apache-storm-0.9.6/

[root@hadoop2 apache-storm-0.9.6]# cd conf/

[root@hadoop2 conf]# cp storm.yaml storm.yaml_back

[root@hadoop2 conf]# cat storm.yaml   (配置一下内容)

 

#指定storm使用的zk集群

storm.zookeeper.servers:

     - "zk01"

     - "zk02"

     - "zk03"

#指定storm集群中的nimbus节点所在的服务器

nimbus.host: "hadoop2"

#指定nimbus启动JVM最大可用内存大小

nimbus.childopts: "-Xmx1024m"

#指定supervisor启动JVM最大可用内存大小

supervisor.childopts: "-Xmx1024m"

#指定supervisor节点上,每个worker启动JVM最大可用内存大小

worker.childopts: "-Xmx768m"

#指定ui启动JVM最大可用内存大小,ui服务一般与nimbus同在一个节点上。

ui.childopts: "-Xmx768m"

# supervisor.slots.ports: 对于每个Supervisor工作节点,需要配置该工作节点可以运行的worker数量。每个worker占用一个单独的端口用于接收消息,该配置选项即用于定义哪些端口是可被worker使用的。默认情况下,每个节点上可运行4workers,分别在6700670167026703端口

supervisor.slots.ports:

    - 6700

    - 6701

    - 6702

    - 6703

 

 

 

分布包:

scp -r storm/ hadoop3:/home/

scp -r storm/ hadoop4:/home/

 

分发配置:

scp /etc/profile hadoop3:/etc/profile

scp /etc/profile hadoop4:/etc/profile

 

使配置生效

source   /etc/profile

 

 

[root@hadoop2 bin]# source /etc/profile

[root@hadoop2 bin]# storm  

Commands:

activate

classpath

deactivate

dev-zookeeper

drpc

help

jar

kill

list

localconfvalue

logviewer

monitor

nimbus

rebalance

remoteconfvalue

repl

shell

supervisor

ui

version

 

Help:

help

help <command>

 

Documentation for the storm client can be found at http://storm.incubator.apache.org/documentation/Command-line-client.html

 

Configs can be overridden using one or more -c flags, e.g. "storm list -c nimbus.host=nimbus.mycompany.com"

 

 

[root@hadoop2 bin]# storm  version

0.9.6

 

如上标红的都为主要的进程节点

 

启动集群

最后一步,启动Storm的所有后台进程。和Zookeeper一样,Storm也是快速失败(fail-fast)的系统,这样Storm才能在任意时刻被停止,并且当进程重启后被正确地恢复执行。这也是为什么Storm不在进程内保存状态的原因,即使NimbusSupervisors被重启,运行中的Topologies不会受到影响。

 

以下是启动Storm各个后台进程的方式

1、在hadoop2上启动storm nimbus &

2、在hadoop2上启动storm ui &

3、在其他的机器上启动storm supervisor &

 

说明:nimbus 为管理节点的老大,UI是提供图形化界面的程序,supervisor是当前物理机上的管理者,默认的分配4worker.

 

注意事项:

    启动Storm后台进程时,需要对conf/storm.yaml配置文件中设置的storm.local.dir目录具有写权限。

    Storm后台进程被启动后,将在Storm安装部署目录下的logs/子目录下生成各个进程的日志文件。

    经测试,Storm UI必须和Storm Nimbus部署在同一台机器上,否则UI无法正常工作,因为UI进程会检查本机是否存在Nimbus链接。

为了方便使用,可以将bin/storm加入到系统环境变量中。

至此,Storm集群已经部署、配置完毕,可以向集群提交拓扑运行了。

 

 

查看集群

 

访问hadoop2:8080 即可看到srorm的管理的UI界面,如下所示:

 

 

Storm常用操作命令

   Srome 有很多管理扩普的命令,他们可以提交、杀死、禁用、再平衡扩普

1-1提交任务命令格式

storm jar jar路径】 【拓扑包名.拓扑类名】 【拓扑名称】bin/storm jar examples/storm-starter/storm-starter-topologies-0.10.0.jar storm.starter.WordCountTopology wordcount

  

1-2杀死任务命令格式

storm kill 【拓扑名称】 -w 10(执行kill命令时可以通过-w [等待秒数]指定拓扑停用以后的等待时间)storm kill topology-name -w 10

 

1-3停用任务命令格式

  

torm deactivte  【拓扑名称】storm deactivte topology-name

我们能够挂起或停用运行中的拓扑。当停用拓扑时,所有已分发的元组都会得到处理,但是spoutsnextTuple方法不会被调用。销毁一个拓扑,可以使用kill命令。它会以一种安全的方式销毁一个拓扑,首先停用拓扑,在等待拓扑消息的时间段内允许拓扑完成当前的数据流。

 

1-4启用任务命令格式

storm activate【拓扑名称】

storm activate topology-name

 

1-5重新部署任务命令格式

 storm rebalance  【拓扑名称】

 storm rebalance topology-name

  再平衡使你重分配集群任务。这是个很强大的命令。比如,你向一个运行中的集群增加了节点。再平衡命令将会停用拓扑,然后在相应超时时间之后重分配worker,并重启拓扑。

 

1-6SpoutBolt的生命周期

    Spout 的生命周期:

 

    

 

 

Bolt的生命周期:

   

 

 

1-7StreamGrouping源码解析

  public Map<List<Integer>, List<MsgInfo>> grouperBatch(List<MsgInfo> batch) {
    Map<List<Integer>, List<MsgInfo>> ret = newHashMap<List<Integer>, List<MsgInfo>>();
    //optimize fieldGrouping & customGrouping
    
if(GrouperType.local_or_shuffle.equals(grouptype)) {
       ret.put(local_shuffer_grouper.grouper(null), batch);
    }  else if (GrouperType.global.equals(grouptype)) {
        // send to task which taskId is 0
        
ret.put(JStormUtils.mk_list(out_tasks.get(0)), batch);
    } else if (GrouperType.fields.equals(grouptype)) {
        fields_grouper.batchGrouper(batch, ret);
    } else if (GrouperType.all.equals(grouptype)) {
        // send to every task
        
ret.put(out_tasks, batch);
    } else if (GrouperType.shuffle.equals(grouptype)) {
        // random, but the random is different from none
        
ret.put(shuffer.grouper(null), batch);
    } else if (GrouperType.none.equals(grouptype)) {
        int rnd = Math.abs(random.nextInt() %out_tasks.size());
        ret.put(JStormUtils.mk_list(out_tasks.get(rnd)), batch);
    } else if (GrouperType.custom_obj.equals(grouptype) || GrouperType.custom_serialized.equals(grouptype)) {
        for (inti =0; i < batch.size(); i++ ) {
            MsgInfo msg = batch.get(i);
            List<Integer> out = custom_grouper.grouper(msg.values);
            List<MsgInfo> customBatch = ret.get(out);
            if (customBatch ==null) {
                customBatch = JStormUtils.mk_list();
                ret.put(out, customBatch);
            }
            customBatch.add(msg);
        }
    } else if (GrouperType.localFirst.equals(grouptype)) {
        ret.put(localFirst.grouper(null), batch);
    } else {
        LOG.warn("Unsupportted group type");
    }
    return ret;
}

 

 

 

注意:StreamGrouping会去匹配用的什么分组类型再去执行用户指定的类型,也可以看出用了什么分组:

  

所有的分组:

/**

 * Licensed to the Apache Software Foundation (ASF) under one

 * or more contributor license agreements.  See the NOTICE file

 * distributed with this work for additional information

 * regarding copyright ownership.  The ASF licenses this file

 * to you under the Apache License, Version 2.0 (the

 * "License"); you may not use this file except in compliance

 * with the License.  You may obtain a copy of the License at

 *

 * http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

package backtype.storm.topology;

 

import backtype.storm.generated.GlobalStreamId;

import backtype.storm.generated.Grouping;

import backtype.storm.grouping.CustomStreamGrouping;

import backtype.storm.tuple.Fields;

 

 

public interface InputDeclarer<T extends InputDeclarer> {

    public T fieldsGrouping(String componentId, Fields fields);

    public T fieldsGrouping(String componentId, String streamId, Fields fields);

 

    public T globalGrouping(String componentId);

    public T globalGrouping(String componentId, String streamId);

 

    public T shuffleGrouping(String componentId);

    public T shuffleGrouping(String componentId, String streamId);

 

    public T localOrShuffleGrouping(String componentId);

    public T localOrShuffleGrouping(String componentId, String streamId);

 

    public T noneGrouping(String componentId);

    public T noneGrouping(String componentId, String streamId);

 

    public T allGrouping(String componentId);

    public T allGrouping(String componentId, String streamId);

 

    public T directGrouping(String componentId);

    public T directGrouping(String componentId, String streamId);

 

    public T customGrouping(String componentId, CustomStreamGrouping grouping);

    public T customGrouping(String componentId, String streamId, CustomStreamGrouping grouping);

     public T grouping(GlobalStreamId id, Grouping grouping);

 

}

 

 

Storm 组件本地目录树

 

Strom  zookeeper目录树

 

 

 

wordcounter单词计数器的设计思路:

 

  

 

代码实现:

 

 

 

 

package cn.****.bigdata.wc;

 

import backtype.storm.Config;

import backtype.storm.LocalCluster;

import backtype.storm.StormSubmitter;

import backtype.storm.topology.TopologyBuilder;

import backtype.storm.tuple.Fields;

 

/**

 * 功能说明:

 * 设计一个topology,来实现对一个句子里面的单词出现的频率进行统计。

 * 整个topology分为三个部分:

 * RandomSentenceSpout:数据源,在已知的英文句子中,随机发送一条句子出去。

 * SplitSentenceBolt:负责将单行文本记录(句子)切分成单词

 * WordCountBolt:负责对单词的频率进行累加

 */

public class WordCountTopologyMain {

    public static void main(String[] args) throws Exception {

        // Storm框架支持多语言,在JAVA环境下创建一个拓扑,需要使用TopologyBuilder进行构建

        TopologyBuilder builder = new TopologyBuilder();

 

        //RandomSentenceSpout类,在已知的英文句子中,随机发送一条句子出去。

        builder.setSpout("spout1", new RandomSentenceSpout(), 3);

 

        // SplitSentenceBolt类,主要是将一行一行的文本内容切割成单词

        builder.setBolt("split1", new SplitSentenceBolt(), 9).shuffleGrouping("spout1");

        // WordCountBolt类,对单词出现的次数进行统计

//        builder.setBolt("count2", new WordCountBolt(),2).fieldsGrouping("split1",new Fields("word"));

        builder.setBolt("count2", new WordCountBolt(), 3).shuffleGrouping("split1");

 

        //启动topology的配置信息

        Config conf = new Config();

 

        //TOPOLOGY_DEBUG(setDebug), 当它被设置成true的话,storm会记录下每个组件所发射的每条消息。

        //这在本地环境调试topology很有用, 但是在线上这么做的话会影响性能的。

//        conf.setDebug(true);

        conf.setDebug(false);

        //storm的运行有两种模式:本地模式和分布式模式.

        if (args != null && args.length > 0) {

            //定义你希望集群分配多少个工作进程给你来执行这个topology

            conf.setNumWorkers(3);

            //向集群提交topology

            StormSubmitter.submitTopologyWithProgressBar(args[0], conf, builder.createTopology());

        } else {

            conf.setMaxTaskParallelism(3);

            LocalCluster cluster = new LocalCluster();

            cluster.submitTopology("word-count", conf, builder.createTopology());

            //指定本地模式运行多长时间之后停止,如果不显式的关系程序将一直运行下去

            //Utils.sleep(10000);

            //cluster.shutdown();

        }

    }

}

 

 

 

 

 

package cn.****.bigdata.wc;

 

import backtype.storm.spout.SpoutOutputCollector;

import backtype.storm.task.TopologyContext;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseRichSpout;

import backtype.storm.tuple.Fields;

import backtype.storm.tuple.Values;

 

import java.util.Map;

import java.util.Random;

 

/**

 * 功能说明

 * 伪造数据源,在storm框架调用nextTuple()方法时,发送英文句子出去。

 */

public class RandomSentenceSpout extends BaseRichSpout {

    private static final long serialVersionUID = 5028304756439810609L;

    //用来收集Spout输出的tuple

    SpoutOutputCollector collector;

    Random rand;

 

    //该方法调用一次,主要由storm框架传入SpoutOutputCollector

    public void open(Map conf, TopologyContext context,

                     SpoutOutputCollector collector) {

        this.collector = collector;

        rand = new Random();

    }

 

    /**

     * 上帝之手,数据之源,strom会不断的调用次函数,用户只要在此生成元数据即可

     * while(true){

     * spout.nexTuple()

     * }

     */

    public void nextTuple() {

        String[] sentences = new String[]{"the cow jumped over the moon",

                "the cow jumped over the moon",

                "the cow jumped over the moon",

                "the cow jumped over the moon", "the cow jumped over the moon"};

        String sentence = sentences[rand.nextInt(sentences.length)];

        collector.emit(new Values(sentence));

// System.out.println("RandomSentenceSpout 发送数据:"+sentence);

    }

 

    //消息源可以发射多条消息流stream多条消息流可以理解为多中类型的数据。

    public void declareOutputFields(OutputFieldsDeclarer declarer) {

 

        declarer.declare(new Fields("sentence"));

    }

 

}

 

 

package cn.****.bigdata.wc;

 

import backtype.storm.task.TopologyContext;

import backtype.storm.topology.BasicOutputCollector;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseBasicBolt;

import backtype.storm.tuple.Fields;

import backtype.storm.tuple.Tuple;

import backtype.storm.tuple.Values;

 

import java.util.Map;

 

public class SplitSentenceBolt extends BaseBasicBolt {

    private static final long serialVersionUID = -5283595260540124273L;

 

    //该方法只会被调用一次,用来初始化

    public void prepare(Map stormConf, TopologyContext context) {

 

        super.prepare(stormConf, context);

    }

 

    /**

     * 接受的参数是RandomSentenceSpout发出的句子,即input的内容是句子execute方法,将句子切割形成的单词发出

     */

    public void execute(Tuple input, BasicOutputCollector collector) {

        String sentence = (String) input.getValueByField("sentence");

        String[] words = sentence.split(" ");

        for (String word : words) {

            word = word.trim();

            if (!word.isEmpty()) {

                // 接受的单词并并转换为小写

                word = word.toLowerCase();

// System.out.println("SplitSentenceBolt 切割单词:"+word);

                // 发出数据

                collector.emit(new Values(word, 1));

            }

        }

    }

 

    //消息源可以发射多条消息流stream。多条消息流可以理解为多种类型的数据。

    public void declareOutputFields(OutputFieldsDeclarer declarer) {

 

        declarer.declare(new Fields("word", "num"));

    }

}

 

 

 

package cn.****.bigdata.wc;

 

import backtype.storm.task.TopologyContext;

import backtype.storm.topology.BasicOutputCollector;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseBasicBolt;

import backtype.storm.tuple.Tuple;

 

import java.util.HashMap;

import java.util.Map;

 

public class WordCountBolt extends BaseBasicBolt {

    private static final long serialVersionUID = 5678586644899822142L;

    // 用来保存最后计算的结果key=单词,value=单词个数

    Map<String, Integer> counters = new HashMap<String, Integer>();

 

    //该方法只会被调用一次,用来初始化

    public void prepare(Map stormConf, TopologyContext context) {

        super.prepare(stormConf, context);

    }

 

    /*

     * collector中的元素存放在成员变量countersMap)中.

     * 如果countersMap)中已经存在该元素,getValule并对Value进行累加操作。

     */

    public void execute(Tuple input, BasicOutputCollector collector) {

        String str = (String) input.getValueByField("word");

        Integer num = input.getIntegerByField("num");

        input.getValue(0);

        //三种方式获取数据

//        input.getValue(0);

//        input.getString(0);

//        input.getValueByField("word");

 

        System.out.println("----------------" + Thread.currentThread().getId() + "     " + str);

//        if (!counters.containsKey(str)) {

//            counters.put(str, num);

//        } else {

//            Integer c = counters.get(str) + num;

//            counters.put(str, c);

//        }

//        System.out.println("WordCountBolt 统计单词:"+counters);

    }

 

    public void declareOutputFields(OutputFieldsDeclarer declarer) {

        // TODO Auto-generated method stub

    }

}

 

 

图示总结

 

 

 

 

 

 

Kafka 学习总结

 

 

问题总结:

1、 kafka是什么?

2、 JMS规范是什么?

3、 为什么需要消息队列?

4、 Kafka核心组件

5、 Kafka安装部署

6、 Kafka生产者Java API

7、 Kafka消费者Java API

 

概述  

     在流式计算中,Kafka一般用来缓存数据,Storm通过消费Kafka的数据进行计算。

Kafka是一个分布式的消息队列,包含生产者消费之的功能,提供了类似于JMS

特性,但是在设计实现上完全不同,此外他并不是JMS范围的实现。

 

 

JMS 的概念

  JMS (java   messger  server )的缩写,即使Java提供的一套技术规范,主要做数据的集成,通信,提高系统的伸缩性,提高用户的体验,是系统之间的模块更加灵活以及方便。

通过什么方式:生产消费者模式(生产者、服务器、消费者)

 

jdkkafkaactivemq……

 

 

JMS消息传输模型

 点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)

发布/订阅模式(一对多,数据生产后,推送给所有订阅者)

 

 

JMS核心组件

Destination:消息发送的目的地,也就是前面说的QueueTopic

Message :从字面上就可以看出是被发送的消息。

Producer: 消息的生产者,要发送一个消息,必须通过这个生产者来发送。

MessageConsumer: 与生产者相对应,这是消息的消费者或接收者,通过它来接收一个消息。

 

 

 

 

:treamMessageJava数据流消息,用标准流操作来顺序的填充和读取。

MapMessage:一个Map类型的消息;名称为string类型,而值为Java的基本类型。

TextMessage:普通字符串消息,包含一个String

ObjectMessage:对象消息,包含一个可序列化的Java对象

BytesMessage:二进制数组消息,包含一个byte[]

XMLMessage:  一个XML类型的消息。

最常用的是TextMessageObjectMessage

 

常见的类JMS消息服务器

1、JMS消息服务器 ActiveMQ

2、分布式消息中间件 Metamorphosis

3、分布式消息中间件 RocketMQ

4其他MQ

 

为什么需要消息队列(重要)

消息系统的核心作用就是三点:解耦,异步和并行

以用户注册的案列来说明消息系统的作用

Kafka名词解释和工作方式

l Producer :消息生产者,就是向kafka broker发消息的客户端。

l Consumer :消息消费者,向kafka broker取消息的客户端

l Topic 咱们可以理解为一个队列。

l Consumer Group CG):这是kafka用来实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)的手段。一个topic可以有多个CGtopic的消息会复制(不是真的复制,是概念上的)到所有的CG,但每个partion只会把消息发给该CG中的一个consumer。如果需要实现广播,只要每个consumer有一个独立的CG就可以了。要实现单播只要所有的consumer在同一个CG。用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic

l Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic

l Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的idoffset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序。

l Offsetkafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafka

 

 

Kafka 的核心组件

l Topic :消息根据Topic进行归类

l Producer:发送消息者

l Consumer:消息接受者

l broker:每个kafka实例(server)

l Zookeeper:依赖集群保存meta信息。

 

 

     

Kafka集群部署

官网:http://kafka.apache.org/downloads.html

 

wget http://mirrors.hust.edu.cn/apache/kafka/0.8.2.2/kafka_2.11-0.8.2.2.tgz

 

1-1)环境准备

 [root@hadoop2 kafka]# chmod a+x kafka_2.11-0.9.0.1.tgz

 [root@hadoop2 kafka]# tar -zxvf kafka_2.11-0.9.0.1.tgz

  

 

1-2)配置文件

配置 server.properties

 

# Licensed to the Apache Software Foundation (ASF) under one or more

# contributor license agreements.  See the NOTICE file distributed with

# this work for additional information regarding copyright ownership.

# The ASF licenses this file to You under the Apache License, Version 2.0

# (the "License"); you may not use this file except in compliance with

# the License.  You may obtain a copy of the License at

#

#    http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.

# see kafka.server.KafkaConfig for additional details and defaults

 

############################# Server Basics #############################

 

# The id of the broker. This must be set to a unique integer for each broker.

# broker的全局唯一编号,不能重复

broker.id=0

 

############################# Socket Server Settings #############################

 

listeners=PLAINTEXT://:9092

 

# The port the socket server listens on

# 用来监听链接的端口,producerconsumer将在此端口建立连接

port=9092

 

# Hostname the broker will bind to. If not set, the server will bind to all interfaces

# host.name=localhost

 

# Hostname the broker will advertise to producers and consumers. If not set, it uses the

# value for "host.name" if configured.  Otherwise, it will use the value returned from

# java.net.InetAddress.getCanonicalHostName().

#advertised.host.name=<hostname routable by clients>

 

# The port to publish to ZooKeeper for clients to use. If this is not set,

# it will publish the same port that the broker binds to.

#advertised.port=<port accessible by clients>

 

# The number of threads handling network requests -->  处理网络请求的线程数量

num.network.threads=3

 

# The number of threads doing disk I/O --> 用来处理磁盘IO的现成数量

num.io.threads=8

 

# The send buffer (SO_SNDBUF) used by the socket server  --> 发送套接字的缓冲区大小

socket.send.buffer.bytes=102400

 

# The receive buffer (SO_RCVBUF) used by the socket server -->  接受套接字的缓冲区大小

socket.receive.buffer.bytes=102400

 

# The maximum size of a request that the socket server will accept (protection against OOM) -->请求套接字的缓冲区大小

socket.request.max.bytes=104857600

 

 

############################# Log Basics #############################

 

# A comma seperated list of directories under which to store log files --> kafka运行日志存放的路径

log.dirs=/home/kafka/kafka

 

# The default number of log partitions per topic. More partitions allow greater

# parallelism for consumption, but this will also result in more files across

# the brokers.   --> topic在当前broker上的分片个数

num.partitions=1

 

# The number of threads per data directory to be used for log recovery at startup and flushing at shutdown.

# This value is recommended to be increased for installations with data dirs located in RAID array.   -->  用来恢复和清理data下数据的线程数量

num.recovery.threads.per.data.dir=1

 

############################# Log Flush Policy #############################

 

# Messages are immediately written to the filesystem but by default we only fsync() to sync

# the OS cache lazily. The following configurations control the flush of data to disk.

# There are a few important trade-offs here:

#    1. Durability: Unflushed data may be lost if you are not using replication.

#    2. Latency: Very large flush intervals may lead to latency spikes when the flush does occur as there will be a lot of data to flush.

#    3. Throughput: The flush is generally the most expensive operation, and a small flush interval may lead to exceessive seeks.

# The settings below allow one to configure the flush policy to flush data after a period of time or

# every N messages (or both). This can be done globally and overridden on a per-topic basis.

 

# The number of messages to accept before forcing a flush of data to disk

#log.flush.interval.messages=10000

 

# The maximum amount of time a message can sit in a log before we force a flush

#log.flush.interval.ms=1000

 

############################# Log Retention Policy #############################

 

# The following configurations control the disposal of log segments. The policy can

# be set to delete segments after a period of time, or after a given size has accumulated.

# A segment will be deleted whenever *either* of these criteria are met. Deletion always happens

# from the end of the log.

 

# The minimum age of a log file to be eligible for deletion  --> segment文件保留的最长时间,超时将被删除

log.retention.hours=168

 

# A size-based retention policy for logs. Segments are pruned from the log as long as the remaining

# segments don't drop below log.retention.bytes.   -->  日志文件中每个segment的大小,默认为1G

log.retention.bytes=1073741824

 

# The maximum size of a log segment file. When this size is reached a new log segment will be created.

log.segment.bytes=1073741824

 

# The interval at which log segments are checked to see if they can be deleted according

# to the retention policies  --> 周期性检查文件大小的时间

log.retention.check.interval.ms=300000

 

############################# Zookeeper #############################

 

# Zookeeper connection string (see zookeeper docs for details).

# This is a comma separated host:port pairs, each corresponding to a zk

# server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002".

# You can also append an optional chroot string to the urls to specify the

# root directory for all kafka znodes.  --> broker需要使用zookeeper保存meta数据

zookeeper.connect=hadoop2:2888,hadoop3:2888,hadoop4:2888

 

# artion buffer中,消息的条数达到阈值,将触发flush到磁盘

log.flush.interval.messages=10000

 

# buffer的时间,达到阈值,将触发flush到磁盘

log.flush.interval.ms=3000

 

# topic需要server.properties中设置delete.topic.enable=true否则只是标记删除

delete.topic.enable=true

 

# 处的host.name为本机IP(重要),如果不改,则客户端会抛出:Producer connection to localhost:9092 unsuccessful 错误!

host.name=hadoop2

 

# Timeout in ms for connecting to zookeeper --> zookeeper链接超时时间

zookeeper.connection.timeout.ms=6000

 

 

 

 

 配置  consumer.properties

 

[root@hadoop2 config]# vi consumer.properties

 

# Licensed to the Apache Software Foundation (ASF) under one or more

# contributor license agreements.  See the NOTICE file distributed with

# this work for additional information regarding copyright ownership.

# The ASF licenses this file to You under the Apache License, Version 2.0

# (the "License"); you may not use this file except in compliance with

# the License.  You may obtain a copy of the License at

#

#    http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 

# 指定多久消费者更新offsetzookeeper中。注意offset更新时基于time而不是每次获得的消息。一旦在更新zookeeper发生异常并重启,将可能拿到已拿到过的消息

zookeeper.sync.time.ms=2000

 

# 指定消费

group.id=consumerCourse

 

# consumer消费一定量的消息之后,将会自动向zookeeper提交offset信息,注意offset信息并不是每消费一次消息就向zk提交一次,而是现在本地保存(内存),并定期提交,默认为true

auto.commit.enable=true

 

# 自动更新时间。默认60 * 1000

auto.commit.interval.ms=1000

 

# 当前consumer的标识,可以设定,也可以有系统生成,主要用来跟踪消息消费情况,便于观察,如果不设置则会自增

conusmer.id

 

# 消费者客户端编号,用于区分不同客户端,默认客户端程序自动产生,最好与group.idvalue值相同

client.id=consumerCourse

# 最大取多少块缓存到消费者(默认10)

queued.max.message.chunks=50

 

# 当有新的consumer加入到group,将会reblance,此后将会有partitions的消费端迁移到新  的consumer,如果一个consumer获得了某个partition的消费权限,那么它将会向zk注册"Partition Owner registry"节点信>,但是有可能此时旧的consumer尚没有释放此节点,此值用于控制,注册节点的重试次数.

rebalance.max.retries=5

 

# 获取消息的最大尺寸,broker不会像consumer输出大于此值的消息chunk每次feth将得到多条消息,此值为总大小,提升此值,将会消耗更多的consumer端内存

fetch.min.bytes=6553600

 

# 当消息的尺寸不足时,server阻塞的时间,如果超时,消息将立即发送给consumer

fetch.wait.max.ms=5000

socket.receive.buffer.bytes=655360

 

# 如果zookeeper没有offset值或offset值超出范围。那么就给个初始的offset。有smallestlargestanything可选,分别表示给当前最小的offset、当前最大的offset、抛异常。默认largest

auto.offset.reset=smallest

 

# 指定序列化处理类

derializer.class=kafka.serializer.DefaultDecoder

 

 

 

配置 producer.properties

 

[root@hadoop2 config]# vi producer.properties

 

# Licensed to the Apache Software Foundation (ASF) under one or more

# contributor license agreements.  See the NOTICE file distributed with

# this work for additional information regarding copyright ownership.

# The ASF licenses this file to You under the Apache License, Version 2.0

# (the "License"); you may not use this file except in compliance with

# the License.  You may obtain a copy of the License at

#

#    http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.

# see kafka.producer.ProducerConfig for more details

 

############################# Producer Basics #############################

 

# list of brokers used for bootstrapping knowledge about the rest of the cluster

# format: host1:port1,host2:port2 ...  -->  指定kafka节点列表,用于获取metadata,不必全部指定

metadata.broker.list=hadoop2:2888,hadoop3:2888,hadoop4:2888

 

# name of the partitioner class for partitioning events; default partition spreads data randomly -->指定分区处理类。默认kafka.producer.DefaultPartitioner,表通过key哈希到对应分区

#partitioner.class=kafka.producer.DefaultPartitioner

 

# specifies whether the messages are sent asynchronously (async) or synchronously (sync)

# 同步还是异步发送消息,默认“sync”表同步,"async"表异步。异步可以提高发送吞吐量,

# 也意味着消息将会在本地buffer,并适时批量发送,但是也可能导致丢失未发送过去的消息

producer.type=sync

# 是否压缩,默认0表示不压缩,1表示用gzip压缩,2表示用snappy压缩。压缩后消息中会有头来指明消息压缩类型,故在消费者端消息解压是透明的无需指定。

compression.codec=none

 

# message encoder   --> 序列化的类

serializer.class=kafka.serializer.DefaultEncoder

 

# allow topic level compression

#compressed.topics=

 

############################# Async Producer #############################

# maximum time, in milliseconds, for buffering data on the producer queue

# async模式下,message被缓存的时间超过此值后,将会批量发送给broker,默认为5000ms

# 此值和batch.num.messages协同工作.

queue.buffering.max.ms=5000

 

# the maximum size of the blocking queue for buffering on the producer

# async模式下,producer端允许buffer的最大消息量

# 无论如何,producer都无法尽快的将消息发送给broker,从而导致消息在producer端大量沉积

# 此时,如果消息的条数达到阀值,将会导致producer端阻塞或者消息被抛弃,默认为10000

queue.buffering.max.messages=20000

 

# Timeout for event enqueue:

# 0: events will be enqueued immediately or dropped if the queue is full

# -ve: enqueue will block indefinitely if the queue is full

# +ve: enqueue will block up to this many milliseconds if the queue is full

# 当消息在producer端沉积的条数达到"queue.buffering.max.meesages"后,阻塞一定时间后,队列仍然没有enqueue(producer仍然没有发送出任何消息)

# 此时producer可以继续阻塞或者将消息抛弃,timeout值用于控制"阻塞"的时间 (-1: 无阻塞超时限制,消息不会被抛弃 ,0:立即清空队列,消息被抛弃)

queue.enqueue.timeout.ms=-1

 

# the number of messages batched at the producer    --> 如果是异步,指定每次批量发送数据量,默认为20

batch.num.messages=500

 

# producer接收到error ACK,或者没有接收到ACK,允许消息重发的次数

# 因为broker并没有完整的机制来避免消息重复,所以当网络异常时(比如ACK丢失)

# 有可能导致broker接收到重复的消息,默认值为3.

message.send.max.retries=3

 

# producer刷新topic metada的时间间隔,producer需要知道partition leader的位置,以及当前topic的情况

# 因此producer需要一个机制来获取最新的metadata,producer遇到特定错误时,将会立即刷新

# (比如topic失效,partition丢失,leader失效等),此外也可以通过此参数来配置额外的刷新机制,默认值600000

topic.metadata.refresh.interval.ms=60000

 

 

 

注意依次修改各server.properties服务器上配置文件的的broker.id,分别是0,1,2不得重复。


1-3) 启动

配置环境变量:

  export KAFKA_HOME=/home/kafka

  export PATH=$PATH:$HABASE_HOME/bin:$STROM_HOME/bin:$KAFKA_HOME/bin

  

依次在各节点上启动kafka   启动命令为:kafka-server-start.sh  config/server.properties

 

前台启动:

kafka-server-start.sh  config/server.properties

 

后台启动:

kafka-server-start.sh  config/server.properties  > /dev/null 2>&1  &

 

查看进程:

 

[root@hadoop2 bin]# jps

4018 Jps

3968 Kafka

3346 QuorumPeerMain

3498 -- process information unavailable

Kafka常用操作命令

1、查看当前服务器中的所有topic

 kafka-topics.sh --list --zookeeper  hadoop2:2888

 

2创建topic

kafka-topics.sh --create --zookeeper hadoop2:2888 --replication-factor 1 --partitions 1 --topic test

 

3、删除topic

kafka-topics.sh --delete --zookeeper hadoop2:2888  --topic test

需要server.properties中设置delete.topic.enable=true否则只是标记删除或者直接重启。

 

4通过shell命令发送消息

kafka-console-producer.sh --broker-list hadoop2:9092 --topic test1

 

5、通过shell消费消息

kafka-console-consumer.sh --zookeeper hadoop2:2888 --from-beginning --topic test1

 

 

6查看消费位置

kafka-run-class.sh kafka.tools.ConsumerOffsetChecker --zookeeper hadoop2:2888 --group testGroup

 

 

7查看某个Topic的详情

kafka-topics.sh --topic test --describe --zookeeper hadoop:2888

 

 

8对分区数进行修改

kafka-topics.sh --zookeeper  hadoop2 --alter --partitions 15 --topic   utopic

 

 

 

 

 

Kafka流程详解

 

 

 

详解:

         

 

Scala总结

 

 

凡事豫则立,不豫则废;言前定,则不跲;事前定,则不困。 — 《礼记·中庸》

 

概述

听说scala是一门神一样的语言,接下来咱们也来了解一下这门神奇的语言。

 

环境安装

1 安装JDK

   运行scala需要JDK的环境,所以在安装scala之前需要安装JDK的环境。

 

2 安装scala

官网:http://www.scala-lang.org/download/

下载地址:wget  http://downloads.typesafe.com/scala/2.11.5/scala-2.11.5.tgz

chmod   a+x  scala-2.11.5.tgz

Tar   -zxvf  scala-2.11.5.tgz

配置环境变量:vi  /etc/profile  

export SCALA_HOME=/home/scala/scala-2.10.6/  

$SCALA_HOME/bin

source  /etc/profile

 

3 运行

[root@hadoop1 bin]# scala

Welcome to Scala version 2.10.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_76).

Type in expressions to have them evaluated.

Type :help for more information.

 

scala> println("hellowd");

hellowd

scala>

 

scala 基础

1、声明变量

      // 整形变量

    var intType =1;

    // 字符型变量

    var stringType ="hellword";

    // 字符型制定类型,这样可以避免拆包与封包的麻烦

    var stringType2: String ="";

    // byte的类型

    var byteType ='a';

    //  char的类型

    var char ='a';

    // Short的类型

    var short =10L;

    // Long类型

    var Long =10L;

    // Float类型

    var float =10.1;

    // 还有double与boolean类型

 

 

2 条件表达式

 var index =10;

    if (index >20) {

      print("a");

    }else{

      print("b");

}

 

 

 

var value =if (index >20) {

      "xiaozhang";

    } else {

      80;

    }

 

 

3块表达式

 

 var indexValue = {

      if (index >5) {

        "这个数已经大于5啦!!";

      } else if (index <3) {

        index

      } else {

        "这个是什么也没符合的数!!";

      }

    }

  println(indexValue)

 

 

4 循环

  // Array 的迭代

    var vls = Array("34","43","343","43");

    for (y <-vls) {

      println("y:" + y);

    }

 

    //  可以把两套的循环放到一个for里面,与JAVA的for一样

    for (i <-1 to3;y <- 1 to5) {

      println(i + "-" + y)

    }

 

    // yield :表示循环体一yield开始

    val ind =for (i <-1 to10)yield i *10;

    println(ind);

 

Vector(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)

调用方法和函数

Scala 中的操作符包括:+/-/*/除/ % 运算与JAVA的运算一样,位运算& | ^ >> <<也一样,只不过有一点特,只不过这些运算符是方法.例如:

a + b

是如下方法调用的简写:

a.+(b)

a 方法 b可以写成 a.方法(b)

 

6定义方法和函数

定义方法:

 

 

 

方法体是可以不写的,回自动的推断出来,但是对于递归的方法来说是必须要写的。

 

6-1) 定义函数

 

 def de = (x: Int, y: Int) => x + y

println("方法的定义:" + de(2,4))

 

 

6-2 ) 方法和函数的区别

在函数式编程语言中,函数是“头等公民”,它可以像任何其他数据类型一样被传递和操作

实例:

 // 两个函数

    var f1 = (x: Int, y: Int) => x * y;

    var f2 = (x: Int, y: Int) => x + y;

 

    var m2 = m1(f1);

    println(m2)

 

 

 

6-3)将方法转换成函数(神奇的下划线)

 

 

// m1的方法

def m1(f: (Int, Int) => Int): Int = {

      f(2, 6)

}

var m3 = m1 _

println(m3)

 

<function1>

 

 

7 数组、映射、元组、集合

println("----------------------")

var  arr1 = Array(1,2,445);

println(arr1.toBuffer)

 

 

ArrayBuffer(1, 2, 445)

 

 

println(arr1(1));

2

 

 

 

val ab = ArrayBuffer[Int](6);

println(ab.toBuffer)   =>  ArrayBuffer(6)

 

 

 val arr3 = Array("hadoop","storm","spark")

 println(arr3.toBuffer)

 println(arr3(2))

 

 

ArrayBuffer(hadoop, storm, spark)

Spark

 

 

7-1 )遍历数组

val arr = Array(1,2,3,4, 5, 6, 7, 8)

  for (x <-arr) {

    print(x)

 }

 

12345678

 

 

 for (i <- (0 untilarr.length).reverse){

   print(arr(i))

}

 

87654321

 

 

7-2 )数组转换

yield关键字将原始的数组进行转换会产生一个新的数组,原始的数组不变

 

val arrq = Array(1,2,3,4, 5, 6, 7, 8, 9)

    val res =for (e <-arrq if e %2 == 0) yield e *10

println(res.toBuffer)

 

ArrayBuffer(20, 40, 60, 80)

 

 

val r =arrq.filter(_ %2 ==0).map(_ * 10)

println(r.toBuffer)

ArrayBuffer(20, 40, 60, 80)

 

8  数组常用算法

Scala中,数组上的某些方法对数组进行相应的操作非常方便!

val arrq = Array(1,2,3,4, 5, 6, 7, 8, 9,455,3,2,43546,3433)

println(arrq.sum)

println(arrq.max)

println(arrq.sorted.toList)

 

47484

43546

List(1, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 455, 3433, 43546)

 

 

8-1) 映射

  //  映射的操作

    var qq = Map("df" -> 32, "s" -> "ff", "34" -> 10.2)

    println(qq.toBuffer)

    var jk = qq.get("df");

    println(jk);

    var ss = qq.getOrElse("sdsdsds", "哈哈哈,没找到我啊!!!!");

println(ss)

 

 

ArrayBuffer((df,32), (s,ff), (34,10.2))

Some(32)

哈哈哈,没找到我啊!!!!

 

 

9  元组

 var t, (s,f,gs) = (2,3,5)

    println(t._1)

println(t._3)

 

2

5

 

 

10 序列

10 -1 ) List

//创建一个不可变的集合

    val lst1 = List(1,2,3)

    //将0插入到lst1的前面生成一个新的List

    val lst2 =0 ::lst1

    val lst3 =lst1.::(0)

    val lst4 =0 +:lst1

val lst5 =lst1.+:(0)

 

 

 

val lst0 = ListBuffer[Int](1,2,3);

    val lstt =new ListBuffer[Int]

    lst0.append(4);

println(lst0)

 

 

ListBuffer(1, 2, 3, 4)

 

 

 

10 -2 ) Set

  // set 的设置

    val set1 =new HashSet[Int]();

    val set2 =set1 +4;

    println("set2:" + set2);

    var set3 =set2 ++Set(3,5,7);

println("set3:" + set3.toBuffer.sorted)

println("set3.getClass():" + set3.getClass());

 

 

set2:Set(4)

set3:ArrayBuffer(3, 4, 5, 7)

set3.getClass():class scala.collection.mutable.HashSet

11  Map

 // map

    val map1 =new mutable.HashMap[String, Int]()

    //向map中添加数据

    map1.put("hadoop",1);

    map1.put("scala",3);

 

    map1 -= "spark"

    map1.remove("hadoop")

println(map1)

 

 

Map(scala -> 3)

 

 

 

小结

 //  list 的使用

    val list0 = List(1,2,3,4, 5, 6, 7, 8, 9, 0);

    println(list0.toBuffer)

    println(list0.toBuffer.sorted)

    println(list0.toBuffer.sorted.reverse)

    println(list0.toBuffer.sorted.reverse.grouped(4).toList)

    println(list0.toBuffer.sorted.reverse.grouped(4).toList.flatten)

    println(list0.toBuffer.sorted.reverse.grouped(4).toList.flatMap(_.map(_ *10)));

    println("-------------------------------------")

 

 

    // heoolword

    val lines = Array("hellow hell","xiaozhang xiaowang","xiao zhang", "da xiao budabuxiao","xiao xiao xiao xiao");

    println(lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).map(t => (t._1,t._2.size)).toList.sortBy(_._2).toList);

println("-------------------------------------")

 

// flatMap的使用

    var llss = Array("wd vw d ew v","ee ede eee fv de s","sd dss sd sd sf", "ww sdcsw www");

    var sdfg =llss.flatMap(_.split(" "));

    println("sdfg:" + sdfg.toBuffer.grouped(4));

    //    val  sf = sdfg.toBuffer.grouped(4);

    println("grouped:" + llss.toBuffer.sorted);

    println(llss.toBuffer.reverse)

    println("-------------------------------------")

 

 

 

 

12 类、对象、继承、特质

Scala的类与JavaC++的类比起来更简洁,学完之后你会更爱Scala!!!

 

12- 1) 类

 package day2scala

/**

 * 在Scala中,类并不用声明为public。

 * Scala源文件中可以包含多个类,所有这些类都具有公有可见性。

 */

class Persion {

  // 用val修饰的变量是只读属性,有getter但没有setter,(相当与Java中用final修饰的变量)

  var id ="xiaozhang"

  // 用var修饰的变量既有getter又有setter

  var age: Int =18

  // 类私有字段,只能在类的内部使用

  private var name: String ="xiaowang"

  // 对象私有字段,访问权限更加严格的,Person类的方法只能访问到当前对象的字段

  private[this]val age ="48"

 

}

 

 

 

object Persion {

 

  def main(args: Array[String]) {

    var persion =new Persion

    println(persion.id)

    println(persion.name)

    println(persion.age)

  }

 

}

 

xiaozhang

xiaowang

18

12- 2) 构造器

注意:主构造器会执行类定义中的所有语句

package day2scala

 

class Student(var name: String,var age: Int, faceValue: Double =99.9, private var height: Int =20) {

 

  private[this]var gender: String =null

 

  def show() {

    println(faceValue)

  }

 

  //辅助构造器  def this (参数)

  def this(name: String, age: Int, gender: String) {

    //辅助构造器第一行一定要先调用主构造器

    this(name, age)

    this.gender = gender

  }

 

}

 

object Student {

  def main(args: Array[String]) {

    var student =new Student("小王",18,85,180)

    println(student.age +"-" +student.height +"-" +student.name +"-" +student.show)

    student.age =14;

    student.height =852;

    student.name ="xiaoli"

    student.show

    println(student.age +"-" +student.height +"-" +student.name +"-" +student.show)

  }

 

}

 

object Main {

  def main(args: Array[String]) {

    var student =new Student("小王",18,85,180)

    println(student.age +"-" +student.name +"-" +student.show)

  }

 

}

 

 

注意:访问的范围的问题

Private  /private[this]  、、、、

 

13 对象

13 - 1单例对象

package day2scala

 

class SingletonDemo {

 

  SingletonDemo.sayHi()

 

  sayHi()

 

  var name: String ="xiaozhang"

 

  def sayHi() {

    println("这是第一个类的方法")

  }

}

 

object SingletonDemo {

 

  var name: String ="xiaozhang"

 

  def sayHi() {

    println("这是第二类的方法")

  }

 

  def main(args: Array[String]) {

    // 一下调用的是本类的属性

    var sing = SingletonDemo

    sing.sayHi()

    sing.name

 

    // 调用的是SingletonDemo的属性

    var singSin =new SingletonDemo

    singSin.sayHi()

    singSin.name

  }

 

}

 

 

13 - 2) 伴生类

package day2scala

/**

 * 在Scala中,类并不用声明为public。

 * Scala源文件中可以包含多个类,所有这些类都具有公有可见性。

 */

class Persion {

  // 用val修饰的变量是只读属性,有getter但没有setter,(相当与Java中用final修饰的变量)

  var id ="xiaozhang"

  // 用var修饰的变量既有getter又有setter

  var age: Int =18

  // 类私有字段,只能在类的内部使用

  private var name: String ="xiaowang"

  // 对象私有字段,访问权限更加严格的,Person类的方法只能访问到当前对象的字段

  private[this]val age ="48"

 

}

/**

 * 与属性的类的名字一样,并在同一个包中

 */

object Persion {

 

  def main(args: Array[String]) {

    var persion =new Persion

    println(persion.id)

    println(persion.name)

    println(persion.age)

  }

 

}

 

 

13- 3apply方法

 

package day3scala

/**

 * apply方法的使用

 */

object Student {

 

  def apply() = {

    println("这是无惨的apply方法")

  }

 

  def apply(name: String): String = {

    println("这是带一个参数的apply的方法" + name +"")

    // 返回的类型

    name

  }

 

  def apply(name: String, sex: Int) = {

    println("这是既有String又有Int类型的apply的方法")

  }

 

  def main(args: Array[String]) {

    Student("小张")

  }

 

}

 

 

13- 4) 应用程序对象

 

package day3scala

/**

 *  运行main方法的另外一种方式

 *  

 *  继承 App -->  main方法

 */

object Dau extends App {

  println("sf")

}

 

 

  /** The main method.

   *  This stores all argument so that they can be retrieved with `args`

   *  and the executes all initialization code segments in the order they were

   *  passed to `delayedInit`

   *  @param args the arguments passed to the main method

   */

  def main(args: Array[String]) = {

    this._args = args

    for (proc <- initCode) proc()

    if (util.Properties.propIsSet("scala.time")) {

      val total = currentTime - executionStart

      Console.println("[total " + total +"ms]")

    }

  }

 

14 继承

scala中扩展类的方式与咱们老朋友JAVA的实现方式是一样一样的,在scala

中定义接口的关键字是Trait

14 - 1scala超类的实现

package day3scala

/**

 *   一个类可以多实现但是只能单继承,就像一个小孩的特征像爸爸又像他二叔以及三叔

 */

class classDome {

  def say(name: String) = {

    println("classDome 的:" + name)

  }

}

 

trait ClassDomeTrait {

  def classDomeRraitTest(name: String): String = {

    "ClassDomeTrait已被调用" + name

  }

}

 

trait ClassDomeTrait1 {

 

  def ClassDomeTrait1Test = {

    println("ClassDomeTrait1Test已被调用")

  }

 

}

 

object ClassDomeDay3extends classDomewith ClassDomeTraitwith ClassDomeTrait1 {

  def main(args: Array[String]) {

    say("小张")

    var test = classDomeRraitTest("大哥大")

    println(test)

    ClassDomeTrait1Test

  }

}

 

 

15 模式匹配

15 -1 )  匹配字符串以及元组以及偏函数

  package day2scala.cases

 

import scala.util.Random

 

case class Dome3(name: String,higth: String)

case class Dome4(x: Int,y: Int)

case class Dome5(z: Int)

 

/**

 *   匹配字符串可以借助第三方的类来实现参数的传递与获取,这是JAVA比不了的

 */

object Dome2 {

 

  def main(args: Array[String]) {

 

    val arr = Array(Dome3("333","43"), Dome4(3,5), Dome5(5));

    // 主要取值的范围

    arr(Random.nextInt(arr.length))match {

      case Dome3(name,higth) => {

        println("dome3")

      }

      case Dome4(3,4) => {

        println("Dome4")

      }

      case Dome5(5) => {

        println("exit")

      }

    }

    

    val map =Map("a" ->1,"b" ->2,"c" ->3)

    val v =map.get("a")match {

      case Some(i) => println("-------------------")

      case None => println("++++++++++++++++++++++++++")

    }

 

    def func2(num: String): Int = nummatch {

      case "one" =>1

      case "two" =>2

      case _ => -1

    }

 

    println(func2("one"))

 

    // 偏函数

    def func1: PartialFunction[String, Int] = {

      case "one" => {

        println("one case")

        1

      }

      case "two" =>2

      case _ => -1

    }

    println(func1("one"))

 

  }

 

}

 

 

15 - 2) 类型匹配

 

package day2scala.cases

 

import scala.util.Random

 

case class Dome(name: String)

/**

 *  字符串匹配加类型匹配

 */

object Dome1 {

 

  def main(args: Array[String]) {

    val arr = Array("校长","小王","小李","大和大", "回复", 3, 343.6, 'd', true)

    val name =arr(Random.nextInt(arr.length))

    println(name)

 

    name match {

      case "校长" => println("")

      case "xiaowang" => {

        println("xiaozhang")

      }

      case "小李" => {

        println("小李")

      }

      case x: Intif (x >10) => {

        println("Int:" + x)

      }

      case y: Double => {

        println("double:" + y)

      }

      case xx: Intif (xx >20) => {

        throw new Exception("你输入的数大于了20")

      }

      case ("小_","","") => {

        println("小_")

      }

      case (_, _, "小_") => {

        println("________小")

      }

      // _ 如果没有匹配则会执行这段代码

      case _ => {

        println("exit")

        exit

      }

    }

 

  }

 

}

 

15 -3 )  集合匹配

package day2scala.cases

/**

 * 集合的匹配

 */

object DomeTest {

  def main(args: Array[String]) {

    val arr = Array(1,1,7,0)

    arr match {

      case Array(1,1,x,y) => println(x +":" +y)

      //    case Array(1, 1, 7, y) => println("only 0")

      case Array(0, _*) => println("0 ...")

      case _ => println("something else")

    }

 

    val lst = List(0)

    lst match {

      case 0 :: Nil => println("only 0")

      case x ::y :: Nil => println("x $x y $y")

      case 0 ::a => println("0 ... $a")

      case _ => println("something else")

    }

 

  }

}

 

15 -4option匹配

package day3scala

/**

 * 在scalaoption中表示类型可能存在或者可能不存在的值,如果存在则用some表示,没有则用None表示

 */

object OptionDome {

  def main(args: Array[String]) {

    var arr =Map("xiaozhang" ->2,"xiaowang" ->4)

    var ls =arr.get("xiaozhang")match {

      case Some(i) => {

        println(i)

      }

      case None => {

        println("没有找到")

      }

    }

 

    // 更好的实现方式

    var ld =arr.getOrElse("c",0)

    println(ld)

  }

}

 

 

15 -5) 偏函数

被花括号括起来的但是没有match的一组叫做偏函数

 

 def func1: PartialFunction[String, Int] = {

      case "one" => {

        println("one case")

        1

      }

      case "two" =>2

      case _ => -1

    }

println(func1("one"))

 

 

Scala actor编程(也就是JAVA的线程)

!   发送异步消息,没有返回值。

!?  发送同步消息,等待返回值。

!!  发送异步消息,有返回值

 

react方式会复用线程,比receive更高效

 

实例一:

 

package day2scala.actor

 

import scala.actors.Actor

/**

 *  MyActorTest执行的时候首先调用start方法

 */

object MyActorTest extends Actor {

 

  def act() {

    for (i <-1 to10) {

      println("MyActorTest:" + Thread.currentThread().getName())

      Thread.sleep(1000)

    }

  }

}

 

object MyActorTest1 extends Actor {

  def act() {

    for (i <-1 to10) {

      println("MyActorTest1:" + Thread.currentThread().getName())

      Thread.sleep(1000)

    }

  }

}

 

object MyActorTest2 {

  def main(args: Array[String]) {

    MyActorTest.start

    MyActorTest1.start

  }

}

 

实例二:

package day2scala.actor

 

import scala.actors.Actor

/**

 * loop 是个无限循环的,相当于oracle上的loop

 */

class YourActorText extends Actor {

 

  def act() {

    loop {

      react {

        case "start" => {

          println("staring .......")

          println(Thread.currentThread().getName())

        }

        case "end" => {

          println("end ......")

          println(Thread.currentThread().getName())

        }

        case "exit" => {

          exit()

        }

      }

    }

  }

}

 

object YourActorText1 {

  def main(args: Array[String]) {

    var your =new YourActorText

    your.start

    your.!("start")

    your.!!("exit")

  }

 

}

 

 

实例三:

package day2scala.actor

 

import scala.actors.Actor

import scala.io.Source

import scala.actors.Future

import scala.collection.mutable.HashSet

import scala.collection.mutable.ListBuffer

 

case class SubmitTask(filePath: String)

case class ResultTask(result: Map[String, Int])

case object StopTask

 

/**

 * loop 是无限循环

 * react 比receive快

 */

class Taskextends Actor {

 

  def act() {

    loop {

      react {

        case SubmitTask(filePath) => {

          val result = Source.fromFile(filePath).getLines.toList.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).mapValues(_.size)

          sender ! ResultTask(result)

        }

        case StopTask => {

          exit()

        }

      }

    }

  }

}

 

object ActorWordCount{

 

  def main(args: Array[String]) {

    val replySet =new HashSet[Future[Any]]()

    val resultList =new ListBuffer[ResultTask]()

    val list = Array[String]("D:\\text.txt","D:\\text1.txt","D:\\text2.txt")

    for (filePath <-list) {

      val task =new Task

      val reply =task.start !! SubmitTask(filePath)

      replySet += reply

    }

 

    while (replySet.size >0) {

      // _.isSet 判断是不是Set集合

      val toCompute =replySet.filter(_.isSet)

      for (f <-toCompute) {

        // 把执行的结果付给ResultTask

        val result =f().asInstanceOf[ResultTask]

        resultList += result

        replySet -= f

      }

      Thread.sleep(1000)

    }

 

    val fr =resultList.flatMap(_.result).groupBy(_._1).mapValues(_.foldLeft(0)(_ + _._2))

    println(fr)

    exit

  }

}

 

 

 

Scala 高级特性

 1-1 )函数的总结

 

package day3scala

/**

 *  柯里化的函数感觉挺有意思的

 */

object Function {

  def main(args: Array[String]) {

    val arr = Array(1,2,3,4, 5, 6, 7, 8, 9, 0)

    // 定义一个函数并把函数复制给了fun1

    val fun1 = (x: Int) => x *10

    // 把函数作为参数传入到MAP中

    arr.map(fun1)

    // 匿名函数,直接把结果传入到map中,效果与以前的一样

    arr.map((x: Int) => x * 10)

    // 使用scala的推断类型的方式来进行计算

    arr.map(x => x * 10)

    // 神奇的下划线

    arr.map(_ * 10)

 

    // 定义一个方法

    def m(x: Int) = x *10

    // 把方法转化为了函数

    val mm = m _

    // 讲方法传入到map中

    arr.map(mm)

 

    for (x <-arr) {

      println(x)

    }

 

    // 柯里化的转换

    def ss(x: Int) = (yy: Int) => x * yy

    // 转化为函数,传递第一个参数

    val ff = ss(4)

    // 传递第二个参数

    val asd =ff(9)

    // 或者这样写

    val dfg = ss(3)(8)

    println(dfg)

 

  }

}

 

 1-2)隐式转换

package day4scala.File

 

object Context {

  implicit val aaa: Int =10

  implicit val bbb: Int =20

}

 

/**

 *  隐式转换总结

 *  1、Context定义对个相同的类型的变量,在使用时只能指定一个

 *  2、Context如果定义一个可以在使用时使用_

 *  3、Context可以放在使用者类的上面作为包导入

 *  4、也可以把Context提取出来使用

 *  5、神奇的隐式转换

 */

import Context.aaa

object ImplicitValueDome {

  def m(x: Int)(implicit y: Int) = x * y

  def main(args: Array[String]) {

    val r = m(2)

    println(r)

  }

}

 

Wordcount  实例

package day2scala.actor

 

import scala.actors.Actor

import scala.io.Source

import scala.actors.Future

import scala.collection.mutable.HashSet

import scala.collection.mutable.ListBuffer

 

case class SubmitTask(filePath: String)

case class ResultTask(result: Map[String, Int])

case object StopTask

 

/**

 * loop 是无限循环

 * react 比receive快

 */

class Taskextends Actor {

 

  def act() {

    loop {

      react {

        case SubmitTask(filePath) => {

          val result = Source.fromFile(filePath).getLines.toList.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).mapValues(_.size)

          sender ! ResultTask(result)

        }

        case StopTask => {

          exit()

        }

      }

    }

  }

}

 

object ActorWordCount{

 

  def main(args: Array[String]) {

    val replySet =new HashSet[Future[Any]]()

    val resultList =new ListBuffer[ResultTask]()

    val list = Array[String]("D:\\text.txt","D:\\text1.txt","D:\\text2.txt")

    for (filePath <-list) {

      val task =new Task

      val reply =task.start !! SubmitTask(filePath)

      replySet += reply

    }

 

    while (replySet.size >0) {

      // _.isSet 判断是不是Set集合

      val toCompute =replySet.filter(_.isSet)

      for (f <-toCompute) {

        // 把执行的结果付给ResultTask

        val result =f().asInstanceOf[ResultTask]

        resultList += result

        replySet -= f

      }

      Thread.sleep(1000)

    }

 

    val fr =resultList.flatMap(_.result).groupBy(_._1).mapValues(_.foldLeft(0)(_ + _._2))

    println(fr)

    exit

  }

}

 

 

 

Scala 简单RPC实现

1 - 1) 图解

 

  1 - 2) 初步实现

   服务器端:

 

     package cn.****.akka.test

 

import akka.actor.Actor

import com.typesafe.config.ConfigFactory

import akka.actor.ActorSystem

import cn.****.akka.Master

import akka.actor.Props

 

object Mastertest1 extends Actor {

  // 会在启动的时候去检测

  override def receive:Receive = {

    case "start" => {

      println("start......")

    }

 

    case "stop" => {

      println("stop......")

    }

 

    // 匹配woker发送过来的数据

    case "connect" => {

      println("a client connection .....")

      // 链接之后发送给客户端消息

      sender ! "success"

    }

 

    case _ => {

      println("匹配的模式为:_")

    }

 

  }

 

}

 

object Mastertest {

 

  def main(args: Array[String]): Unit = {

    val host ="127.0.0.1"

    val port =8888

 

    val confStr =s"""

         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"

         |akka.remote.netty.tcp.hostname = "$host"

         |akka.remote.netty.tcp.port = "$port"

       """.stripMargin

 

    val conf = ConfigFactory.parseString(confStr)

    val actorSystem =ActorSystem("MaterActorSystem",conf)

    // 发送本身的信息到其他的客户端

    val actor =actorSystem.actorOf(Props[Master],"Mastertest1")

    // 发送异步的消息但是不等待消息返回·  9   

    actor ! "start"

    actor ! "stop"

    actor ! "hello"

    actorSystem.awaitTermination

  }

 

}

 

 

客户端:

 

 package cn.****.akka.test

 

import akka.actor.Actor

import com.typesafe.config.ConfigFactory

import akka.actor.ActorSystem

import akka.actor.Props

 

class WokerTest1 extends Actor {

  // perStart 构造器会在Receive只想之前运行

  override def preStart(): Unit = {

    val master = context.actorSelection("akka.tcp://MaterActorSystem@127.0.0.1:8888/user/Mastertest1")

    println("connection ..........")

    master ! "connect"

  }

 

  // 接受master发送过来的消息

  def receive: Receive = {

    case "success" => {

      println("a messger for  master :success !!")

    }

  }

}

 

object WakerTest {

  def main(args: Array[String]): Unit = {

 

    val host = "127.0.0.1"

    val port = 999

 

    // stripMargin表示把|去掉,s表示引入了可变的参数

    val confStr = s"""

         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"

         |akka.remote.netty.tcp.hostname = "$host"

         |akka.remote.netty.tcp.port = "$port"

       """.stripMargin

    val conf = ConfigFactory.parseString(confStr)

    val actorSystem = ActorSystem("WorkerActorSystem", conf)

    actorSystem.actorOf(Props[WokerTest1], "WakerTest1")

    // 等待优雅的推出,方式与nginx一致

    actorSystem.awaitTermination()

  }

}

 

 

  1 - 3深入探究:

 

 

服务器端:

 

package test.cn.****.test

 

import akka.actor.Actor

import com.typesafe.config.impl.ConfigString

import com.typesafe.config.ConfigFactory

import akka.actor.ActorSystem

import akka.actor.Props

import scala.collection.mutable

import java.util.UUID

import scala.concurrent.duration._

import scala.collection.mutable.HashMap

import scala.collection.mutable.HashSet

 

class TestMaster(val host:String,val port: Int)extends Actor {

  // 保存wokerId 到TestwokerInfo的映射

  val idtoWoker =new HashMap[String, TestWorkerInfo]()

  // 保存所有TestWokerInfo的信息

  val wokers =new HashSet[TestWorkerInfo]

 

  // 单位秒

  val CHECK_INTERVAL =15000

 

  // 在Receive运行之前运行

  override def preStart(): Unit = {

    import context.dispatcher

    context.system.scheduler.schedule(0 millis,CHECK_INTERVAL millis,self,TestCheckTimeOutWorker)

  }

 

  def receive:Receive = {

    // 接受woker发送过来的注册信息

    case TestRegisterWorker(wokerId,cores,memory) => {

      if (!idtoWoker.contains(wokerId)) {

        try {

          val wokerInfo =new TestWorkerInfo(wokerId,cores,memory)

          idtoWoker(wokerId) =wokerInfo

          wokers += wokerInfo

          sender ! TestRegisterdWorker(s"akka.tcp://${TestMaster.MASTER_SYSTEM}@$host:$port/user/${TestMaster.MASTER_NAME}")

        } catch {

          case t:Throwable =>t.printStackTrace()

        }

      }

    }

 

    // worker 发送给master的心跳信息

    case TestHeartbeat(workerId) => {

      if (idtoWoker.contains(workerId)) {

        val workerInfo =idtoWoker(workerId)

        val currentTime = System.currentTimeMillis()

        workerInfo.lastHeartbeatTime =currentTime

      }

    }

 

    case TestCheckTimeOutWorker => {

      val currentTime = System.currentTimeMillis()

      val deadWorkers =wokers.filter {x =>currentTime - x.lastHeartbeatTime > CHECK_INTERVAL }

      deadWorkers.foreach {

        x =>

          idtoWoker -= x.wokerId

          wokers -= x

      }

      println("现在存活的个数为:" + wokers.size)

    }

  }

}

 

object TestMaster {

  // 定义常量

  val MASTER_NAME ="TestMaster"

  val MASTER_SYSTEM ="TestMasterActorSystem"

 

  def main(args: Array[String]): Unit = {

    val host ="127.0.0.1"

    val port =8888

 

    val confStr =s"""

         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"

         |akka.remote.netty.tcp.hostname = "$host"

         |akka.remote.netty.tcp.port = "$port"

       """.stripMargin

    val conf = ConfigFactory.parseString(confStr)

    val actorSystem =ActorSystem(MASTER_SYSTEM,conf)

    val actor =actorSystem.actorOf(Props(new TestMaster(host,port)),MASTER_NAME)

    actorSystem.awaitTermination()

  }

 

}

 

 

客户端:

package test.cn.****.test

 

import akka.actor.Actor

import com.typesafe.config.ConfigFactory

import akka.actor.ActorSystem

import akka.actor.ActorSelection

import akka.actor.Props

import java.util.UUID

import scala.concurrent.duration._

 

class TestWoker(val cores: Int,val memory: Int,val masterHost:String,val masterPort: Int)extends Actor {

  // master 的URL

  var masterUrl:String = _

  // master的引用  

  var master: ActorSelection = _

  // 定时时间

  val HEARTBEAT_INTERVAL =10000

  // wokerID

  val wokerId = UUID.randomUUID().toString()

  override def preStart(): Unit = {

    master = context.actorSelection(s"akka.tcp://${TestMaster.MASTER_SYSTEM}@$masterHost:$masterPort/user/${TestMaster.MASTER_NAME}")

    println("preStart:" + UUID.randomUUID().toString())

    // 向master发送注册信息

    master ! TestRegisterWorker(wokerId,cores,memory)

  }

 

  def receive:Receive = {

    // 注册完成后master发送给woker的信息

    case TestRegisterdWorker(masterUrl) => {

      this.masterUrl =masterUrl

      // 启动定时任务,定时的向master发送心跳

      import context.dispatcher

      context.system.scheduler.schedule(0 milli,HEARTBEAT_INTERVAL milli,self,TestSendHeartbeat)

    }

 

    // 定时向master发送心跳

    case TestSendHeartbeat => {

      println("TestSendHeartbeat:" + UUID.randomUUID().toString())

      master ! TestHeartbeat(wokerId)

    }

 

  }

}

 

object TestWoker {

  def main(args: Array[String]): Unit = {

    // woker的信息

    val host ="127.0.0.1"

    val port =9999

    val cores =4

    val memory =1024

 

    // master的信息

    val masterHost ="127.0.0.1"

    val masterPort =8888

 

    val confStr =s"""

         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"

         |akka.remote.netty.tcp.hostname = "$host"

         |akka.remote.netty.tcp.port = "$port"

       """.stripMargin

    val conf = ConfigFactory.parseString(confStr)

    val actorSystem =ActorSystem("TestWokerActorSystem",conf)

    val actor =actorSystem.actorOf(Props(new TestWoker(cores,memory,masterHost,masterPort)), "TestWoker")

    actorSystem.awaitTermination()

  }

}

 

 

接口端:

package test.cn.****.test

// 定义接口

trait TestMessage extends Serializable

 

// woker -> master 发送注册信息

case class TestRegisterWorker(workerId:String,cores: Int, memory: Int) extends TestMessage

 

// master -> woker 注册成功信息

case class TestRegisterdWorker(masterUrl:String)extends TestMessage

 

// woker  master 发送心跳

case class TestHeartbeat(wokerId:String)

 

// woker  初始化的信息

case object TestSendHeartbeat extends TestMessage

 

// 检查超时的时间

case object TestCheckTimeOutWorker extends TestMessage

 

 

 

构造器端:

 

package test.cn.****.test

 

class TestWorkerInfo(val wokerId:String,val cores: Int,val memory: Int) {

  var lastHeartbeatTime: Long = _

}

 

 

 

Spark总结

 

集群环境搭建

官网:http://www.apache.org/dyn/closer.lua/spark/spark-1.5.2/spark-1.5.2-bin-hadoop2.6.tgz

1-1)单机版配置spark集群

 

[root@hadoop1 nginx]# tar -zxvf spark-1.3.1-bin-hadoop2.6.tgz -C /usr/local/

[root@hadoop1 conf]# cd /usr/local/spark-1.3.1-bin-hadoop2.6/

[root@hadoop1 spark-1.3.1-bin-hadoop2.6]# cd conf/

[root@hadoop1 conf]# cp spark-env.sh.template  spark-env.sh.template_back

[root@hadoop1 conf]# mv spark-env.sh.template spark-env.sh

export JAVA_HOME=/home/jdk1.7

export SPARK_MASTER_IP=hadoop1

export SPARK_MASTER_PORT=7077

 

[root@hadoop1 conf]# cp slaves.template slaves

[root@hadoop1 conf]# vi slaves

hadoop2

hadoop3

 

[root@hadoop1 local]# scp -r spark-1.3.1-bin-hadoop2.6/ hadoop2:$PWD

[root@hadoop1 local]# scp -r spark-1.3.1-bin-hadoop2.6/ hadoop3:$PWD

 

 

 

1-2)启动spark

[root@hadoop1 /]# cd usr/local/spark-1.3.1-bin-hadoop2.6/sbin/

[root@hadoop1 sbin]# ./start-all.sh

starting org.apache.spark.deploy.master.Master, logging to /usr/local/spark-1.3.1-bin-hadoop2.6/sbin/../logs/spark-root-org.apache.spark.deploy.master.Master-1-hadoop1.out

hadoop2: starting org.apache.spark.deploy.worker.Worker, logging to /usr/local/spark-1.3.1-bin-hadoop2.6/sbin/../logs/spark-root-org.apache.spark.deploy.worker.Worker-1-hadoop2.out

hadoop3: starting org.apache.spark.deploy.worker.Worker, logging to /usr/local/spark-1.3.1-bin-hadoop2.6/sbin/../logs/spark-root-org.apache.spark.deploy.worker.Worker-1-hadoop3.out

 

1-3)查看spark信息

 

[root@hadoop1 sbin]# jps

8784 Master

8880 Jps

 

[root@hadoop2 /]# jps

6421 Worker

6477 Jps

 

[root@hadoop3 /]# jps

5999 Worker

 

1-4) 查看单机版图形界面

 

 

 

1-5)使用zookeeper辅助搭建spark集群

 需要zk 启动正常,注意以下标红的部分

[root@hadoop1 sbin]# ./stop-all.sh

[root@hadoop1 conf]# /usr/local/spark-1.3.1-bin-hadoop2.6/conf

[root@hadoop1 conf]#  vi spark-env.sh

export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER

-Dspark.deploy.zookeeper.url=hadoop1:2181,hadoop2:2181,hadoop3:2181

-Dspark.deploy.zookeeper.dir=/spark"

 

[root@hadoop2 conf]# vi spark-env.sh

[root@hadoop2 conf]#  vi spark-env.sh

export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER

-Dspark.deploy.zookeeper.url=hadoop1:2181,hadoop2:2181,hadoop3:2181

-Dspark.deploy.zookeeper.dir=/spark"

 

 

[root@hadoop1 conf]# /usr/local/spark-1.3.1-bin-hadoop2.6/sbin

[root@hadoop1 sbin]# ./start-all.sh

 

[root@hadoop2 conf]# cd ../sbin/

[root@hadoop2 sbin]# ./start-master.sh

1-6)查看集群版图形界面

Hadoop1

 

 

 

Hadoop2

 

 

 

 

启动多个master的实例:

spark-shell --master spark://hadoop1:7077,hadoop2:7077 --executor-memory 512m --total-executor-cores 7

 

 

执行wc程序:

sc.textFile("hdfs://hadoop1:9000/wc").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)

.sortBy(_._2, false).saveAsTextFile("hdfs://hadoop1:9000/out05")

 

 

启动集群:

spark-submit  --master spark://hadoop1:7077,hadoop1:7077

--executor-memory 512m --total-executor-cores 7 --class cn.****.spark.WordCount 

/root/spark-1.0-SNAPSHOT.jar hdfs://hadoop1:9000/wc  hdfs://hadoop1:9000/out0001

 

 

注意:spark-shell启动的是单机版,spark-submit启动的则是集群版,以上标红的部分来自于下面wordCount的实例

 

WordCount 实例

package Hellword

import org.apache.spark.rdd.RDD

import org.apache.spark.{ SparkConf, SparkContext }

object WordCount {

  def main(args: Array[String]) {

    //创建sparkconf

    val conf =new SparkConf().setAppName("WordCount")

    //创建sparkcontext

    val sc =new SparkContext(conf)

    //读取hdfs中的数据

    val lines:RDD[String] =sc.textFile(args(0))

    //切分单词

    val words:RDD[String] =lines.flatMap(_.split(" "))

    //将单词计算

    val wordAndOne:RDD[(String, Int)] =words.map((_,1))

    //分组聚合

    val result:RDD[(String, Int)] =wordAndOne.reduceByKey((x, y) => x + y)

    //排序

    val finalResult:RDD[(String, Int)] =result.sortBy(_._2,false)

    //将数据存储到HDFS中

    finalResult.saveAsTextFile(args(1))

    //释放资源

    sc.stop()

 

  }

}

 

Spark执行过程

 

 

 

 

Spark计算模型

1-1) RDD 总结

概述:

RDDResilient Distributed Dataset)即使分布式数据集,是spark最基本的数据抽象,它代表了一个不可变、可分区、里面的元素可以并行计算的集合。

 

特点:

1、自动容错、位置感知性调度和伸缩性

2RDD 允许用户把数据放在内存中,下次读取时可以在内存中直接读取

 

 

RDD的属性

1-1)一组分片

是数据的最基本的数据单位,对于RDD来说,每一个分片都会计算一个任务处理,并决定计算的粒度,没有没有执行,则会使用默认的值,默认的值就是程序分配到的CPU  CORE的个数

 

1-2)一个计算每个分区的函数。

SparkRDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。

 

1-3RDD之间的依赖关系。

RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。

 

1-4)一个Partitioner,即RDD的分片函数。

当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于于key-valueRDD,才会有Partitioner,非key-valueRDDParititioner的值是NonePartitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。

 

1-5)储存位置

一个列表,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。

 

 

算子

Spark 提供了很多API,RDD提供了两个API,是TransFormationActions

 

TransFormation:转换操作,返回值还是一个RDD,如:map , filter , union

Actions:行动操作,返回结果是把RDD持久化起来,如count , collect , save

 

TransFormation采用的是懒策略,如果只是将TransFormation提交是不会提交任务来执行的,任务执行只会在action被提交时才被触发。

 

1-1)常用的常用的Transformation

执行后不会马上执行,直到遇到action

转换

含义

map(func)

返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成

filter(func)

返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成

flatMap(func)

类似于map,但是每一个输入元素可以被映射为0或多个输出元素(所以func应该返回一个序列,而不是单一元素)

mapPartitions(func)

类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]

mapPartitionsWithIndex(func)

类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是

(Int, Interator[T]) => Iterator[U]

sample(withReplacement, fraction, seed)

根据fraction指定的比例对数据进行采样,可以选择是否使用随机数进行替换,seed用于指定随机数生成器种子

union(otherDataset)

对源RDD和参数RDD求并集后返回一个新的RDD

intersection(otherDataset)

对源RDD和参数RDD求交集后返回一个新的RDD

distinct([numTasks]))

对源RDD进行去重后返回一个新的RDD

groupByKey([numTasks])

在一个(K,V)的RDD上调用,返回一个(K, Iterator[V])的RDD

reduceByKey(func, [numTasks])

在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,与groupByKey类似,reduce任务的个数可以通过第二个可选的参数来设置

aggregateByKey(zeroValue)(seqOp, combOp, [numTasks])

 

sortByKey([ascending], [numTasks])

在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD

sortBy(func,[ascending], [numTasks])

与sortByKey类似,但是更灵活

join(otherDataset, [numTasks])

在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD

cogroup(otherDataset, [numTasks])

在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable<V>,Iterable<W>))类型的RDD

cartesian(otherDataset)

笛卡尔积

pipe(command, [envVars])

 

coalesce(numPartitions)

 

repartition(numPartitions)

 

repartitionAndSortWithinPartitions(partitioner)

 

 

 

1-2) 常用的Action

 

动作

含义

reduce(func)

通过func函数聚集RDD中的所有元素,这个功能必须是课交换且可并联的

collect()

在驱动程序中,以数组的形式返回数据集的所有元素

count()

返回RDD的元素个数

first()

返回RDD的第一个元素(类似于take(1))

take(n)

返回一个由数据集的前n个元素组成的数组

takeSample(withReplacement,num, [seed])

返回一个数组,该数组由从数据集中随机采样的num个元素组成,可以选择是否用随机数替换不足的部分,seed用于指定随机数生成器种子

takeOrdered(n[ordering])

 

saveAsTextFile(path)

将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本

saveAsSequenceFile(path

将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。

saveAsObjectFile(path

 

countByKey()

针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数。

foreach(func)

在数据集的每一个元素上,运行函数func进行更新。

 

 

 

1-3RDD实例

练习1

 

前提是安装好spark集群

Spark-shell  --master  spark://hadoop1:7077

 

通过并行化生成ADD

 

val rdd1 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10))

 

对每一个rdd1每个数乘以10然后排序

Val rdd2  =rdd1.map(_*10).sortBy(x=>x ,flase)

 

过滤等于10的数

Val a rdd3 = rdd2.filter(_>=10)

 

以元素的形式显示数据

 

rdd3.collect

练习2

生成ADD

val rdd4 = sc.parallelize(Array("a b c", "d e f", "h i j"))

 

rdd4的数据先过滤再压平

rdd4.flatMap(_.split(" "))

 

练习3

val rdd1 = sc.parallelize(List(5, 6, 4, 3))

val rdd2 = sc.parallelize(List(1, 2, 3, 4))

//求并集

val rdd3 = rdd1.union(rdd2)

//求交集

val rdd4 = rdd1.intersection(rdd2)

//去重

rdd3.distinct.collect

// 查看数据

rdd4.collect

 

练习4

val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2)))

val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))

// 求交集

Val  redd3 = Rdd1.jion(rdd2)

red3.collect

// 求并集

Val  redd4 = redd3 union redd2

// key进行分组

Val  redd5 = redd4.groupByKey

// 查看信息

redd5.collect

 

 

练习5

val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))

val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))

//cogroup

val rdd3 = rdd1.cogroup(rdd2)

//注意cogroup与groupByKey的区别

rdd3.collect

 

练习6

val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5))

// reduce聚合

val rdd2 = rdd1.reduce(_ + _)

// 查看

rdd2.collect

 

练习7

val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2),  ("shuke", 1)))

val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 3), ("shuke", 2), ("kitty", 5)))

val rdd3 = rdd1.union(rdd2)

// key进行聚合

rdd3.reduceByKey(_+_);

 

 

 

// 想要了解更多,访问下面的地址

http://homepage.cs.latrobe.edu.au/zhe/ZhenHeSparkRDDAPIExamples.html

 

练习8:

http://homepage.cs.latrobe.edu.au/zhe/ZhenHeSparkRDDAPIExamples.html

 

mapPartitionsWithIndex

val func = (index: Int, iter: Iterator[(Int)]) => {

  iter.toList.map(x => "[partID:" +  index + ", val: " + x + "]").iterator

}

val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 2)

rdd1.mapPartitionsWithIndex(func).collect

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

aggregate

 

def func1(index: Int, iter: Iterator[(Int)]) : Iterator[String] = {

  iter.toList.map(x => "[partID:" +  index + ", val: " + x + "]").iterator

}

val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 2)

rdd1.mapPartitionsWithIndex(func1).collect

rdd1.aggregate(0)(math.max(_, _), _ + _)

rdd1.aggregate(5)(math.max(_, _), _ + _)

 

 

val rdd2 = sc.parallelize(List("a","b","c","d","e","f"),2)

def func2(index: Int, iter: Iterator[(String)]) : Iterator[String] = {

  iter.toList.map(x => "[partID:" +  index + ", val: " + x + "]").iterator

}

rdd2.aggregate("")(_ + _, _ + _)

rdd2.aggregate("=")(_ + _, _ + _)

 

val rdd3 = sc.parallelize(List("12","23","345","4567"),2)

rdd3.aggregate("")((x,y) => math.max(x.length, y.length).toString, (x,y) => x + y)

 

val rdd4 = sc.parallelize(List("12","23","345",""),2)

rdd4.aggregate("")((x,y) => math.min(x.length, y.length).toString, (x,y) => x + y)

 

val rdd5 = sc.parallelize(List("12","23","","345"),2)

rdd5.aggregate("")((x,y) => math.min(x.length, y.length).toString, (x,y) => x + y)

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

aggregateByKey

 

val pairRDD = sc.parallelize(List( ("cat",2), ("cat", 5), ("mouse", 4),("cat", 12), ("dog", 12), ("mouse", 2)), 2)

def func2(index: Int, iter: Iterator[(String, Int)]) : Iterator[String] = {

  iter.toList.map(x => "[partID:" +  index + ", val: " + x + "]").iterator

}

pairRDD.mapPartitionsWithIndex(func2).collect

pairRDD.aggregateByKey(0)(math.max(_, _), _ + _).collect

pairRDD.aggregateByKey(100)(math.max(_, _), _ + _).collect

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

checkpoint

sc.setCheckpointDir("hdfs://node-1.****.cn:9000/ck")

val rdd = sc.textFile("hdfs://node-1.****.cn:9000/wc").flatMap(_.split(" ")).map((_, 1)).reduceByKey(_+_)

rdd.checkpoint

rdd.isCheckpointed

rdd.count

rdd.isCheckpointed

rdd.getCheckpointFile

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

coalesce, repartition

val rdd1 = sc.parallelize(1 to 10, 10)

val rdd2 = rdd1.coalesce(2, false)

rdd2.partitions.length

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

collectAsMap

val rdd = sc.parallelize(List(("a", 1), ("b", 2)))

rdd.collectAsMap

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

combineByKey

val rdd1 = sc.textFile("hdfs://node-1.****.cn:9000/wc").flatMap(_.split(" ")).map((_, 1))

val rdd2 = rdd1.combineByKey(x => x, (a: Int, b: Int) => a + b, (m: Int, n: Int) => m + n)

rdd2.collect

 

val rdd3 = rdd1.combineByKey(x => x + 10, (a: Int, b: Int) => a + b, (m: Int, n: Int) => m + n)

rdd3.collect

 

 

val rdd4 = sc.parallelize(List("dog","cat","gnu","salmon","rabbit","turkey","wolf","bear","bee"), 3)

val rdd5 = sc.parallelize(List(1,1,2,2,2,1,2,2,2), 3)

val rdd6 = rdd5.zip(rdd4)

val rdd7 = rdd6.combineByKey(List(_), (x: List[String], y: String) => x :+ y, (m: List[String], n: List[String]) => m ++ n)

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

countByKey

 

val rdd1 = sc.parallelize(List(("a", 1), ("b", 2), ("b", 2), ("c", 2), ("c", 1)))

rdd1.countByKey

rdd1.countByValue

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

filterByRange

 

val rdd1 = sc.parallelize(List(("e", 5), ("c", 3), ("d", 4), ("c", 2), ("a", 1)))

val rdd2 = rdd1.filterByRange("b", "d")

rdd2.colllect

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

flatMapValues

val a = sc.parallelize(List(("a", "1 2"), ("b", "3 4")))

rdd3.flatMapValues(_.split(" "))

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

foldByKey

 

val rdd1 = sc.parallelize(List("dog", "wolf", "cat", "bear"), 2)

val rdd2 = rdd1.map(x => (x.length, x))

val rdd3 = rdd2.foldByKey("")(_+_)

 

val rdd = sc.textFile("hdfs://node-1.****.cn:9000/wc").flatMap(_.split(" ")).map((_, 1))

rdd.foldByKey(0)(_+_)

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

foreachPartition

val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)

rdd1.foreachPartition(x => println(x.reduce(_ + _)))

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

keyBy

val rdd1 = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)

val rdd2 = rdd1.keyBy(_.length)

rdd2.collect

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

keys values

val rdd1 = sc.parallelize(List("dog", "tiger", "lion", "cat", "panther", "eagle"), 2)

val rdd2 = rdd1.map(x => (x.length, x))

rdd2.keys.collect

rdd2.values.collect

 

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

mapPartitions

 

 

 

 

RDD的依赖关系

 

1-1) 窄依赖--没有进行Shuffer

窄依赖指的是每一个父RDDPartition最多被子RDD的一个Partition使用

 

 

 

 

 

 

1-2)宽依赖--进行Shuffer

宽依赖指的是多个子RDDPartition会依赖同一个父RDDPartition

 

 

 

 

注意:窄依赖与宽依赖的依据是是不是执行了Shuffer, JION在大多的情况下是宽依赖,如果之前分好组了那么就是窄依赖。

1-3Lineage

当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。

 

 

1-4RDD的缓存

Spark 之所以快是因为spark使用了内存的技术,当持久化后会把数据集保存到内存中,在以后运算中速度更快。

 

1-5RDD缓存方式

 

RDD通过persist或者cache的方法计算出来的结果加入到内存中,以便今后是有。

一下源码的查看:

 

 

可以看出cache后也是调用的persist,默认的存储级别都是仅在内存存储一份,Spark的存储级别还有好多种,存储级别在object StorageLevel中定义的。

一下是方法的定义:

 

 

 

RDD 内存的储的机制,如果储存的数据过大,当在内存中占用30%是就会把数据保存到磁盘中,以后运算时也是在磁盘中获取数据。

 

 

 

DAG的生成 <--调用RDD的算子生成了RDD的数组

DAG(Directed Acyclic Graph)叫做有向无环图,原始的RDD通过一系列的转换就就形成了DAG,根据RDD之间的依赖关系的不同将DAG划分成不同的Stage,对于窄依赖,partition的转换处理在Stage中完成计算。对于宽依赖,由于有Shuffle的存在,只能在parent RDD处理完成后,才能开始接下来的计算,因此宽依赖是划分Stage的依据。

 

 

WorkCount 图解:

 1-1 ) 官方图解:

 

 

 

 1-2)个人图解:

 

JAVA调用Scala实例

package JavaScala;

 

import org.apache.spark.SparkConf;

import org.apache.spark.api.java.JavaPairRDD;

import org.apache.spark.api.java.JavaRDD;

import org.apache.spark.api.java.JavaSparkContext;

import org.apache.spark.api.java.function.FlatMapFunction;

import org.apache.spark.api.java.function.Function2;

import org.apache.spark.api.java.function.PairFunction;

import scala.Tuple2;

 

import java.util.Arrays;

 

/**

 * Created by ZhaoXing on 2016/8/22.

 */

public class JavaScalaTest {

 

public static void main(String[]args) {

SparkConf conf = new SparkConf().setAppName("JavaWordCount");

// 创建java sparkcontext

JavaSparkContext jsc = new JavaSparkContext(conf);

// 读取数据

JavaRDD<String> lines = jsc.textFile(args[0]);

// 切分

JavaRDD<String> words = lines

.flatMap(new FlatMapFunction<String, String>() {

public Iterable<String> call(Stringline)throws Exception {

return Arrays.asList(line.split(" "));

}

});

// 遇见一个单词就记作一个1

JavaPairRDD<String, Integer> wordAndOne =words

.mapToPair(new PairFunction<String, String, Integer>() {

@Override

public Tuple2<String, Integer> call(Stringword)

throws Exception {

return new Tuple2<String, Integer>(word, 1);

}

});

// 分组聚合

JavaPairRDD<String, Integer> result =wordAndOne

.reduceByKey(new Function2<Integer, Integer, Integer>() {

@Override

public Integer call(Integeri1, Integeri2)

throws Exception {

return i1 +i2;

}

});

// 反转顺序

JavaPairRDD<Integer, String> swapedPair =result

.mapToPair(new PairFunction<Tuple2<String, Integer>, Integer, String>() {

private static final long serialVersionUID = 1L;

 

@Override

public Tuple2<Integer, String> call(

Tuple2<String, Integer> tp) throws Exception {

return new Tuple2<Integer, String>(tp._2,tp._1);

}

});

// 排序并调换顺序

JavaPairRDD<String, Integer> finalResult =swapedPair

.sortByKey(false)

.mapToPair(

new PairFunction<Tuple2<Integer, String>, String, Integer>() {

private static final long serialVersionUID = 1L;

 

public Tuple2<String, Integer> call(

Tuple2<Integer, String> tp)

throws Exception {

return tp.swap();

}

});

// 保存

finalResult.saveAsTextFile(args[1]);

jsc.stop();

}

}


Spark  SQL

 

 

概述:

Spark  sql 是用来处理结构化数据的一个模块,它提供了一个编程抽象叫做DataFrame并且作为分布式SQL查询引擎的作用。

 

特点:

1、spark  sql 要比hive执行的速度要快,原因在于spark sql不用通过mapreduce来执行程序,减少了执行的复杂性。

2、Spark sql 可以将数据转化为RDD(内存中),大大提高了执行的效率。

3、易整合、统一的数据访问方式、兼容hive、标准的数据连接

 

 

DataFrames

概述:

RDD类似,DataFrame也是一个分布式数据容器。然而DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。同时,与Hive类似,DataFrame也支持嵌套数据类型(struct、array和map)。从API易用性的角度上 看,DataFrame API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。由于与R和Pandas的DataFrame类似,Spark DataFrame很好地继承了传统单机数据分析的开发体验。

 

 

 

实例:

准备数据:

在本地创建一个文件,有三列,分别是idnameage,用空格分隔,然后上传到hdfs

hdfs dfs -put person.txt /

启动:

spark-shell --master spark://hadoop1:7077

//1.读取数据,将每一行的数据使用列分隔符分割

val lineRDD = sc.textFile("hdfs://hadoop1:9000/person.txt", 1).map(_.split(" "))

 

//2.定义case class(相当于表的schema

case class Person(id:Int, name:String, age:Int)

 

//3.导入隐式转换,在当前版本中可以不用导入

import sqlContext.implicits._

 

//4.lineRDD转换成personRDD

val personRDD = lineRDD.map(x => Person(x(0).toInt, x(1), x(2).toInt))

 

//5.personRDD转换成DataFrame

val personDF = personRDD.toDF

 

 

 

6.personDF进行处理

 

#(SQL风格语法)

personDF.registerTempTable("t_person")

sqlContext.sql("select * from t_person order by age desc limit 2").show

sqlContext.sql("desc t_person").show

val result = sqlContext.sql("select * from t_person order by age desc")

 

7.保存结果

result.save("hdfs://hadoop1:9000/sql/res1")

result.save("hdfs://hadoop1:9000/sql/res2", "json")

 

#JSON文件格式覆写HDFS上的JSON文件

import org.apache.spark.sql.SaveMode._

result.save("hdfs://hadoop1:9000/sql/res2", "json" , Overwrite)

 

8.重新加载以前的处理结果(可选)

sqlContext.load("hdfs://hadoop1:9000/sql/res1")

sqlContext.load("hdfs://hadoop1:9000/sql/res2", "json")

 

 

DSL风格语法

//查看DataFrame中的内容

personDF.show

 

//查看DataFrame部分列中的内容

personDF.select(personDF.col("name")).show

personDF.select(col("name"), col("age")).show

personDF.select("name").show

 

//打印DataFrame的Schema信息

personDF.printSchema

 

//查询所有的name和age,并将age+1

personDF.select(col("id"), col("name"), col("age") + 1).show

personDF.select(personDF("id"), personDF("name"), personDF("age") + 1).show

 

 

//过滤age大于等于18的

personDF.filter(col("age") >= 18).show

 

 

//按年龄进行分组并统计相同年龄的人数

personDF.groupBy("age").count().show()

 

 

 

 

SQL风格语法

如果想使用SQL风格的语法,需要将DataFrame注册成表

personDF.registerTempTable("t_person")

 

//查询年龄最大的前两名

sqlContext.sql("select * from t_person order by age desc limit 2").show

 

 

//显示表的Schema信息

sqlContext.sql("desc t_person").show

 

 

 

以编程方式执行Spark SQL查询

编写Spark SQL查询程序

Pom.xml 加入如下:

<dependency>

    <groupId>org.apache.spark</groupId>

    <artifactId>spark-sql_2.10</artifactId>

    <version>1.5.2</version>

</dependency>

 

1-1)通过反射推断Schema

import org.apache.spark.{ SparkConf, SparkContext }

import org.apache.spark.sql.SQLContext

 

object InferringSchema {

  def main(args: Array[String]) {

 

    //创建SparkConf()并设置App名称

    val conf =new SparkConf().setAppName("SQL-1")

    //SQLContext要依赖SparkContext

    val sc =new SparkContext(conf)

    //创建SQLContext

    val sqlContext =new SQLContext(sc)

 

    //从指定的地址创建RDD

    val lineRDD =sc.textFile("hdfs://node1.****.cn:9000/person.txt").map(_.split(" "))

 

    //创建case class

    //将RDD和case class关联

    val personRDD =lineRDD.map(x =>Person(x(0).toInt,x(1),x(2).toInt))

    //导入隐式转换,如果不到人无法将RDD转换成DataFrame

    //将RDD转换成DataFrame

    import sqlContext.implicits._

    val personDF =personRDD.toDF

    //注册表

    personDF.registerTempTable("t_person")

    //传入SQL

    val df =sqlContext.sql("select * from t_person order by age desc limit 2")

    //将结果以JSON的方式存储到指定位置

    df.write.json("hdfs://node1.****.cn:9000/out ")

    //停止Spark Context

    sc.stop()

  }

}

//case class一定要放到外面

case class Person(id: Int,name:String,age: Int)

 

 

1-2 )通过StructType直接指定Schema

package day7

 import org.apache.spark.sql.{Row, SQLContext}

import org.apache.spark.sql.types._

import org.apache.spark.{SparkContext, SparkConf}

object SpecifyingSchema {

 

/**

  * Created by ZX on 2015/12/11.

  */

object SpecifyingSchema {

  def main(args: Array[String]) {

    //创建SparkConf()并设置App名称

    val conf =new SparkConf().setAppName("SQL-2")

    //SQLContext要依赖SparkContext

    val sc =new SparkContext(conf)

    //创建SQLContext

    val sqlContext =new SQLContext(sc)

    //从指定的地址创建RDD

    val personRDD =sc.textFile("hdfs://node1.****.cn:9000/person.txt").map(_.split(" "))

    //通过StructType直接指定每个字段的schema

    val schema = StructType(

      List(

        StructField("id", IntegerType,true),

        StructField("name", StringType,true),

        StructField("age", IntegerType,true)

      )

    )

    //将RDD映射到rowRDD

    val rowRDD =personRDD.map(p => Row(p(0).toInt, p(1).trim, p(2).toInt))

    //将schema信息应用到rowRDD上

    val personDataFrame =sqlContext.createDataFrame(rowRDD, schema)

    //注册表

    personDataFrame.registerTempTable("t_person")

    //执行SQL

    val df =sqlContext.sql("select * from t_person order by age desc limit 4")

    //将结果以JSON的方式存储到指定位置

    df.write.json("hdfs://node1.****.cn:9000/out1")

    //停止Spark Context

    sc.stop()

  }

}

 

 

数据源

 1-1) JDBC

 Spark SQL可以通过JDBC从关系型数据库中读取数据的方式创建DataFrame,通过对DataFrame一系列的计算后,还可以将数据再写回关系型数据库中。

 

 

 1-2) 启动Spark Shell,必须指定mysql连接驱动jar

/usr/local/spark-1.5.2-bin-hadoop2.6/bin/spark-shell \

--master spark://hadoop1:7077 \

--jars /usr/local/spark-1.5.2-bin-hadoop2.6/mysql-connector-java-5.1.35-bin.jar \

--driver-class-path /usr/local/spark-1.5.2-bin-hadoop2.6/mysql-connector-java-5.1.35-bin.jar

 

1.mysql中加载数据

val jdbcDF = sqlContext.read.format("jdbc").options(Map("url" -> "jdbc:mysql://hadoop1:3306/bigdata", "driver" -> "com.mysql.jdbc.Driver", "dbtable" -> "person", "user" -> "root", "password" -> "123456")).load()

 

2.执行查询

jdbcDF.show()

 

 

 

 

 1-3) 将数据写入到MySQL

package day7

import java.util.Properties

import org.apache.spark.sql.{ SQLContext, Row }

import org.apache.spark.sql.types.{ StringType, IntegerType, StructField, StructType }

import org.apache.spark.{ SparkConf, SparkContext }

object JdbcRDD {

  def main(args: Array[String]) {

    val conf =new SparkConf().setAppName("MySQL-Demo")

    val sc =new SparkContext(conf)

    val sqlContext =new SQLContext(sc)

    //通过并行化创建RDD

    val personRDD =sc.parallelize(Array("1 tom 5","2 jerry 3", "3 kitty 6")).map(_.split(" "))

    //通过StructType直接指定每个字段的schema

    val schema =StructType(

      List(

        StructField("id", IntegerType,true),

        StructField("name", StringType, true),

        StructField("age", IntegerType, true)))

    //将RDD映射到rowRDD

    val rowRDD =personRDD.map(p =>Row(p(0).toInt, p(1).trim, p(2).toInt))

    //将schema信息应用到rowRDD上

    val personDataFrame =sqlContext.createDataFrame(rowRDD, schema)

    //创建Properties存储数据库相关属性

    val prop =new Properties()

    prop.put("user","root")

    prop.put("password","123456")

    //将数据追加到数据库

    personDataFrame.write.mode("append").jdbc("jdbc:mysql://192.168.10.1:3306/bigdata","bigdata.person", prop)

    //停止SparkContext

    sc.stop()

  }

}


Spark-Streaming 总结

 

官方文档:

http://spark.apache.org/docs/1.6.2/streaming-programming-guide.html

 

概述:

Spark Streaming类似于Apache Storm,用于流式数据的处理。根据其官方文档介绍,Spark Streaming有高吞吐量和容错能力强等特点。Spark Streaming支持的数据输入源很多,例如:KafkaFlumeTwitterZeroMQ和简单的TCP套接字等等。数据输入后可以用Spark的高度抽象原语如:mapreducejoinwindow等进行运算。而结果也能保存在很多地方,如HDFSredis, hbise数据库等。另外Spark Streaming也能和MLlib(机器学习)以及Graphx完美融合。

 

什么是DStream

Discretized StreamSpark Streaming的基础抽象,代表持续性的数据流和经过各种Spark原语操作后的结果数据流。在内部实现上,DStream是一系列连续的RDD来表示。每个RDD含有一段时间间隔内的数据,如下图:

 

对数据的操作也是按照RDD为单位来进行的

 

计算过程由Spark engine来完成

 

1-1) DStream相关操作

       DStream上的原语与RDD的类似,分为Transformations(转换)和Output Operations(输出)两种,此外转换操作中还有一些比较特殊的原语,如:updateStateByKey()transform()以及各种Window相关的原语。

 

 

1-2) Transformations on DStreams

reduce(func)

Return a new DStream of single-element RDDs by aggregating the elements in each RDD of the source DStream using a function func (which takes two arguments and returns one). The function should be associative so that it can be computed in parallel.

countByValue()

When called on a DStream of elements of type K, return a new DStream of (K, Long) pairs where the value of each key is its frequency in each RDD of the source DStream.

reduceByKey(func, [numTasks])

When called on a DStream of (K, V) pairs, return a new DStream of (K, V) pairs where the values for each key are aggregated using the given reduce function. Note: By default, this uses Spark's default number of parallel tasks (2 for local mode, and in cluster mode the number is determined by the config property spark.default.parallelism) to do the grouping. You can pass an optional numTasks argument to set a different number of tasks.

join(otherStream, [numTasks])

When called on two DStreams of (K, V) and (K, W) pairs, return a new DStream of (K, (V, W)) pairs with all pairs of elements for each key.

cogroup(otherStream, [numTasks])

When called on a DStream of (K, V) and (K, W) pairs, return a new DStream of (K, Seq[V], Seq[W]) tuples.

transform(func)

Return a new DStream by applying a RDD-to-RDD function to every RDD of the source DStream. This can be used to do arbitrary RDD operations on the DStream.

updateStateByKey(func)

Return a new "state" DStream where the state for each key is updated by applying the given function on the previous state of the key and the new values for the key. This can be used to maintain arbitrary state data for each key.

 

 

 

1-3) 特殊的Transformations

 

1.UpdateStateByKey Operation

UpdateStateByKey原语用于记录历史记录,上文中Word Count示例中就用到了该特性。若不用UpdateStateByKey来更新状态,那么每次数据进来后分析完成后,结果输出后将不在保存

 

2.Transform Operation

Transform原语允许DStream上执行任意的RDD-to-RDD函数。通过该函数可以方便的扩展Spark API。此外,MLlib(机器学习)以及Graphx也是通过本函数来进行结合的。

 

3.Window Operations

Window Operations有点类似于Storm中的State,可以设置窗口的大小和滑动窗口的间隔来动态的获取当前Steaming的允许状态

 

Output Operations on DStreams

Output Operations可以将DStream的数据输出到外部的数据库或文件系统,当某个Output Operations原语被调用时(与RDDAction相同),streaming程序才会开始真正的计算过程。

 

Output Operation

Meaning

print()

Prints the first ten elements of every batch of data in a DStream on the driver node running the streaming application. This is useful for development and debugging.

saveAsTextFiles(prefix, [suffix])

Save this DStream's contents as text files. The file name at each batch interval is generated based on prefix and suffix: "prefix-TIME_IN_MS[.suffix]".

saveAsObjectFiles(prefix, [suffix])

Save this DStream's contents as SequenceFiles of serialized Java objects. The file name at each batch interval is generated based on prefix and suffix: "prefix-TIME_IN_MS[.suffix]".

saveAsHadoopFiles(prefix, [suffix])

Save this DStream's contents as Hadoop files. The file name at each batch interval is generated based on prefix and suffix: "prefix-TIME_IN_MS[.suffix]".

foreachRDD(func)

The most generic output operator that applies a function, func, to each RDD generated from the stream. This function should push the data in each RDD to an external system, such as saving the RDD to files, or writing it over the network to a database. Note that the function func is executed in the driver process running the streaming application, and will usually have RDD actions in it that will force the computation of the streaming RDDs.

 

 

 

Spark Streaming实现实时WordCount

 

 

 

package day8.test

 

object StreamingWordCount {

  def main(args: Array[String]) {

    val conf =new SparkConf().setAppName("StreamingWordCount").setMaster("local[2]")

    //创建StreamingContext并设置产生批次的间隔时间

    val ssc =new StreamingContext(conf,Seconds(5))

    //从Socket端口中创建RDD

    val lines:ReceiverInputDStream[String] =ssc.socketTextStream("172.16.0.201",8888)

    val words:DStream[String] =lines.flatMap(_.split(" "))

    val wordAndOne:DStream[(String, Int)] =words.map((_,1))

    val result:DStream[(String, Int)] =wordAndOne.reduceByKey(_ + _)

    //打印

    result.print()

    //开启程序

    ssc.start()

    //等待结束

    ssc.awaitTermination()

  }

}

TCP端口中读取数据

需要的JAR

<dependency>

            <groupId>org.apache.spark</groupId>

            <artifactId>spark-sql_2.10</artifactId>

            <version>${spark.version}</version>

 </dependency>

 

 <dependency>

            <groupId>org.apache.spark</groupId>

            <artifactId>spark-streaming_2.10</artifactId>

            <version>${spark.version}</version>

 </dependency>

 

  <dependency>

            <groupId>org.apache.spark</groupId>

            <artifactId>spark-streaming-kafka_2.10</artifactId>

            <version>1.6.1</version>

  </dependency>

 

 

1-1) 安装nc

[root@hadoop1 ~]# yum install -y nc

Loaded plugins: fastestmirror, langpacks

Loading mirror speeds from cached hostfile

 * base: mirrors.neusoft.edu.cn

 * extras: mirrors.neusoft.edu.cn

 * updates: mirrors.nwsuaf.edu.cn

updates/7/x86_64/primary_db    FAILED                                            99% [======================================================================-]  70 kB/s | 7.0 MB  00:00:00 ETA

http://mirrors.nwsuaf.edu.cn/centos/7.2.1511/updates/x86_64/repodata/f444054b66ff65397e29b26ca982cb38039365a4dcb20acc5876a487ac88d867-primary.sqlite.bz2: [Errno -1] Metadata file does not match checksum

Trying other mirror.

^Cdates/7/x86_64/primary_db                                                      78% [========================================================               ]  76 kB/s | 5.6 MB  00:00:20 ETA

 

 

1-2)常用的命令

[root@hadoop1 ~]# nc -help

Ncat 6.40 ( http://nmap.org/ncat )

Usage: ncat [options] [hostname] [port]

 

Options taking a time assume seconds. Append 'ms' for milliseconds,

's' for seconds, 'm' for minutes, or 'h' for hours (e.g. 500ms).

  -4                         Use IPv4 only

  -6                         Use IPv6 only

  -U, --unixsock             Use Unix domain sockets only

  -C, --crlf                 Use CRLF for EOL sequence

  -c, --sh-exec <command>    Executes the given command via /bin/sh

  -e, --exec <command>       Executes the given command

      --lua-exec <filename>  Executes the given Lua script

  -g hop1[,hop2,...]         Loose source routing hop points (8 max)

  -G <n>                     Loose source routing hop pointer (4, 8, 12, ...)

  -m, --max-conns <n>        Maximum <n> simultaneous connections

  -h, --help                 Display this help screen

  -d, --delay <time>         Wait between read/writes

  -o, --output <filename>    Dump session data to a file

  -x, --hex-dump <filename>  Dump session data as hex to a file

  -i, --idle-timeout <time>  Idle read/write timeout

  -p, --source-port port     Specify source port to use

  -s, --source addr          Specify source address to use (doesn't affect -l)

  -l, --listen               Bind and listen for incoming connections

  -k, --keep-open            Accept multiple connections in listen mode

  -n, --nodns                Do not resolve hostnames via DNS

  -t, --telnet               Answer Telnet negotiations

  -u, --udp                  Use UDP instead of default TCP

      --sctp                 Use SCTP instead of default TCP

  -v, --verbose              Set verbosity level (can be used several times)

  -w, --wait <time>          Connect timeout

      --append-output        Append rather than clobber specified output files

      --send-only            Only send data, ignoring received; quit on EOF

      --recv-only            Only receive data, never send anything

      --allow                Allow only given hosts to connect to Ncat

      --allowfile            A file of hosts allowed to connect to Ncat

      --deny                 Deny given hosts from connecting to Ncat

      --denyfile             A file of hosts denied from connecting to Ncat

      --broker               Enable Ncat's connection brokering mode

      --chat                 Start a simple Ncat chat server

      --proxy <addr[:port]>  Specify address of host to proxy through

      --proxy-type <type>    Specify proxy type ("http" or "socks4")

      --proxy-auth <auth>    Authenticate with HTTP or SOCKS proxy server

      --ssl                  Connect or listen with SSL

      --ssl-cert             Specify SSL certificate file (PEM) for listening

      --ssl-key              Specify SSL private key (PEM) for listening

      --ssl-verify           Verify trust and domain name of certificates

      --ssl-trustfile        PEM file containing trusted SSL certificates

      --version              Display Ncat's version information and exit

 

See the ncat(1) manpage for full options, descriptions and usage examples

 

 

1-3)启动NC

[root@hadoop1 ~]# nc -lk 8888

dfhf

fbfr

ere

Gfr

 

1-4)图解

 

 

 

package day8.test

 

import org.apache.spark.HashPartitioner

 

object StreamingWordCount {

 

  val updateFunc = (it:Iterator[(String,Seq[Int], Option[Int])]) => {

    // 原因在于在方法中需要使用{}来包含运算的逻辑

    it.map {case (x,y,z) => (x,y.sum,z.getOrElse(0)) }

  }

 

  def main(args: Array[String]) {

    val conf =new SparkConf().setAppName("StreamingWordCount").setMaster("local[2]")

    //创建StreamingContext并设置产生批次的间隔时间,设置DStream批次时间间隔为5秒

    val ssc =new StreamingContext(conf,Seconds(5))

    //从Socket端口中创建RDD

    val lines:ReceiverInputDStream[String] =ssc.socketTextStream("172.16.0.201",8888)

    val words:DStream[String] =lines.flatMap(_.split(" "))

    val wordAndOne:DStream[(String, Int)] =words.map((_,1))

//    val result: DStream[(String,Int)] = wordAndOne.reduceByKey(_ + _)

    val result:DStream[(String, Int)] =wordAndOne.updateStateByKey(wordAndOne,new HashPartitioner(ssc.sparkContext.defaultParallelism),true))

    //打印

    result.print()

    //开启程序

    ssc.start()

    //等待结束

    ssc.awaitTermination()

  }

}

 

 

[root@hadoop1 ~]# nc -lk 8888

dufr

fhrhg

hgfr

dfef

efer

efr rr

rgr freg ee efe efe

 

 

 

Spark 结合flume

1-1 )安装

<dependency>

    <groupId>org.apache.spark</groupId>

    <artifactId>spark-streaming_2.10</artifactId>

    <version>1.6.2</version>

</dependency>

1-2flume-poll.conf的配置

# Name the components on this agent

a1.sources = r1

a1.sinks = k1

a1.channels = c1

 

# source

a1.sources.r1.type = spooldir

a1.sources.r1.spoolDir = /var/log/flume

a1.sources.r1.fileHeader = true

 

# Describe the sink

a1.sinks.k1.type = org.apache.spark.streaming.flume.sink.SparkSink

a1.sinks.k1.hostname = hadoop1

a1.sinks.k1.port = 8888

 

# Use a channel which buffers events in memory

a1.channels.c1.type = memory

a1.channels.c1.capacity = 1000

a1.channels.c1.transactionCapacity = 100

 

# Bind the source and sink to the channel

a1.sources.r1.channels = c1

a1.sinks.k1.channel = c1

 

 

 

[root@hadoop1 bin]# flume-ng agent -n a1 -c conf/ -f conf/flume-poll.conf -Dflume.root.logger=WARN,console

 

 

1-3)代码

package streams

 

import java.net.InetSocketAddress

 

import org.apache.spark.SparkConf

import org.apache.spark.storage.StorageLevel

import org.apache.spark.streaming.dstream.{ DStream, ReceiverInputDStream }

import org.apache.spark.streaming.flume.{ FlumeUtils, SparkFlumeEvent }

import org.apache.spark.streaming.{ Seconds, StreamingContext }

 

/**

 * Created by ZhaoXing on 2016/8/26.

 */

object FlumeStreamingWordCount {

 

  def main(args: Array[String]) {

    val conf =new SparkConf().setAppName("FlumeStreamingWordCount").setMaster("local[2]")

    //创建StreamingContext并设置产生批次的间隔时间

    val ssc =new StreamingContext(conf,Seconds(5))

    //从Socket端口中创建RDD

    val flumeStream:ReceiverInputDStream[SparkFlumeEvent] =FlumeUtils.createPollingStream(ssc, Array(new InetSocketAddress("hadoop1",8888)), StorageLevel.MEMORY_AND_DISK)

    //去取Flume中的数据

    val words =flumeStream.flatMap(x =>new String(x.event.getBody().array()).split(" "))

    val wordAndOne:DStream[(String, Int)] =words.map((_,1))

    val result:DStream[(String, Int)] =wordAndOne.reduceByKey(_ + _)

    //打印

    result.print()

    //开启程序

    ssc.start()

    //等待结束

    ssc.awaitTermination()

  }

}

 

1-4) 测试数据

[root@hadoop1 bin]# cp /home/hadoop/hadoop-2.6.4/etc/hadoop/*  /var/log/flume

 

 

Spark 结合Kafka

1-1) 启动Kafka

[root@hadoop1 bin]# ./kafka-server-start.sh ../config/server.properties > /dev/null 2> &1 &

 

 

1-2) 创建topic

bin/kafka-topics.sh --create --zookeeper hadoop1:2181 --replication-factor 1 --partitions 1 --topic lines

 

1-3) 查看所有的topic

[root@hadoop1 bin]# ./kafka-topics.sh  --list --zookeeper  hadoop1:2181

 

1-4) topic写入数据

[root@hadoop1 bin]# ./kafka-console-producer.sh  --broker-list hadoop1:9092 --topic lines

 

1-5)启动一个生产者发送消息

./kafka-console-producer.sh --broker-list  hadoop1:9092 --topic lines

 

1-6)启动spark-streaming应用程序

./spark-submit --class cn.****.spark.streaming.KafkaWordCount /root/streaming-1.0.jar hadoop1:2181  group1  wordcount   1

 

1-7)代码

package streams

 

import org.apache.spark.storage.StorageLevel

import org.apache.spark.streaming.dstream.ReceiverInputDStream

import org.apache.spark.{ HashPartitioner, SparkConf }

import org.apache.spark.streaming.kafka.KafkaUtils

import org.apache.spark.streaming.{ Seconds, StreamingContext }

 

/**

 * Created by root on 2016/5/21.

 */

object KafkaWordCount {

 

  val updateFunc = (iter:Iterator[(String,Seq[Int], Option[Int])]) => {

    //iter.flatMap(it=>Some(it._2.sum + it._3.getOrElse(0)).map(x=>(it._1,x)))

    iter.flatMap {case (x,y,z) =>Some(y.sum + z.getOrElse(0)).map(i => (x, i)) }

  }

 

  def main(args: Array[String]) {

    LoggerLevels.setStreamingLogLevels()

    val Array(zkQuorum,group,topics,numThreads) = args

    val sparkConf =new SparkConf().setAppName("KafkaWordCount").setMaster("local[2]")

    val ssc =new StreamingContext(sparkConf,Seconds(5))

    ssc.checkpoint("c://ck200")

    //"alog-2016-04-16,alog-2016-04-17,alog-2016-04-18"

    //"Array((alog-2016-04-16, 2), (alog-2016-04-17, 2), (alog-2016-04-18, 2))"

    val topicMap =topics.split(",").map((_,numThreads.toInt)).toMap

    val data:ReceiverInputDStream[(String, String)] =KafkaUtils.createStream(ssc,zkQuorum,group,topicMap, StorageLevel.MEMORY_AND_DISK_SER)

    val words =data.map(_._2).flatMap(_.split(" "))

    val wordCounts =words.map((_,1)).updateStateByKey(updateFunc,new HashPartitioner(ssc.sparkContext.defaultParallelism),true)

    wordCounts.print()

    ssc.start()

    ssc.awaitTermination()

  }

}

 

1-8)启动spark-submit

bin/spark-submit --class cn.****.spark.UrlCount --master spark://hadoop1:7077 --executor-memory 1G --total-executor-cores 2 /root/SparkDemo-1.0.jar hadoop1:2181, hadoop2:2181,  hadoop3::2181  group1  weblog   2

 

 

 

 

Elasticsearch 总结

 

 

官网 : https://www.elastic.co

概述:

    Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。

 

特点:

 

Elasticsearch不仅仅是Lucene和全文搜索,我们还能这样去描述它:

1、分布式的实时文件存储,每个字段都被索引并可被搜索

2、分布式的实时分析搜索引擎

3、可以扩展到上百台服务器,处理PB级结构化或非结构化数据

 

 

安装elasticsearch:

elasticsearch这是出于系统安全考虑设置的条件。由于ElasticSearch可以接收用户输入的脚本并且执行,为了系统安全考虑

 

[root@hadoop1elasticsearch]#wget https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.3.5/elasticsearch-2.3.5.tar.gz

 

[root@hadoop1 elasticsearch]# chmod a+x elasticsearch-2.1.1.tar.gz

[root@hadoop1 elasticsearch]# tar -zxvf elasticsearch-2.1.1.tar.gz

[root@hadoop1 bin]# groupadd elsearch

[root@hadoop1 bin]# useradd es -g elsearch -p elasticsearch

[root@hadoop1 bin]# cd ../../

[root@hadoop1 elasticsearch]# chown -R es:elsearch  elasticsearch-2.1.1

[root@hadoop1 elasticsearch]# mkdir /path/to/data

[root@hadoop1 elasticsearch]# mkdir -p /path/to/logs

[root@hadoop1 /]# chown -R es:elsearch  path/

[root@hadoop1 elasticsearch]# su es

[elsearch@hadoop1 elasticsearch]$ cd elasticsearch-2.1.1/

[elsearch@hadoop1 elasticsearch-2.1.1]$ cd bin/

 

 

查看进程:

[root@localhost nginx]# jps

53668 -- process information unavailable

53752 Jps

安装elasticsearch-head:

[elsearch@hadoop1 bin]$ ./plugin install mobz/elasticsearch-head

-> Installing mobz/elasticsearch-head...

Trying https://github.com/mobz/elasticsearch-head/archive/master.zip ...

Downloading .........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................DONE

Verifying https://github.com/mobz/elasticsearch-head/archive/master.zip checksums if available ...

NOTE: Unable to verify checksum for downloaded plugin (unable to find .sha1 or .md5 file to verify)

Installed head into /home/elasticsearch/elasticsearch-2.1.1/plugins/head

 

 

配置集群:

[root@hadoop1 config]# vi elasticsearch.yml

4.修改配置

vi /bigdata/elasticsearch-2.3.1/config/elasticsearch.yml

#集群名称,通过组播的方式通信,通过名称判断属于哪个集群

cluster.name: bigdata

#节点名称,要唯一

node.name: es-1

#数据存放位置

path.data: /data/es/data

#日志存放位置

path.logs: /data/es/logs

#es绑定的ip地址

network.host: 172.16.0.14

#初始化时可进行选举的节点

discovery.zen.ping.unicast.hosts: ["hadoop1",  "hadoop2",  "hadoop3"]

 

 

[root@hadoop1 elasticsearch]# scp elasticsearch-2.1.1 hadoop2:$PWD

[root@hadoop1 elasticsearch]# scp elasticsearch-2.1.1 hadoop3:$PWD

 

 

实例:

# ======================== Elasticsearch Configuration =========================

#

# NOTE: Elasticsearch comes with reasonable defaults for most settings.

#       Before you set out to tweak and tune the configuration, make sure you

#       understand what are you trying to accomplish and the consequences.

#

# The primary way of configuring a node is via this file. This template lists

# the most important settings you may want to configure for a production cluster.

#

# Please see the documentation for further information on configuration options:

# <http://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html>

#

# ---------------------------------- Cluster -----------------------------------

#

# Use a descriptive name for your cluster:

#

  cluster.name: my-application

#

# ------------------------------------ Node ------------------------------------

#

# Use a descriptive name for the node:

#

  node.name: node-1

#

# Add custom attributes to the node:

#

# node.rack: r1

#

# ----------------------------------- Paths ------------------------------------

#

# Path to directory where to store the data (separate multiple locations by comma):

#

  path.data: /path/to/data

#

# Path to log files:

#

  path.logs: /path/to/logs

#

# ----------------------------------- Memory -----------------------------------

#

# Lock the memory on startup:

#

# bootstrap.mlockall: true

#

# Make sure that the `ES_HEAP_SIZE` environment variable is set to about half the memory

# available on the system and that the owner of the process is allowed to use this limit.

#

# Elasticsearch performs poorly when the system is swapping the memory.

#

# ---------------------------------- Network -----------------------------------

#

# Set the bind address to a specific IP (IPv4 or IPv6):

#

  network.host: 192.168.215.134

#

# Set a custom port for HTTP:

#

  http.port: 9200

#

# For more information, see the documentation at:

# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-network.html>

#

# --------------------------------- Discovery ----------------------------------

#

# Pass an initial list of hosts to perform discovery when new node is started:

# The default list of hosts is ["127.0.0.1", "[::1]"]

#

  discovery.zen.ping.unicast.hosts: ["hadoop1", "hadoop2", "hadoop3"]

#

# Prevent the "split brain" by configuring the majority of nodes (total number of nodes / 2 + 1):

#

# discovery.zen.minimum_master_nodes: 3

#

# For more information, see the documentation at:

# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html>

#

# ---------------------------------- Gateway -----------------------------------

#

# Block initial recovery after a full cluster restart until N nodes are started:

#

# gateway.recover_after_nodes: 3

#

# For more information, see the documentation at:

# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-gateway.html>

#

# ---------------------------------- Various -----------------------------------

#

# Disable starting multiple nodes on a single system:

#

# node.max_local_storage_nodes: 1

#

# Require explicit names when deleting indices:

#

# action.destructive_requires_name: true

 

 

 

前台运行:

[elsearch@hadoop1 bin]$ ./elasticsearch

[2016-08-27 14:18:15,854][INFO ][node                     ] [Trader] version[2.1.1], pid[7894], build[40e2c53/2015-12-15T13:05:55Z]

[2016-08-27 14:18:15,855][INFO ][node                     ] [Trader] initializing ...

[2016-08-27 14:18:16,743][INFO ][plugins                  ] [Trader] loaded [], sites []

[2016-08-27 14:18:16,993][INFO ][env                      ] [Trader] using [1] data paths, mounts [[/ (rootfs)]], net usable_space [11gb], net total_space [17.6gb], spins? [unknown], types [rootfs]

[2016-08-27 14:18:22,613][INFO ][node                     ] [Trader] initialized

[2016-08-27 14:18:22,613][INFO ][node                     ] [Trader] starting ...

[2016-08-27 14:18:22,856][INFO ][transport                ] [Trader] publish_address {127.0.0.1:9300}, bound_addresses {127.0.0.1:9300}, {[::1]:9300}

[2016-08-27 14:18:22,874][INFO ][discovery                ] [Trader] elasticsearch/HW-4e50uSkeSFEeNgEkMJQ

[2016-08-27 14:18:26,020][INFO ][cluster.service          ] [Trader] new_master {Trader}{HW-4e50uSkeSFEeNgEkMJQ}{127.0.0.1}{127.0.0.1:9300}, reason: zen-disco-join(elected_as_master, [0] joins received)

[2016-08-27 14:18:26,242][INFO ][http                     ] [Trader] publish_address {127.0.0.1:9200}, bound_addresses {127.0.0.1:9200}, {[::1]:9200}

[2016-08-27 14:18:26,242][INFO ][node                     ] [Trader] started

[2016-08-27 14:18:26,257][INFO ][gateway                  ] [Trader] recovered [0] indices into cluster_state

^C[2016-08-27 14:21:54,274][INFO ][node                     ] [Trader] stopping ...

[2016-08-27 14:21:54,361][INFO ][node                     ] [Trader] stopped

[2016-08-27 14:21:54,362][INFO ][node                     ] [Trader] closing ...

[2016-08-27 14:21:54,371][INFO ][node                     ] [Trader] closed

后端运行:

./elasticsearch -d

 

 

 

 

 

 

 

http://hadoop1:9200/_plugin/head/

停止ES

若是要停止ES服务,则输入sh elasticsearch stop,则输出如下图,则ES成功停止。

kill `ps -ef | grep Elasticsearch | grep -v grep | awk '{print $2}'`

 

代码实例:

 

 

实体类:

package com.sdcet;

 

public class Medicine {

private Integer id;

private String name;

private String function;

 

public Integer getId() {

return id;

}

 

public void setId(Integerid) {

this.id =id;

}

 

public String getName() {

return name;

}

 

public void setName(Stringname) {

this.name =name;

}

 

public String getFunction() {

return function;

}

 

public void setFunction(Stringfunction) {

this.function =function;

}

 

public Medicine() {

}

 

public Medicine(Integerid, Stringname, Stringfunction) {

this.id =id;

this.name =name;

this.function =function;

}

 

@Override

public String toString() {

return "Medicine [id=" +id +", name=" +name +", function="

+ function + "]";

}

 

}

 

工具类:

package com.sdcet;

 

import java.io.IOException;

import org.elasticsearch.common.xcontent.XContentBuilder;

import org.elasticsearch.common.xcontent.XContentFactory;

 

public class JsonUtil {

 

/**

 * 实现将实体对象转换成json对象

 *

 * @param medicine

 *            Medicine对象

 * @return

 */

public static String obj2JsonData(Medicinemedicine) {

String jsonData = null;

try {

// 使用XContentBuilder创建json数据

XContentBuilder jsonBuild = XContentFactory.jsonBuilder();

jsonBuild.startObject().field("id",medicine.getId())

.field("name", medicine.getName())

.field("funciton", medicine.getFunction()).endObject();

jsonData = jsonBuild.string();

System.out.println(jsonData);

} catch (IOExceptione) {

e.printStackTrace();

}

return jsonData;

}

}

 

工厂类:

package com.sdcet;

 

import java.util.ArrayList;

import java.util.List;

 

public class DataFactory {

public static DataFactorydataFactory =new DataFactory();

 

private DataFactory() {

 

}

 

public DataFactory getInstance() {

return dataFactory;

}

 

public static List<String> getInitJsonData() {

List<String> list = new ArrayList<String>();

for (int i = 0;i < 10;i++) {

String data1 = JsonUtil.obj2JsonData(new Medicine(i,"银花 感冒 颗粒",

"功能主治:银花感冒颗粒 ,头痛,清热,解表,利咽。"));

list.add(data1);

}

return list;

}

}

 

 

主类:

package com.sdcet;

 

import java.net.InetAddress;

import java.net.UnknownHostException;

import java.util.ArrayList;

import java.util.List;

 

import org.elasticsearch.action.index.IndexRequestBuilder;

import org.elasticsearch.action.index.IndexResponse;

import org.elasticsearch.action.search.SearchResponse;

import org.elasticsearch.client.Client;

import org.elasticsearch.client.transport.TransportClient;

import org.elasticsearch.common.transport.InetSocketTransportAddress;

import org.elasticsearch.index.query.QueryBuilder;

import org.elasticsearch.index.query.QueryBuilders;

import org.elasticsearch.search.SearchHit;

import org.elasticsearch.search.SearchHits;

 

public class ElasticSearchHandler {

private Client client;

 

public ElasticSearchHandler() {

// 使用本机做为节点

this("127.0.0.1");

}

 

public ElasticSearchHandler(StringipAddress) {

try {

client = TransportClient

.builder()

.build()

.addTransportAddress(

new InetSocketTransportAddress(InetAddress

.getByName("127.0.0.1"), 9300));

} catch (UnknownHostExceptione) {

e.printStackTrace();

}

// 集群连接超时设置

/*

 * Settings settings =

 * ImmutableSettings.settingsBuilder().put("client.transport.ping_timeout"

 * , "10s").build(); client = new TransportClient(settings);

 */

// client = new TransportClient()

// .addTransportAddress(new InetSocketTransportAddress(ipAddress,

// 9200));

 

}

 

/**

 * 建立索引,索引建立好之后,会在elasticsearch-0.20.6\data\elasticsearch\nodes\0创建所以你看

 *

 * @param indexName

 *            为索引库名,一个es集群中可以有多个索引库。名称必须为小写

 * @param indexType

 *            Type为索引类型,是用来区分同索引库下不同类型的数据的,一个索引库下可以有多个索引类型。

 * @param jsondata

 *            json格式的数据集合

 *

 * @return

 */

public void createIndexResponse(Stringindexname, Stringtype,

List<String> jsondata) {

// 创建索引库 需要注意的是.setRefresh(true)这里一定要设置,否则第一次建立索引查找不到数据

IndexRequestBuilder requestBuilder =client.prepareIndex(indexname,

type).setRefresh(true);

for (int i = 0;i <jsondata.size();i++) {

System.out.println(jsondata.get(i) +"-");

requestBuilder.setSource(jsondata.get(i)).execute().actionGet();

}

 

}

 

/**

 * 创建索引

 *

 * @param client

 * @param jsondata

 * @return

 */

public IndexResponse createIndexResponse(Stringindexname, Stringtype,

String jsondata) {

IndexResponse response = client.prepareIndex(indexname, type)

.setSource(jsondata).execute().actionGet();

return response;

}

 

/**

 * 执行搜索

 *

 * @param queryBuilder

 * @param indexname

 * @param type

 * @return

 */

public List<Medicine> searcher(QueryBuilderqueryBuilder, Stringindexname,

String type) {

List<Medicine> list = new ArrayList<Medicine>();

SearchResponse searchResponse = client.prepareSearch(indexname)

.setTypes(type).setQuery(queryBuilder).execute().actionGet();

// SearchHits hits = searchResponse.hits();

SearchHits hits = searchResponse.getHits();

System.out.println("查询到记录数=" +hits.getTotalHits());

SearchHit[] searchHists = hits.getHits();

if (searchHists.length > 0) {

for (SearchHit hit : searchHists) {

Integer id = (Integer) hit.getSource().get("id");

String name = (String) hit.getSource().get("name");

String function = (String) hit.getSource().get("funciton");

list.add(new Medicine(id,name,function));

}

}

return list;

}

 

public static void main(String[]args) {

ElasticSearchHandler esHandler = new ElasticSearchHandler();

List<String> jsondata = DataFactory.getInitJsonData();

String indexname = "indexdemo";

String type = "typedemo";

// 创建索引

// esHandler.createIndexResponse(indexname, type,jsondata);

// 查询条件F

// QueryBuilder queryBuilder = QueryBuilders.fuzzyQuery("name", "感冒");

QueryBuilder queryBuilder = QueryBuilders.functionScoreQuery();

// QueryBuilder queryBuilder = QueryBuilders.fieldQuery("name", "感冒");

// QueryBuilders.se

/*

 * QueryBuilder queryBuilder = QueryBuilders.boolQuery()

 * .must(QueryBuilders.termQuery("id", 1));

 */

List<Medicine> result = esHandler.searcher(queryBuilder,indexname,

type);

for (int i = 0;i <result.size();i++) {

Medicine medicine = result.get(i);

System.out.println("(" +medicine.getId() +")药品名称:"

+ medicine.getName() + "\t\t" + medicine.getFunction());

}

}

}

 

 

 

查看效果:

查看执行时间以及条数,感觉还可以

 

 

 

 

 

 

 

查询语句:

must: 文档必须完全匹配条件
should: should下面会带一个以上的条件,至少满足一个条件,这个文档就符合shouldmust_not: 文档必须不匹配条件

 

详细请见http://www.cnblogs.com/yjf512/p/4897294.html

1-1精确查找:

{

  "query": {

    "match_phrase": {

      "name": "小青"

    }

  }

}

 

1-2)模糊查找:

{

  "query": {

    "match_phrase": {

      "name": "小青"

    }

  }

}

 

 

1-3)查找多个字段的数据

{

  "query": {

    "multi_match": {

      "query": "小张感冒",

      "fields": [

        "name",

        "funciton"

      ]

    }

  }

}

 

 

1-4) 我们希望完全匹配的文档占的评分比较高,则需要使用best_fields

 

{

  "query": {

    "multi_match": {

      "query": "小张感冒",

      "type": "best_fields",

      "fields": [

        "name",

        "funciton"

      ],

      "tie_breaker": 0.1

    }

  }

}

 

意思就是完全匹配"宝马 发动机"的文档评分会比较靠前,如果只匹配宝马的文档评分乘以0.3的系数

 

 

1-5) 多字段匹配

  

{

  "query": {

    "multi_match": {

      "query": "小张感冒",

      "type": "most_fields",

      "fields": [

        "name",

        "funciton"

      ],

      "tie_breaker": 0.1

    }

  }

}

 

1-6我们会希望这个词条的分词词汇是分配到不同字段中的,那么就使用cross_fields

{

  "query": {

    "term": {

      "content": "1"

    }

  }

}

 

 

1-7)高亮设置

 

{

  "query": {

    "match_phrase": {

      "name": "大青叶"

    }

  },

  "highlight": {

    "fields": {

      "name": {}

    }

  }

}

 

1-8URL查询

http://localhost:9200/indexdemo/typedemo/AVbVXZ5YQST3amZyeBjV?_source=id,name

 

 

 

 

 

Logstash 总结

 

官网:https://www.elastic.co/products/logstash

 

概述:

Logstash 主要收集一些数据并把数据保存到数据库,或者文件中或者其他的介质中。

特点:

Logstash收集日志的能力很强轻,不限日志来源,不限日志形式,对系统监控,问题分析应该很好,而且对技术要求也不高,实现的语言是ruby

安装:

[root@hadoop1 bin]# tar -zxvf logstash-2.3.1.tar.gz

[root@hadoop1 logstash]# cd logstash-2.3.1/

[root@hadoop1 logstash-2.3.1]# cd bin/

 

[root@hadoop1 bin]# ./logstash -help

Usage:

    /bin/logstash agent [OPTIONS]

 

Options:

    -f, --config CONFIG_PATH      Load the logstash config from a specific file

                                  or directory.  If a directory is given, all

                                  files in that directory will be concatenated

                                  in lexicographical order and then parsed as a

                                  single config file. You can also specify

                                  wildcards (globs) and any matched files will

                                  be loaded in the order described above.

    -e CONFIG_STRING              Use the given string as the configuration

                                  data. Same syntax as the config file. If no

                                  input is specified, then the following is

                                  used as the default input:

                                  "input { stdin { type => stdin } }"

                                  and if no output is specified, then the

                                  following is used as the default output:

                                  "output { stdout { codec => rubydebug } }"

                                  If you wish to use both defaults, please use

                                  the empty string for the '-e' flag.

                                   (default: "")

    -w, --pipeline-workers COUNT  Sets the number of pipeline workers to run.

                                   (default: 1)

    -b, --pipeline-batch-size SIZE Size of batches the pipeline is to work in.

                                   (default: 125)

    -u, --pipeline-batch-delay DELAY_IN_MS When creating pipeline batches, how long to wait while polling

                                  for the next event.

                                   (default: 5)

    --filterworkers COUNT         DEPRECATED. Now an alias for --pipeline-workers and -w

    -l, --log FILE                Write logstash internal logs to the given

                                  file. Without this flag, logstash will emit

                                  logs to standard output.

    -v                            Increase verbosity of logstash internal logs.

                                  Specifying once will show 'informational'

                                  logs. Specifying twice will show 'debug'

                                  logs. This flag is deprecated. You should use

                                  --verbose or --debug instead.

    --quiet                       Quieter logstash logging. This causes only

                                  errors to be emitted.

    --verbose                     More verbose logging. This causes 'info'

                                  level logs to be emitted.

    --debug                       Most verbose logging. This causes 'debug'

                                  level logs to be emitted.

    --debug-config                translation missing: en.logstash.runner.flag.debug_config (default: false)

    -V, --version                 Emit the version of logstash and its friends,

                                  then exit.

    -p, --pluginpath PATH         A path of where to find plugins. This flag

                                  can be given multiple times to include

                                  multiple paths. Plugins are expected to be

                                  in a specific directory hierarchy:

                                  'PATH/logstash/TYPE/NAME.rb' where TYPE is

                                  'inputs' 'filters', 'outputs' or 'codecs'

                                  and NAME is the name of the plugin.

    -t, --configtest              Check configuration for valid syntax and then exit.

    --[no-]allow-unsafe-shutdown  Force logstash to exit during shutdown even

                                  if there are still inflight events in memory.

                                  By default, logstash will refuse to quit until all

                                  received events have been pushed to the outputs. (default: false)

    -r, --[no-]auto-reload        Monitor configuration changes and reload

                                  whenever it is changed.

                                  NOTE: use SIGHUP to manually reload the config

                                   (default: false)

    --reload-interval RELOAD_INTERVAL How frequently to poll the configuration location

                                  for changes, in seconds.

                                   (default: 3)

    --allow-env                   EXPERIMENTAL. Enables templating of environment variable

                                  values. Instances of "${VAR}" in strings will be replaced

                                  with the respective environment variable value named "VAR".

                                   (default: false)

-h, --help                    print help

 

 

 

Kafka 实例:

[root@hadoop1 bin]# ./logstash -f flow-kafka.conf

Settings: Default pipeline workers: 1

Pipeline main started

 

[root@hadoop1 bin]# ./logstash -f kafka-es.conf

Settings: Default pipeline workers: 1

Pipeline main started

 

 

[root@hadoop1 ~]# kafka-console-consumer.sh --zookeeper hadoop1:2181 --from-beginning --topic folwKafka

 

1472703222.048 192.168.215.1         Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36  

1472703222.073 192.168.215.1         Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36  

 

 

 

配置文件实例:

[root@hadoop1 bin]# cat flow-es.conf

 

input {

file {

type => "logstash_es"

                # path => "/log/*/*.log"

                path => "/usr/local/nginx/logs/access.log"

discover_interval => 1

start_position => "beginning"

}

}

 

output {

    elasticsearch {

index => "logstash-es-%{+YYYY.MM.dd}"

        hosts => ["hadoop1:9200", "hadoop2:9200", "hadoop3:9200"]

    }

}

 

 

 

 

[root@hadoop1 bin]# cat flow-kafka.conf

input {

  file {

    path => "/usr/local/nginx/logs/access.log"

    discover_interval => 5

    start_position => "beginning"

  }

}

 

output{

 kafka {

    topic_id => "logTest"

     codec => plain {

        format => "%{message}"

      }

    bootstrap_servers => "hadoop1:9092,hadoop2:9092,hadoop3:9092"

 }

}

 

 

[root@hadoop1 bin]# cat kafka-es.conf

input {

  kafka {

    type => "kafka-es"

    auto_offset_reset => "smallest"

    codec => plain {

      charset => "GB2312"

    }

group_id => "es"

topic_id => "logTest"

zk_connect => "hadoop1:2181,hadoop2:2181,hadoop3:2181"

  }

}

 

filter {

    mutate {

     split => { "message" => "  " }

      add_field => {

       "ip_address" => "%{message[2]}"

        "liulanqi" => "%{message[3]}"

     }

     remove_field => [ "message" ]

  }

}

 

output {

    elasticsearch {

      index => "kafka-flow-es-%{+YYYY.MM.dd}"

  codec => plain {

        charset => "GB2312"

      }

      hosts => ["hadoop1:9200", "hadoop2:9200", "hadoop3:9200"]

    }

}

 

 

其他带有匹配实例:

input {

  kafka {

    type => "accesslogs"

    codec => "plain"

    auto_offset_reset => "smallest"

    group_id => "elas1"

    topic_id => "accesslogs"

    zk_connect => "hadoop1:2181,hadoop2:2181,hadoop3:2181"

  }

 

  kafka {

    type => "gamelogs"

    auto_offset_reset => "smallest"

    codec => "plain"

    group_id => "elas2"

    topic_id => "gamelogs"

    zk_connect => "hadoop1:2181,hadoop2:2181,hadoop3:2181"

  }

}

 

filter {

  if [type] == "accesslogs" {

    json {

      source => "message"

  remove_field => [ "message" ]

  target => "access"

    }

  }

 

  if [type] == "gamelogs" {

    mutate {

      split => { "message" => " " }

      add_field => {

        "event_type" => "%{message[3]}"

        "current_map" => "%{message[4]}"

        "current_X" => "%{message[5]}"

        "current_y" => "%{message[6]}"

        "user" => "%{message[7]}"

        "item" => "%{message[8]}"

        "item_id" => "%{message[9]}"

        "current_time" => "%{message[12]}"

     }

     remove_field => [ "message" ]

   }

  }

}

 

output {

  if [type] == "accesslogs" {

    elasticsearch {

      index => "accesslogs"

  codec => "json"

      hosts => ["hadoop1:9200", "hadoop2:9200", "hadoop3:9200"]

    }

  }

 

  if [type] == "gamelogs" {

    elasticsearch {

      index => "gamelogs"

      codec => plain {

        charset => "UTF-16BE"

      }

      hosts => ["hadoop1:9200", "hadoop2:9200", "hadoop3:9200"]

    }

  }

}

 

 

前台启动:

[root@hadoop1 bin]# ./logstash -f kafka-es.conf

 

后台启动:

[root@hadoop1 bin]# ./logstash -f kafka-es.conf    /dev/null 2>&1 &

 

 

查看ES保存的数据:

 

 

Kibana 总结

 

概述:

Kibana 主要是查看ES状态的一个工具,因有较强的图标界面的操作受到了广大用户的喜爱。

 

安装:

官网:https://www.elastic.co/

中文网站:http://kibana.logstash.es/content/kibana/v4/setup.html

 

[root@hadoop1 local]# tar -zxvf kibana-4.5.0-linux-x64.tar.gz

[root@hadoop1 local]#  mv kibana-4.5.0-linux-x64/ kibana

[root@hadoop1 local]# cd kibana

[root@hadoop1 kibana]# cd config/

[root@hadoop1 config]# vi kibana.yml

加入以下配置:

# The Elasticsearch instance to use for all your queries.

  elasticsearch.url: "http://hadoop1:9200"

 

 

启动:

先要启动elasticsearch

1-1)前台启动:

[root@hadoop1 bin]# ./kibana

  log   [22:06:10.964] [info][status][plugin:kibana] Status changed from uninitialized to green - Ready

  log   [22:06:11.027] [info][status][plugin:elasticsearch] Status changed from uninitialized to yellow - Waiting for Elasticsearch

  log   [22:06:11.056] [info][status][plugin:kbn_vislib_vis_types] Status changed from uninitialized to green - Ready

  log   [22:06:11.074] [info][status][plugin:markdown_vis] Status changed from uninitialized to green - Ready

  log   [22:06:11.087] [info][status][plugin:metric_vis] Status changed from uninitialized to green - Ready

  log   [22:06:11.101] [info][status][plugin:spyModes] Status changed from uninitialized to green - Ready

  log   [22:06:11.180] [info][status][plugin:statusPage] Status changed from uninitialized to green - Ready

  log   [22:06:11.196] [info][status][plugin:table_vis] Status changed from uninitialized to green - Ready

  log   [22:06:11.213] [info][status][plugin:elasticsearch] Status changed from yellow to green - Kibana index ready

  log   [22:06:11.233] [info][listening] Server running at http://0.0.0.0:5601

1-2)后台启动

[root@hadoop1 bin]# ./kibana /dev/null 2>&1 &

 

 

查看界面

端口是:5601

 

1-1kibana的界面

 

 

 

 

 

 

 

1-2Elasticsearch的数据库的信息

 

 

 

 

1-3)数据查看



0 0
原创粉丝点击