快学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-1)JDK安装
[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 mysql-libs
[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虚拟机运行时数据区域被分为五个区域:
l 方法区(Method Area):用于存储类结构信息的地方,包括常量池、静态变量、构造函数等。虽然JVM规范把方法区描述为堆的一个逻辑部分,它有个别名non-heap(非堆)。方法区还包含一个运行时常量池。
l java堆(Heap):存储java实例或者对象的地方。这块是GC的主要区域(后面解释)。从存储的内容我们可以很容易知道,方法区和堆是被所有java线程共享的。
l java栈(Stack):java栈总是和线程关联在一起,每当创建一个线程时,JVM就会为这个线程创建一个对应的java栈。在这个java栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,用于存储局部变量表、操作栈、方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程。所以java栈是线程私有的。
l 程序计数器(PC Register):用于保存当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方,程序计数器也是线程私有的。
l 本地方法栈(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区(From和to)
当Eden区被对象填满时,就会执行Minor GC,并把所有存活下来的对象转移到其中一个survivor区(from区或to区)。这样在一段时间内,总会有一个空的survivor区。经过多次GC周期后,仍然存活下来的对象会被转移到年老代内存空间。通常这是在年轻代有资格提升到年老代前通过设定年龄阈值来完成的。
需要注意,Survivor的两个区是对称的,没先后关系,from和to是相对的。
年老代<标记-整理(Mark-Sweep)>:年轻代中经历了N次回收后仍没被清除的对象
可以说他们都是久经沙场而不亡的一代,都是生命周期较长的对象。对于年老代和永久代,就不能再采用像年轻代中那样搬移腾挪的回收算法。
在老年代内存被占满时通常会触发Full GC,回收整个堆内存。
永久代:用于存放静态文件,比如java类、方法等。
持久代对垃圾回收没有显著的影响。
分代回收的效果图如下:
1-3)垃圾收集器概览
1-4)Serial收集器
1、是一个单线程的收集器,“Stop The World”
2、对于运行在Client模式下的虚拟机来说是一个很好的选择
4、简单而高效
1-5)ParNew收集器
1、Serial串行收集器的多线程版本
2、单CPU不如Serial
3、Server模式下新生代首选,目前只有它能与CMS收集器配合工作
4、使用-XX:+UseConcMarkSweepGC选项后的默认新生代收集器,也可以使用-XX:+UseParNewGC选项来强制指定它。
5、-XX:ParallelGCThreads:限制垃圾收集的线程数。
1-6)Parallel Scavenge收集器
新生代收集器,复制算法,并行的多线程收集器
目标是达到一个可控制的吞吐量(Throughput)。
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
两个参数用于精确控制吞吐量:
-XX:MaxGCPauseMillis是控制最大垃圾收集停顿时间
-XX:GCTimeRatio直接设置吞吐量大小
-XX:+UseAdaptiveSizePolicy:动态设置新生代大小、Eden与Survivor区的比例、晋升老年代对象年龄
并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。
1-7)Serial Old收集器
1、Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。
2、主要意义也是在于给Client模式下的虚拟机使用。
3、如果在Server模式下,那么它主要还有两大用途:
一种用途是在JDK 1.5以及之前的版本中与Parallel Scavenge收集器搭配使用[1],
另一种用途就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。
1-8)Parallel Old收集器
1、Parallel Scavenge并行收集器的老年代版本,使用多线程和“标记-整理”算法。
2、在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel 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、当今收集器技术发展的最前沿成果之一
2、G1是一款面向服务端应用的垃圾收集器。
3、优点:
并行与并发:充分利用多CPU、多核环境下的硬件优势
分代收集:不需要其他收集器配合就能独立管理整个GC堆
空间整合:“标记—整理”算法实现的收集器,局部上基于“复制”算法不会产生内存空间碎片
可预测的停顿:能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒
4、G1收集器的运作大致可划分为以下几个步骤:
初始标记:标记一下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
Client、Server模式默认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,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的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 有两种角色分别是leader与follower ,在选举时机器超过一般即可存活,一般的机器配置成单个数
特性:
1、每台机器上保存的数据一致,用户不管访问那台机器获取的数据信息都是一致的
2、分布式的读写,更细请求转发都是由leader转发的
3、数据更新原子性,一次数据的更新要不成功要不失败
4、每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识
选举机制:
那么,初始化的时候,是按照上述的说明进行选举的,但是当zookeeper运行了一段时间之后,有机器down掉,重新选举时,选举过程就相对复杂了。
需要加入数据version、leader 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)(断开连接不删除)
2、Znode有四种形式的目录节点(默认是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-1)String存放到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-3)Redis购物车的使用
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-4)Redis集合的使用
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-5)zincrby 方法的使用
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等大数据处理相关的服务安装和监控管理的组件,对集群中主机、Hadoop、Hive、Spark等服务的安装配置管理做了极大简化。
安装:
[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 系统详解:
概述:
1、HDFS分为两大角色:Namenode , datanode , Secondary Name
2、nameNode保存数据的元数据
3、DataNode负责管理用户的文件的数据块,文件会按照块的大小保存到不同的DataNode ,每一个数据都有很多块的副本,保存在不同的机器上。
4、dataNode会定期的向namenode虎豹闲杂保存到数据的情况
5、HDFS的内部工作机制对客户端保持透明,客户端请求访问HDFS都是通过向namenode申请来进行
用户上传文件思路:
1、客户端向nameNode发送要上传文件的请求,并把文件分为128M的块
2、nameNode 返回给用户是否能上传数据的状态
3、加入用户端需要上传一个120M的文件,客户端会通过Rpc请求NameNode,并返回需要上传给那些DataNode(分配机器的距离以及空间的大小等),会选择离namenode比较近的机器分配,同机架的有限.
4、客户端请求建立block传输管道chnnel上传数据
5、在上传是datanode会与其他的机器建立连接并把数据块传送到其他的机器上
6、dataNode向namenode汇报自己的储存情况以及自己的信息
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的附带作用
namenode和secondary namenode的工作目录存储结构完全相同,所以,当namenode故障退出需要重新恢复时,可以从secondary namenode的工作目录中将fsimage拷贝到namenode的工作目录,以恢复namenode的元数据
DataNode 工作机制:
1-1)dataNode工作机制
Data会定期的向namenode汇报自己的block储存的信息,被称为心跳,因为储存的信息特别重要,配置现象如下:
<property>
<name>dfs.blockreport.intervalMsec</name>
<value>3600000</value>
<description>Determines block reporting interval in milliseconds.</description>
</property>
1-2)namenode故障判断
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-1)Datanode
http://hadoop1:50070/
1-2)Nodes
http://hadoop1:8088/
1-3)SecondaryNameNode
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 {
// 构造一个配置参数对象,设置一个参数:我们要访问的hdfs的URI
// 从而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-1)hadoop优化思路
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、前提是需要安装HADOOP与Mysql
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、解决包冲突问题:
hive的lib目录中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
(hadoop01是hiveserver2所启动的那台主机名,端口默认是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-7)DDL其他操作
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-8)DML其他操作
1)Load 的使用
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
2)INSERT的使用
利用查询语句,将查询结果插入新的表
INSERT OVERWRITE [INTO] TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 FROM from_statement
插入一条数据
INSERT INTO TABLE VALUES(XX,YY,ZZ);
3)SELECT的使用
ü 语法结构
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]
注:1、order by会对输入做全局排序,因此只有一个reducer,会导致当输入规模较大时,需要较长的计算时间。
2、sort by不是全局排序,其在数据进入reducer前完成排序。因此,如果用sort by进行排序,并且设置mapred.reduce.tasks>1,则sort by只保证每个reducer的输出有序,不保证全局有序。
3、distribute by(字段)根据指定的字段将数据分到不同的reducer,且分发算法是hash散列。
4、Cluster 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-1)split的使用
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的切的索引的问题,0和1是相同的结果
8-2)get_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-1)HIVE的参数
语法结构
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-1)java开发实例
UDF 作用于单个数据行,产生一个数据行作为输出。(数学函数,字符串函数)
UDAF(用户定义聚集函数):接收多个输入数据行,并产生一个输出数据行。(count,max)
简单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包添加到hive的classpath
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是一个分布式、可靠、和高可用的海量日志采集、聚合和传输的系统。
u Flume可以采集文件,socket数据包等各种形式源数据,又可以将采集到的数据输出到HDFS、hbase、hive、kafka等众多外部存储系统中
u 一般的采集需求,通过对flume的简单配置即可实现
u Flume针对特殊场景也具备良好的自定义扩展能力,因此,flume可以适用于大部分的日常数据采集场景
运行机制:
1、 Flume分布式系统中最核心的角色是agent,flume采集系统就是由一个个agent所连接起来形成
2、 每一个agent相当于一个数据传递员,内部有三个组件:
a) Source:采集源,用于跟数据源对接,以获取数据
b) Sink:下沉地,采集数据的传送目的,用于往下一级agent传递数据或者往最终存储系统传递数据
c) Channel:angent内部的数据传输通道,用于从source将数据传递到sink
注:
Source 到 Channel到Sink之间传递数据的形式是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支持众多的source和sink类型,详细手册可参考官方文档
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上只不过时间太短了,有做好了下一个写入的准备,下一个的写入准备正好是上一个的准备的事件)。
2、2050 会截取上一个整数的事件点,来获取数据
3、往HDFS写入数据需要在运行在HDFS环境中运行,也就是卓只要运行在HDFS红就可以啦。多方便啊!!!!!!
3 多机测试实例:
hadoop2 做sources的采集 实现hadoop3与hadoop4做sinks(实现原理与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 为了很好地组织起这样的复杂执行计划,需要一个工作流调度系统来调度执行;
实现的方式 :
简单的任务调度:直接使用linux的crontab来定义;
复杂的任务调度:开发调度平台或使用现成的开源调度系统,比如ooize、azkaban等
下载地址: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-2)web服务器配置
[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-3)azkaban 执行服务器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-1) web服务器
配置环境变量
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内置的任务类型支持command、java
案例
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'
通过azkaban的web管理平台创建project并上传job压缩包
首先创建project,见下图:
1-2) Command类型多job工作流flow
创建有依赖关系的多个job描述
第一个job:foo.job
# foo.job
type=command
command=echo foo
第二个job:bar.job依赖foo.job
# bar.job
type=command
dependencies=foo
command=echo bar
1、将所有job资源文件打到一个zip包中
2、在azkaban的web管理界面创建工程并上传zip包
3、启动工作流flow
1-3) HDFS操作任务
1、创建job描述文件
# fs.job
type=command
command=/home/hadoop/apps/hadoop-2.6.1/bin/hadoop fs -mkdir /azaz
2、将job资源文件打包成zip文件
3、通过azkaban的web管理平台创建project并上传job压缩包
4、启动执行该job
1-4)MAPREDUCE任务
Mr任务依然可以使用command的job类型来执行
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、在azkaban的web管理界面创建工程并上传zip包
4、启动job
1-5)HIVE脚本任务
创建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、在azkaban的web管理界面创建工程并上传zip包
4、启动job
Sqoop数据迁移
3.1 概述
sqoop是apache旗下一款“Hadoop和关系数据库服务器之间传送数据”的工具。
导入数据:MySQL,Oracle导入数据到Hadoop的HDFS、HIVE、HBASE等数据存储系统;
导出数据:从Hadoop的文件系统中导出数据到关系数据库mysql等
3.2 工作机制
将导入或导出命令翻译成mapreduce程序来实现
在翻译出的mapreduce中主要是对inputformat和outputformat进行定制
3.3 sqoop实战及原理
3.3.1 sqoop安装
安装sqoop的前提是已经具备java和hadoop的环境
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、加入mysql的jdbc驱动包
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的数据导入
“导入工具”导入单个表从RDBMS到HDFS。表中的每一行被视为HDFS的记录。所有记录都存储为文本文件的文本数据(或者Avro、sequence文件等二进制数据)
3.4.1 语法
下面的语法用于将数据导入HDFS。
$ sqoop import (generic-args) (import-args)
3.4.2 示例
表数据
在mysql中有一个库userdb中三个表:emp, emp_add和emp_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
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数据库
导出前,目标表必须存在于目标数据库中。
u 默认操作是从将文件中的数据使用INSERT语句插入到表中
u 更新模式下,是生成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
相当于mapreduce的map与reduce
Storm 的分组:
1、Shuffer 分组:Task自由的分配,可以保证同一级Bolt上的每一个Task处理的Tuple数据一致。
2、Filed分组:根据Tuple中的某一个Filed或者多个Filed的值进行划分。
3、ALL分组:所有的Tuple都会分发到所有的Task。如图:
4、Global 分组:整个starem会选择一个Task作为分发地的目的,通常最新的ID的Task。
实时平台架构介绍
当网站或者APP的用户达到一定的用户时,一般需要一套Tracker系统进行手机用户的行为,从而构造出用户画像,以及界面的相应,容错等信息,并把这些信息上报给日志服务器,给开发团队分析这些信息。
日志服务器也可以通过Flme系统Sink到Kafka等队列中,供Stome实时处理消息:
流式计算整体结构
flume用来获取数据
Kafka用来临时保存数据
Strom用来计算数据
Redis是个内存数据库,用来保存数据
Storm通信机制 Disruptor
Storm 使用ZeroMQ与Netty (0.9版本之后默认的使用)作为进程间通信的消息框架,woker之间的内部通信:不同woker的thread通信使用LMAXDisruptor 来完成的。不同topology之间的通信strom不负责,需要自己想办法实现,比如使用工具KAFKA。worker执行executor,executor有自己的incoming-queue和outgoing-queue的配置。Disruptor能每秒处理6百万个订单,是一个有界队列。而队列的应用场景自然就是“生产者-消费者”模型。可以实现的原理是环形缓冲区。
Disruptor 的特点:
1、没有竞争=没有锁=非常快
2、是一个有限队列
3、所有访问者都记录自己的序号的实现方式,允许多个生产者与多个消费者共享相同的数据结构。
4、在每个对象中都能跟踪序列号(ring buffer,claim Strategy,生产者和消费者),加上神奇的cache line padding,就意味着没有为伪共享和非预期的竞争。
ACK 的总结
1-1)ACK是什么?
ack 机制是storm整个技术体系中非常闪亮的一个创新点。
通过Ack机制,spout发送出去的每一条消息,都可以确定是被成功处理或失败处理, 从而可以让开发者采取动作。比如在Meta中,成功被处理,即可更新偏移量,当失败时,重复发送数据。
另外Ack机制还常用于限流作用: 为了避免spout发送数据太快,而bolt处理太慢,常常设置pending数,当spout有等于或超过pending数的tuple没有收到ack或fail响应时,跳过执行nextTuple, 从而限制spout发送数据。
在strom中ack主要做一些异或的运算,来判断在spout到bout中数据是否处理成功!
Storm 常见问题总结
1-1)为什么有Storm
stome解决了不能处理实时数据流的缺点,使数据的信息能及时的展现出来。
1-2)Storm有什么特点
1、容错性
2、数据不丢失
3、分布式
4、低延迟
5、可扩展
6、高可用
1-3)离线计算与实时计算的区别
离线时处理多条(成吨)的数据,而实时处理一行(细细)的数据。
1-4)Storm架构中的核心组件
Sport (数据源)与Bolt(数据操作)
1-5)Storm编程模型是什么
spout、Bolt、Stream Groupings
1-6)为什么有StreamGrouping,常用分组策略
Streamgrouping控制着在event在Topology中如何流动,或者说成定义一个流在Blot任务间该如何被切分。
有Shuffergrouping、Fieldsgrouping、allgrouping、Global grouping、None grouping、Direct grouping、CustomStreamGroupimg等分组的策略。
1-7)Wordcount中都用到什么技术点
(我靠这个好难总结啊!!)不搞了、、、
1-8)Tuple是什么
说白了就是收集数据的,不过还的专业点吧!是一个topology中产生数据源的组件,通常情况下会从外部获取数据,然后转换为topology的数据的组件。
1-9)Storm的并行度是什么
说白了就是在物理机上同时能运行多少个Task的实体的单元:
关系:
1、storm集群里的物理机会启动一个或者多个Woeker,所有的topology都在woker中运行,那么一个woker又会运行一个或者多个executor线程,
每个executor只会运行一个topology的一个componentde的task的实例
2、一个task是最终完成数据处理的实体单元
1-10)梳理实时业务指标项目
(我靠这个不写了,的写好多啊!)
背景:
业务:
架构:
技术点:
1-11)redis数据结构的运用:
1. String——字符串
2、Hash——字典
3. List——列表
4. Set——集合
5. Sorted Set——有序集合
1-12)redis的Key如何设计?
按照实际需求来设计呗!!(不想回答了,困了、、、、)
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使用的。默认情况下,每个节点上可运行4个workers,分别在6700、6701、6702和6703端口
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不在进程内保存状态的原因,即使Nimbus或Supervisors被重启,运行中的Topologies不会受到影响。
以下是启动Storm各个后台进程的方式:
1、在hadoop2上启动storm nimbus &
2、在hadoop2上启动storm ui &
3、在其他的机器上启动storm supervisor &
说明:nimbus 为管理节点的老大,UI是提供图形化界面的程序,supervisor是当前物理机上的管理者,默认的分配4个worker.
注意事项:
启动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
我们能够挂起或停用运行中的拓扑。当停用拓扑时,所有已分发的元组都会得到处理,但是spouts的nextTuple方法不会被调用。销毁一个拓扑,可以使用kill命令。它会以一种安全的方式销毁一个拓扑,首先停用拓扑,在等待拓扑消息的时间段内允许拓扑完成当前的数据流。
1-4) 启用任务命令格式
storm activate【拓扑名称】
storm activate topology-name
1-5)重新部署任务命令格式
storm rebalance 【拓扑名称】
storm rebalance topology-name
再平衡使你重分配集群任务。这是个很强大的命令。比如,你向一个运行中的集群增加了节点。再平衡命令将会停用拓扑,然后在相应超时时间之后重分配worker,并重启拓扑。
1-6)Spout与Bolt的生命周期
Spout 的生命周期:
Bolt的生命周期:
1-7)StreamGrouping源码解析
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中的元素存放在成员变量counters(Map)中.
* 如果counters(Map)中已经存在该元素,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提供的一套技术规范,主要做数据的集成,通信,提高系统的伸缩性,提高用户的体验,是系统之间的模块更加灵活以及方便。
通过什么方式:生产消费者模式(生产者、服务器、消费者)
jdk,kafka,activemq……
JMS消息传输模型
点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
发布/订阅模式(一对多,数据生产后,推送给所有订阅者)
JMS核心组件
Destination:消息发送的目的地,也就是前面说的Queue和Topic。
Message :从字面上就可以看出是被发送的消息。
Producer: 消息的生产者,要发送一个消息,必须通过这个生产者来发送。
MessageConsumer: 与生产者相对应,这是消息的消费者或接收者,通过它来接收一个消息。
注:treamMessage:Java数据流消息,用标准流操作来顺序的填充和读取。
MapMessage:一个Map类型的消息;名称为string类型,而值为Java的基本类型。
TextMessage:普通字符串消息,包含一个String。
ObjectMessage:对象消息,包含一个可序列化的Java对象
BytesMessage:二进制数组消息,包含一个byte[]。
XMLMessage: 一个XML类型的消息。
最常用的是TextMessage和ObjectMessage。
常见的类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可以有多个CG。topic的消息会复制(不是真的复制,是概念上的)到所有的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中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序。
l Offset:kafka的存储文件都是按照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
# 用来监听链接的端口,producer或consumer将在此端口建立连接
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.
# 指定多久消费者更新offset到zookeeper中。注意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.id的value值相同
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。有smallest、largest、anything可选,分别表示给当前最小的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)
5 调用方法和函数
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的类与Java、C++的类比起来更简洁,学完之后你会更爱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- 3) apply方法
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 - 1) scala超类的实现
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 -4) option匹配
package day3scala
/**
* 在scala中option中表示类型可能存在或者可能不存在的值,如果存在则用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
// woker的ID
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 总结
概述:
RDD(Resilient Distributed Dataset)即使分布式数据集,是spark最基本的数据抽象,它代表了一个不可变、可分区、里面的元素可以并行计算的集合。
特点:
1、自动容错、位置感知性调度和伸缩性
2、RDD 允许用户把数据放在内存中,下次读取时可以在内存中直接读取
RDD的属性
1-1)一组分片
是数据的最基本的数据单位,对于RDD来说,每一个分片都会计算一个任务处理,并决定计算的粒度,没有没有执行,则会使用默认的值,默认的值就是程序分配到的CPU CORE的个数
1-2)一个计算每个分区的函数。
Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。
1-3)RDD之间的依赖关系。
RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。
1-4)一个Partitioner,即RDD的分片函数。
当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。
1-5)储存位置
一个列表,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。
算子
Spark 提供了很多API,RDD提供了两个API,是TransFormation与Actions
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-3)RDD实例
练习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
窄依赖指的是每一个父RDD的Partition最多被子RDD的一个Partition使用
1-2)宽依赖--进行Shuffer
宽依赖指的是多个子RDD的Partition会依赖同一个父RDD的Partition
注意:窄依赖与宽依赖的依据是是不是执行了Shuffer, JION在大多的情况下是宽依赖,如果之前分好组了那么就是窄依赖。
1-3)Lineage
当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。
1-4)RDD的缓存
Spark 之所以快是因为spark使用了内存的技术,当持久化后会把数据集保存到内存中,在以后运算中速度更快。
1-5)RDD缓存方式
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很好地继承了传统单机数据分析的开发体验。
实例:
准备数据:
在本地创建一个文件,有三列,分别是id、name、age,用空格分隔,然后上传到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支持的数据输入源很多,例如:Kafka、Flume、Twitter、ZeroMQ和简单的TCP套接字等等。数据输入后可以用Spark的高度抽象原语如:map、reduce、join、window等进行运算。而结果也能保存在很多地方,如HDFS,redis, hbise数据库等。另外Spark Streaming也能和MLlib(机器学习)以及Graphx完美融合。
什么是DStream
Discretized Stream是Spark 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原语被调用时(与RDD的Action相同),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-2)flume-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-8)URL查询
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-1)kibana的界面
1-2)Elasticsearch的数据库的信息
1-3)数据查看
- 快学Big Data
- BIG DATA
- Big Data
- Big Data
- Big Data
- Big Data
- Big data
- BIG DATA
- Big Data
- Big-Data
- Big Data
- sql big data :ZT
- Big Data技术综述
- Big Data 技术综述
- Big Data Application Platform
- Big Data 2011
- Big Data 文章
- Big Data技术综述
- Android官方MVP架构示例项目解析
- c语言的指针、数组和常量修饰符const
- 【原创】东方耀reactnative 视频12之-view组件
- Java-面向对象(基础篇)--static关键字
- Python tornado
- 快学Big Data
- Reducing the Dimensionality of Data with Neural Networks:神经网络用于降维
- Java注解总结
- LibRIL 运行机制
- enum 枚举的作用
- android 动态加载框架 快速使用
- Hadoop的容错性
- python 操作MySQL数据库
- C++学习笔记4--表达式