感谢DT大数据梦工厂支持提供技术支持,DT大数据梦工厂专注于Spark发行版定制。
本期概览:
1 Receiver生命全周期
首先,我们找到数据来源的入口,入口如下
Receiver的设计是极其巧妙的。它的设计非常的出色,很多的地方都值得我们认真的学习。
在深入认识Receiver之前,我们有必要思考一下,假如没有spark,我们可以尝试思考一下,Receiver不断的接受输入进来的数据,如果是我们来做,我们该怎么做?该怎么启动Receiver呢?
我们尝试从以下几个方向来假设思考。
方式如下所示
Receiver是应用程序启动的一部分,我们启动Receiver的时候,Receiver与InputStream是一一对应的。假如我们启动多个Receiver,一个partition有多条一条数据是没有关系的。但是这里有一个问题,从资源调度的角度看,有可能从一台机器上启动多个Receiver,从而导致负载不均衡,同时也有可能导致Receiver启动失败。因为RDD不同的分片对应不同的分片。在不同的机器上有可能Executor失败,导致任务失败。
我们要要求,只要我们的集群在运行,我们的Receiver就要正常运行。如果Receiver不正常运行,就导致整个集群任务不能执行,这是不可以接受的。
因此,我们的这俩个假设都不可行,可行的办法是,Receiver可以失败,但是不能影响Job的正常运行。Receiver失败后一定会容错,最终一定会成功运行,那么我们来看spark官方是怎么做这么一个巧妙的Receiver的容错性能的。
其实我们可以认为InputStreams与Receivers是一一对应的。
不过,这样可能导致负载不均衡,因为Receiver在不同的机器上。另外Receiver启动可能失败。
至今,我们仍然没有看到启动Receiver的代码,那么启动它的代码在哪呢?
然后接下来就是启动Receiver的方法了
这个代码进一步证明了一个Receiver只有一个InputStream与之对应。
Driver层面决定在哪个Executor上执行Receiver
终止一个Receiver,意味着不用重新启动一个JOB
Receiver start不会重试
为了启动Receiver,启动了一个spark作业
下面一个问题很重要:
这里要启动一个作业,这个作业是每个Receiver都启动一个Job,还是多个Receiver启动一个Job.循环启动每个Receiver,每个Receiver启动一个Job
这样,我们就解决了启动一个Task来启动Receiver的缺点,每个Receiver对应一个Job,对应一个任务。最大程度的避免负载不均衡,不会使得Receiver失败使得整个Job不能运行。另外对解决任务倾斜也有一定好处。
重新启动Receiver的时候会将不可用的Executor剪掉
这里设计得非常的美妙,能保证Receiver无论如何都能成功的启动
任务一旦失败,框架会装作若无其事的ReStartReceiver,可以说设计得天衣无缝。
线程池的方式并发启动Receiver,因为可能不同的Receiver接收来的数据是没有耦合的
到现在,我们视乎还有一团乌云没有解开,那就是决定Receiver具体在哪些机器上,代码如下
最后研究的一行代码:保证Executor活着(默认50个线程,20个并发度),作为一个SparkStreaming应用程序,超过50个数据来源的可能性不大。