00:00
接下来我们给大家呢,把那个for each啊,咱们演示个小例子啊,咱们看看,呃,来这里呢,我们拷贝,拷贝以后写上一个我们的零七啊,然后拷贝一下点击OK,好把咱们这个地方呢改一改啊,把里面的代码呢,咱们都去掉,那咱们演示一个什么小例子呢?我们想演示这么个小例子啊,就是我们写个class,写个U,然后呢,给它声明一个属性,咱们叫int等于30啊,咱们这么写,然后呢,我的RDD,点我们的for it啊for it,然后给个number,因为for it是挨个遍历嘛,然后呢,我就想打印一下,打印什么呢?我在这边呀,有一个啊,咱们统一的UR,咱们叫做new UR啊来。然后呢,把这个U呢,我们放到这儿来啊,咱们写上叫年龄,嗯,咱们放过来叫做年龄,它等于它等于什么呢?它等于U的点H,再加上咱们的number。
01:03
什么意思啊,我们的年龄是30岁吧,对不对,那么好循环加一加二加三加四,那么31323334对不对,只不过for是一个分布式循环的一个打印操作,那这个时候谁先打印谁后打印不确定吧,同学们对不对?诶就是这样的啊,这是我们预期的结果,那好我们现在运行看一看我们的执行结果跟我们刚才所说的是不是一样呢?诶,大家看到没有,出现了错误了,诶怎么出现错误了呢?这个逻辑并不复杂,而且咱们之前还用过呀,对不对,所以啊,咱们一块来看一看,到底什么地方出现了问题,大家看一下,他说了,在主线程当中出现了一个叫Spark exception,叫做什么呢?叫task,我们放到这啊来。写上叫task not suable,叫做任务没有序列化,你任务有没有序列化跟我有啥关系呀?这不很奇怪一件事情吗?为什么这么说呢?因为我这个又不是来分解任务,咱们这没有任务啊,任务跟我没有关系,那你能不能序列化好像不是我决定的呀,所以这个就很奇怪了。
02:13
它怎么出现了一个叫task notable的一个错误呢?不知道,所以这个错误信息呢,可能还真不是我们想要的,所以啊,咱们往下看,有没有一些具体的错误信息,我们要往下看啊,当出现问题大家不要着急,我们找到错误的原因,咱们来分析它,咱们往下找,往下找诶找到这个地方的时候,出现了这么一个错误,叫做not sable exception,它的原因呢,就是我们的这样的一个异常,这个异常它就明确的告诉你了什么就是我们的这个UR。他没有序列化,所以啊,他这边所报的错误呢,所以我们拷贝啊拿过来,嗯。上面那个异常呢,是我们最终的一个现象,那么我们的原因呢,其实就是在我们的这个地方,对不对,所以啊,同学们记住了啊,对于我们来讲,咱们真正的错误的原因是我们的这个U的它没有序列化,诶怎么还跟U的有关系呢,这是啥意思?哎,确实呀,有的确实没序列化,但是凭什么他需要序列化呀。
03:19
这不觉得很奇怪呀,他为什么要学六方不知道,所以啊,在我们分析这个问题的时候,我们要回过头来再去想一想,这个什么地方咱们用到了U呢?你要想序列化,我得用上它,我都不用它,哪来的序列化对不对?所以咱们在哪用上了,你会发现在这个地方咱们构建了对象,在这个地方我们用到了这个U。那么它为什么就要序列化呢?所以我们回过头来,咱们接着往这来看,所以啊,我写上一个driver,然后呢,有一个exec,好,然后有一个exor,把箭头我们拿过来。
04:01
把箭头我们拿过来啊,好了,拿过来以后同学们分析一下吧,我们之前是不是给大家讲过,说了这个for它是RDD的方法,咱们称之为叫算子,是行动算子,那么这个算子的外部的代码它是在哪?去执行的。它是在咱们的driver端对不对,所以说在driver端它构建了一个对象没问题吧,同学们,所以它里面有一个U,等于什么new,咱们的U对不对?诶它有一个U,诶就是new出来的,或者咱们这么写可能不太好,咱们直接啊来把这个圆圈拿过来,就是在我们driver端呀,它构建了一个什么呢?U,嗯。好啊,咱们给它放过来啊,咱们放过来好了,放过来以后记住啊,同学们这是我们的UR啊,嗯,好了,然后呢,我们再来往下看,然后你循环便历的时候,问问同学们这是啥,这个是不是在我们算子的内部,算子的内部是不是在哪执行,它应该是在咱们的ECU端吧,所以说记住它会在ECU端去做这个操作,这什么操作叫打印吧,所以来咱们继续啊,来在这里面把这个,诶咱们在这个地方加个按钮之类的吧,在这儿啊。
05:20
嗯,拖过来啊,好,咱们叫做print,嗯,咱们叫做print,那你的这个有print,难道我这就没有吗?所以我们这是不是可能也有啊,所以拿过来,那么你print的时候,大家看一下你怎么了,你是不是用到了我们这边的U,就意味着你用到了这个吧。可是你要在这儿来用,可是你的UR在哪?在这块内存中,那你是不是应该想办法把这块内存中的U给我传过来呀?所以大家会发现,你是不是要把这个UR传到这儿来,你要把它传到这儿来对吗?你要把它传到这里来的话,那是不是意味着你的优点需要在网络中传递呢?那如果你需要在网络中传递的话,大家想一想。
06:08
那是不是应该序列化呀?所以他报这个错误其实是对的啊,就是因为我们算子的外部是在driver,它的内部是在excuor,那么driver和E要共享数据,它是需要传递数据的,那么就需要给他序列化,所以你在这儿给他来混入咱们的S,诶,你放过来,你放过来之后你再执行这个应该就没事了。所以啊,咱们这个地方的算子这个概念,我们还得再去着重的去理解一下,咱们强了,咱们前面讲了那么多的算子了,但是我们其实只有在最后的时候才强调这个driver和E的问题,大家看一下,你看出来结果了吧,没有报错,为什么?因为我们序列化了啊,就是这个意思,诶,我们其实啊,这个是一种方式,好,我们再来或者这么写吧,把这个去掉,去掉以后其实大家看啊,我们还可以再来一个叫case class,我们叫什么呢?叫做UR,我们这么写其实它也行,哎,那这么写为什么也可以呢?咱们再来试一试啊,我们运行。
07:16
运行以后看一看,我这个好像并没有去继承我们的sable,或叫做混入这个特质吧,没有吧,但是为什么我们说也行呢?大家看一看我们的执行结果,看它会不会发生错误。发生错误了吗?没有,那这是为什么?因为啊,咱们说一下咱们的样例类啊,它会什么呢?在编译时,在我们编译时啊,它会自动啊,它会自动混入,咱们叫什么呢?叫序列化我们的特质,所以啊,那你这就简单了,或者说咱们或者叫换个说法叫实现啊,咱们叫实现可序列化接口对吧?实现可序列化接口这是自动的,在编译头自动帮你完成的,你什么都不需要做,所以样例类其实用的会非常的多啊哎,老师啊,那如果咱们这样做呢,你想一想。
08:12
比方说如果我现在这个地方没有数据,那它还会不会出现错误呢?比方说同们看把这个注掉住掉以后来啊,我们写上一个class,咱们叫做什么呢?叫做U,嗯。好了,那你现在的这个地方,比方说我们现在没有序列化,但是呢,你的这是集合是空啊,你的集合如果是空的话,那就意味着它循环是过不来的,那过不来的话,那你这就执行不到啊,对不对,那你的这个地方会不会发生问题呢?不要怀疑了啊,为什么呢?我一个都走不到,我如果一个都走不到的情况下,那你根本就走不进去,那你这个对象也不需要传了吧,所以我们现在运行啊,运行以后看结果,看看跟我预想的是不是一样的啊,呃,这个地方是什么?嗯。
09:06
我确认一下来。咱们的这个class啊,我们来来来number啊,这个age哦,咱们这里呢,没有加什么呢,类型,它这个类型写错了啊,咱们给它一个int,嗯,好重新来。当你为空的时候,它的类型判断呢,就发生了问题,那这个加号就不是数字相加了啊,所以就出现了问题,嗯,好,那我们现在呢,来看一看它的这个结果啊嗯,诶同学们有没有发现它这边又出现问题了,而且发生的异常还是还是咱们之前的这个。为什么呀?这不觉得很奇怪呀?为什么它是我们的这种情况对不对,那我们这里呢,就要给大家稍微的解释一下了,因为啊,咱们这个里面的for意思是函数式编程,函数式编程它的匿名函数其实是需要用到一个特殊的操作的,这个操作我们称之为叫B包。
10:11
在GALA当中有一个B包的操作,大家有印象吗?所以啊,咱们在这里需要再补充一下啊,这个比较特殊了,我们的这个嗯,RDD啊RDD算子中啊,算子中啊,如果它传递啊,如果其实也不是传递了,嗯,咱们叫传递的这个函数啊,它是什么呢?是会,嗯,咱们叫包含B包操作的闭包操作,那么。它就会什么呢?就会啊进行我们的检测功能,它为什么要进行检测功能呢。就是因为啊,我们在B包的里面,我要引入外部的变量,那么这个外部的变量它就应该传递到E里面,所以说我根本就不需要真正的执行,我只需要判断你传进来的这个B包的变量它是不是序列化就行了,我不需要干嘛呀去执行,我就能判断出来,为什么呢?因为它有闭包,而闭包就是我们的函数,它把外部的变量引入到它的内部,形成一个闭合的效果,它改变这个变量的生命周期,对不对?诶,你的U的不就被包进去了吗?可是你包进去以后发现它不能序列化,那肯定是有问题的,我都不用执行。
11:27
对不对,所以这样的话就会有一个检测功能,我们把它称之为叫什么呢?叫B包检测,叫B包检测功能。哎,所以就是这个意思啊,那这个是怎么能够判断出来的呢?其实很简单,因为源码中有这样的处理,我们点开一下,咱们叫for it,这个for it当中大家会发现它里面有一个叫clean clean啊,这个clean clean,咱们再点一下,它就叫closer cleaner,就是B包的清除器,把那些B包当中我们不需要的东西全给它清除掉,那么这边有个叫做什么呢?叫check sable,叫检查序列化,它给个true,就是当你调这个方法的时候,它就会去什么呢?进行序列化啊,有一个判断在里面,然后我们点击clean点,点完以后再点clean,然后诶往下走,往下走以后大家会发现在这个位置呀,它就会有一个检测B包的概念,叫做is closer。
12:23
但是呀,有一个问题,因为我们史该LA语言呢,2.11版本和2.12版本呢,他们B包的这种规则发生了变化,所以啊,其实这个方法它是2.11的,而这个呢是2.12以上的啊,所以他们其实规则呢有点不太一样啊,这个咱们暂时就先不考虑了,咱们就先看一看这个什么概念就可以了啊,所以这个B包它是在这判断的啊,因为它的版本比较多嘛,所以这个也没有办法给大家去把每个都看一遍啊,咱们接着往下看,看关键性的点,往下走,往下走看这段代码能不能看懂,我相信不难啊,嗯。
13:01
往下走,往下走,看这什么意思?它是不是在检测序列化,如果当你的这个值为数的情况下,它就确保能够序列化,把你当前那个匿名函数里面的东西,我要看看能不能序列化,就在这儿,所以他会尝试着去序列化,可是发现呢,你不能序列化,直接就会发生错误,所以啊,啊,同学们看啊,我们现在再重新来执行一下啊,来重新执行,重新执行的时候,你会发现那个报错的地方不是运行作业的地方,而是B包检测的位置。啊,所以我们来给大家看到这一点。好,咱们回过头,回过头以后,大家会发现是不是在这个叫closer cleaner里面有一个确保序列化的功能,是在这个地方发生错误,还没有到咱们的是吧,我们的run job那个位置还没有例行作业呢,他就已经检测出问题来了,直接发生错误,好吧,同学们,所以啊,我们又把咱们这个driver啊,Ex个qor这个概念我们又理解了一下啊,大家再体会体会这种分布式执行所需要注意的地方。
我来说两句