spark on yarn模式分为两种模式:
一、Yarn-cluster模式
1、通过spark-submit提交spark jar包(Application),与RM进行通信请求启动AM
2、RM接收到请求之后,会在一个相对空闲的nodemanager中分配Container,然后再此Container中启动AM
3、AM启动之后,会向RM进行通信,请求资源用于启动nodemagaer(相当于Worker)节点中的Executor
4、RM分配一批Container容器到NM中,用于启动Executor
5、AM与其他NM(worker)进行通信,启动Executor
6、executor启动之后,就会向AM(Driver)进行反注册
7、接下来的执行流程,就与Standalone模式的执行流程是一样的
二、Yarn-Client模式
1、通过spark-submit提交spark jar包(Application),与RM进行通信请求启动AM,并且在客户端本地启动Driver进程
2、RM接收到请求之后,会在一个相对空闲的nodemanager中分配Container,然后再此Container中启动AM
3、AM启动之后,会向RM进行通信,请求资源用于启动nodemagaer(相当于Worker)节点中的Executor
4、RM分配一批Container容器到NM中,用于启动Executor
5、AM与其他NM(worker)进行通信,启动Executor
6、Executor启动之后,就会向客户端本地的Driver进行反注册。
7、接下来的执行流程,就与Standalone模式的执行流程是一样的。
三、总结:
经过上面的描述,yarn-cluster与yarn-client模式有一个很大的区别,就是Driver进程所运行的位置。
在yarn-cluster模式中,Driver运行在AM所在节点上
在yarn-client模式中,Driver运行在客户端本地上
在yarn-client模式中,由于Driver运行在客户端本地,如果运行中出现Bug,那么可以在本地直接通过查Log日志来判定Bug的原因,从而对程序进行调整。
在yarn-client模式中,由于Driver运行在AM所在节点上,如果运行中出现BUG,只能通过集群中产生的Log日志进行判定,需要用到application_id来进行查找log,过程比较麻烦。
综上所述,yarn-client模式便于排错,一般用于测试环境,而yarn-cluster模式一般用于真实的生产环境中。
案例分析:Spark项目案例-电影受众分析系统
一、 数据结构
users.dat:
UserID::Gender::Age::Occupation( 职业) ::Zip-code( 邮政编码)
movies.dat
MovieID::Title::Genres( 题材)
ratings.dat
UserID::MovieID::Rating::Timestamp
二、 功能需求
1、 看过“ Lord of the Rings,The(1978) ” 用户年龄和性别分布
2、 年龄段在“ 18-24” 的男性年轻人, 最喜欢看哪10部电影
3、 得分最高的10部电影
4、 看过电影最多的前10个人
5、 女性看过最多的10部电影
6、 男性看过最多的10部电影
7、 年龄段在“ 45-49” 的男性观众中, 得分最高的10部电影名称
8、 1995年最受欢迎的前3部电影的用户年龄和性别数量分布
9、moiveid=2116这部电影各年龄段的平均影评(年龄段,平均影评分)
10、统计最喜欢看电影的那位女士最喜欢的10部电影,观众对他们的平均影评分(电影名,影评)
1、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
object MovieDemo { var sc:SparkContext= null def main(args: Array[String]): Unit = { val conf= new SparkConf().setAppName( "MovieDemo" ) .setMaster( "local" ) sc= new SparkContext(conf) ageAndSexCount } /** * @param sc SparkContext对象 * @return 返回用户信息 */ def getUsers(sc:SparkContext):RDD[Array[String]]={ val scobj=sc val users=scobj.textFile( "file:///home/tg/datas/users.dat" ) .map(_.split( "::" )) users } /** * @param sc * @return 返回电影信息 */ def getMovies(sc:SparkContext):RDD[Array[String]]={ val scobj=sc val movies=scobj.textFile( "file:///home/tg/datas/movies.dat" ) .map(_.split( "::" )) movies } /** * * @param sc * @return 电影评分信息 */ def getRatings(sc:SparkContext):RDD[Array[String]]={ val scobj=sc val ratings=scobj.textFile( "file:///home/tg/datas/ratings.dat" ) .map(_.split( "::" )) ratings } /** * 看过“Lord of the Rings,The (1978) ”用户年龄和性别(数量)分布 */ def ageAndSexCount: Unit ={ //获取“Lord of the Rings,The (1978) ”这部电影的movieId val movies=getMovies(sc) val movieId=movies.filter(x=>x.length== 3 && x( 1 ).equals( "Lord of the Rings, The (1978)" )) .map(x=>x( 0 ).trim).toArray()( 0 ) //从users.dat中获取所有user的(userid,(age,gender)) val users=getUsers(sc) val usersRDD=users.map(x=>{ (x( 0 ),(x( 2 ),x( 1 ))) //(userid,(age,gender)) }) //获取看过"Lord of the Rings,The (1978)"这部电影的(userid,movieid) val ratings=getRatings(sc) val ratingRDD=ratings.map(x=>{ (x( 0 ),x( 1 )) //ratings数据中所有的(userid,movieid) }).filter(x=>x._2.equals(movieId)) //将ratingRDD和usersRDD进行join操作,结果<userid,(movieid,(age,gender))> val userratingRDD=ratingRDD.join(usersRDD) println( "看过“Lord of the Rings,The (1978) ”用户年龄和性别(数量)分布" ) userratingRDD.map(x=>{ (x._2._2, 1 ) }).reduceByKey(_+_) .foreach(println(_)) } } |
2、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/** * 年龄段在“18-24”的男性年轻人,最喜欢看哪10部电影 */ def top10LookeMovie: Unit ={ //获取年龄段在“18-24”的男性年轻人的userid val users=getUsers(sc) val userList=users.filter(x=>x( 1 ).equals( "M" ) && x( 2 ).toInt== 18 ) .map(x=>x( 0 )).collect() //注意:HashSet()后面要带小括号 val userSet=HashSet() ++ userList //创建广播变量 val broadcastUserSet=sc.broadcast(userSet) //统计出18-24岁男性喜欢看的前10名电影的movieid和次数 val ratings=getRatings(sc) val topNMovies=ratings.map(x=>(x( 0 ),x( 1 ))) //ratings中所有的(userid,movieid) //从rating数据过滤出“18-24”的男性年轻人的观影信息 .filter(x=>broadcastUserSet.value.contains(x._1)) .map(x=>(x._2, 1 )) .reduceByKey(_+_) //(movieid,次数) .sortBy(_._2, false ) .take( 10 ) //(movieid,次数) val movies=getMovies(sc) //获取所有电影的(movieid,title) val movieTitle=movies.map(x=>(x( 0 ),x( 1 ))).collect().toMap topNMovies.map(x=>(movieTitle.getOrElse(x._1, null ),x._2)) .foreach(x=>println(x._1+ " " +x._2)) } |
3、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
def getTenMovie: Unit ={ val ratings=getRatings(sc) //获取得分最高的10部电影的movieId val top10moiveid=ratings.map(x=>(x( 0 ),x( 1 ),x( 2 ))) //(userid,movieid,rating) .map(x=>(x._2,(x._2.toInt, 1 ))) //(movieid,(rating,1)) .reduceByKey((x,y)=>{ (x._1+y._1,x._2+y._2) //注意此行代码,求出了电评的总分、评价的次数 }).map(x=>{ (x._2._1.toDouble/x._2._2.toDouble,x._1) //(电影平均分,movieid) }).sortByKey( false ).take( 10 ).map(x=>x._2) //广播变量 val broadcastMovieList=sc.broadcast(top10moiveid) val movies=getMovies(sc) val result=movies.map(x=>(x( 0 ),x( 1 ))) //取出全部电影的(movieid,title) .filter(x=>broadcastMovieList.value.contains(x._1)) println( "得分最高的10部电影是:" ) result.foreach(x=>println(x._1+ " " +x._2)) } |
4、
5、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/** * 女性看过最多的10部电影 */ def top10FaleLookMovie: Unit ={ val users = getUsers(sc) //获取所有女性的userid val faleUserId = users.filter(x => x( 1 ).equals( "F" )) .map(x => x( 0 )).collect() val faleUserSet = HashSet() ++ faleUserId //创建广播变量,里面存储所有女性的userid val broadcastFaleSet = sc.broadcast(faleUserSet) val ratings = getRatings(sc) //统计出女性看过最多的10部电影的(movieid,观看次数) val top10moiveid = ratings.map(x => (x( 0 ), x( 1 ))) //(userid,movieid) //过滤出女性观影数据 .filter(x => broadcastFaleSet.value.contains(x._1)) .map(x => (x._2, 1 )) //(movieid,1) .reduceByKey(_ + _) .sortBy(_._2, false ) .take( 10 ) val top10movieRDD=sc.parallelize(top10moiveid) //(movieid,次数) val movies=getMovies(sc) val allmoviesRDD=movies.map(x=>(x( 0 ),x( 1 ))) //(movieid,title) //对两个RDD进行join操作,取二者的共同匹配项 allmoviesRDD.join(top10movieRDD) //(movieid,(title,次数)) .map(x=>(x._1,x._2._1,x._2._2)) .foreach(x=>println(x._1+ " " +x._2+ " " +x._3)) } |
6、解决方法:将女性改为男性就好了
7、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
/** * 年龄段在“45-49”的男性观众中,得分最高的10部电影名称 */ def top10MaleMovie: Unit ={ //获取所有年龄段在“45-49”的男性观众的userid val users=getUsers(sc) val usersList=users.filter(x=>x( 1 ).equals( "M" ) && x( 2 ).toInt== 45 ) .map(x=>x( 0 )).collect() val userSet=HashSet() ++ usersList //创建广播变量 val broadcastUserSet=sc.broadcast(userSet) val ratings=getRatings(sc) //得分最高的10部电影的(movieid,avg) val topmovies=ratings.map(x=>(x( 0 ),x( 1 ),x( 2 ))) //(userid,movieid,rating) .filter(x=>broadcastUserSet.value.contains(x._1)) .map(x=>(x._2,(x._3.toInt, 1 ))) //(movieid,(rating,1)) .reduceByKey((x,y)=>{ (x._1+y._1,x._2+y._2) }).map(x=>{ val sum=x._2._1.toDouble val rcount=x._2._2.toDouble val avg=sum/rcount //求出平均分 (avg,x._1) }).sortByKey( false ).take( 10 ) .map(x=>(x._2,x._1)) //(movieid,avg) val topmoviesRDD=sc.parallelize(topmovies) val movies=getMovies(sc) val allmovies=movies.map(x=>(x( 0 ),x( 1 ))).collect().toMap topmoviesRDD.map(x=>(allmovies.getOrElse(x._1, null ),x._2)) .foreach(println(_)) } |
8、第一步 统计出1995年播放电影的moviesid(substring) title.substring(title.length-5,title.length-1).equals("1995")
第二步从ratings中统计出1995年最受欢迎的前三部电影的movieid
第三步 计算出观看过1995年最受欢迎的前三部电影的(userid,movieid)
第四步 统计出1995年最受欢迎的前3部电影的观众年龄和性别数量分布
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/** * 1995年最受欢迎的前3部电影的观众年龄和性别数量分布 */ def top3AgeAndSexCount: Unit ={ //第一步:统计出1995年播放电影的moviesid val movies=getMovies(sc) val moviesid=movies.filter(x=>{ var title=x( 1 ).trim title.substring(title.length- 5 ,title.length- 1 ).equals( "1995" ) }).map(x=>x( 0 ))collect() val moviesidSet=HashSet()++moviesid //创建广播变量 val broadcastmoviesidSet=sc.broadcast(moviesidSet) //第二步:从ratings中统计出1995年最受欢迎的前3部电影的movieid val ratings=getRatings(sc) val top3moviesid=ratings.map(x=>(x( 0 ),x( 1 ))) //(userid,movieid) .distinct() //数据去重 //过滤出1995年观影的(userid,movieid) .filter(x=>broadcastmoviesidSet.value.contains(x._2)) .map(x=>(x._2, 1 )) //(movieid,1) .reduceByKey(_+_) //(movieid,次数) .map(x=>(x._2,x._1)) .sortByKey( false ).take( 3 ) .map(x=>x._2) //movieid val top3moviesidSet=HashSet()++top3moviesid val broadcasttop3moviesidSet=sc.broadcast(top3moviesidSet) //第三步:统计出观看过1995年最受欢迎的前3部电影的(userid,movieid) val top3useridmovieid=ratings.map(x=>(x( 0 ),x( 1 ))) //所有的(userid,movieid) .filter(x=>broadcasttop3moviesidSet.value.contains(x._2)) //第四步:统计出1995年最受欢迎的前3部电影的观众年龄和性别数量分布 val users=getUsers(sc) val allusers=users.map(x=>(x( 0 ),(x( 2 ),x( 1 )))) //(userid,(age,gender)) top3useridmovieid.join(allusers) //(userid,(movieid,(age,gender))) .map(x=>(x._2._2, 1 )) //((age,gender),1) .reduceByKey(_+_) .map(x=>(x._1,x._2)) //((age,gender),数量) .foreach(println(_)) } |
9、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/** * movieid = 2116 这部电影各年龄段的平均影评(年龄段,平均影评分) */ def ageAndRating: Unit ={ //第一步:从users中获取所有的(userid,age) val usersRDD=getUsers(sc) .map(x=>(x( 0 ),x( 2 ))) //(userid,age) //第二步:从ratings中获取观众对movieid=2116这部电影的影评(userid,rating) val useridratingRDD=getRatings(sc) .map(x=>(x( 0 ),x( 1 ),x( 2 ))) //(userid,movieid,rating) .filter(x=>x._2.toInt== 2116 ) .map(x=>(x._1,x._3)) //(userid,rating) //第三步:usersRDD join useridratingRDD,这两个RDD进行join操作 usersRDD.join(useridratingRDD) //(userid,(age,rating)) .map(x=>(x._2._1,(x._2._2.toInt, 1 ))) //(age,(rating,1)) .reduceByKey((x,y)=>{ (x._1+y._1,x._2+y._2) //分别对rating和1进行累计求和 }).map(x=>{ val age=x._1 val avg=x._2._1.toDouble/x._2._2.toDouble (age,avg) }).foreach(x=>println(x._1+ " " +x._2)) } |
10、第一步:统计最喜欢看电影的那位女士的userid 第二步:那位女士最喜欢的10部电影的movieid 第三步:观众对这10部电影的平均影评
def twoNum(num:Double):String={
val df = new DecimalFormat("#.00");
val res=df.format(num)
res
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
/** * 统计最喜欢看电影的那位女士最喜欢的10部电影, * 观众对它们的平均影评分(电影名,影评) */ def titleAndRating: Unit ={ //第一步:统计最喜欢看电影的那位女士的userid //从users中获取所有女性的userid val users=getUsers(sc) val faleusersid=users.filter(x=>x( 1 ).equals( "F" )) .map(x=>x( 0 )).collect() val faleuseridSet=HashSet()++faleusersid val broadcastfaleidSet=sc.broadcast(faleuseridSet) val ratings=getRatings(sc) val fuserid=ratings.map(x=>(x( 0 ),x( 1 ))) //(userid,movieid) .distinct() //过滤出所有女性的观影数据 .filter(x=>broadcastfaleidSet.value.contains(x._1)) .map(x=>(x._1, 1 )) //(userid,1) .reduceByKey(_+_) .map(x=>(x._2,x._1)) .sortByKey( false ).take( 1 ) .map(x=>x._2) //此userid是最喜欢看电影的那位女士的userid val broadfuesrid=sc.broadcast(fuserid) //第二步:那位女士最喜欢的10部电影的moviesid val top10moviesid=ratings.filter(x=>x( 0 ).equals(broadfuesrid.value( 0 ))) .map(x=>(x( 1 ),(x( 2 ).toInt, 1 ))) //(movieid,(rating,1)) .reduceByKey((x,y)=>{ (x._1+y._1,x._2+y._2) }).map(x=>{ val movieid=x._1 val avg=x._2._1.toDouble/x._2._2.toDouble (avg,movieid) }).sortByKey( false ).take( 10 ) .map(x=>x._2) //movieids val top10moviesidSet=HashSet()++top10moviesid val broadtop10moviesSet=sc.broadcast(top10moviesidSet) //第三步:观众对这10部电影的平均影评,影评保留两位小数,四舍五入 val top10moviesavg=ratings.map(x=>(x( 1 ),x( 2 ))) //(movieid,rating) .filter(x=>broadtop10moviesSet.value.contains(x._1)) .map(x=>(x._1,(x._2.toInt, 1 ))) //(movieid,(rating,1)) .reduceByKey((x,y)=>{ (x._1+y._1,x._2+y._2) }).map(x=>{ val movieid=x._1 val avg=x._2._1.toDouble/x._2._2.toDouble val resavg=twoNum(avg) //对平均影评保留两位小数 (movieid,resavg) //(movieid,平均分) }) //(电影名,影评) val movies=getMovies(sc) val allmovies=movies.map(x=>(x( 0 ),x( 1 ))) //(movieid,title) allmovies.join(top10moviesavg) //(movieid,(title,平均分)) .map(x=>(x._2._1,x._2._2)) //(title,平均分) .foreach(println(_)) } |