通过案例对SparkStreaming透彻理解三板斧之一

本节课通过二个部分阐述SparkStreaming的理解:

一、解密SparkStreaming另类在线实验

二、瞬间理解SparkStreaming本质

Spark源码定制班主要是自己做发行版、自己动手改进Spark源码,通常在电信、金融、教育、医疗、互联网等领域都有自己不同的业务,如果Sprak官方版本没有你需要的业务功能,你自己可以定制、扩展Spark的功能,满足公司的业务需要。

选择SparkStreaming框架源码研究、二次开发的原因

1、Spark起初只有Spark Core基础框架没有其他的子框架(Spark SQL、Spark Streaming、Spark ML、Spark Graphx、Spark R等),在其后加入了各种子框架来满足不同的需求。而分析这些子框架发现,选择Spark Streaming框架来研究,是最明智的选择,通过研究Spark Core上的Spark Streaming子框架,是迈向精通Spark力量源泉和解决所有问题之道。

2、Spark有很多子框架,我们选择Spark Streaming而为什么不用其他框架?

  Spark SQL涉及了很多SQL语法细节的解析和优化,当然分析其解析、优化从而集中精力去研究Spark而言是一件重要的事情,但不是最重要的事情,所以Spark SQL不太适合作为具体的子框架值得我们去研究。

  目前Spark R现在不成熟,支撑功能有限。

  图计算,从各版本演进而言Graphx几乎没有改进,这种趋势,Graphx是不是已经发展基本到尽头了;另外图计算而言有很多数学级别的算法,而要把Spark做到极致,数学对我们来说重要,但对于研究而言不是最重要的。

  Mechine Learning在封装了Vector向量、Metrics构建了众多的算法库,从而涉及了太多的数学知识,所有选择ML其实也不是太好的选择。

  最后筛选出SparkStreaming子框架才是最佳的研究切入黄金点。

  2015年在美国Spark是大数据领域最火的一年,当然也是SparkStreaming流式处理的一年;SparkStreaming是大数据最有吸引力的地方。

大数据Spark领域在业界薪资排名

  2016年上半年,StackOverFlow调查结果得出大数据领域Spark薪水最高,而从事Spark技术时使用SparkStreaming是最吸引富有吸引力;大家考虑采用Spark技术也主要是考虑了Spark Streaming子框架。

  Spark Streaming是流式计算框架,一切数据如果和流处理不相关的话都是无效的数据。流式处理才是我们真正对大数据的初步印象,数据流进来它立即会给我们一个反馈,而不是数据挖掘、图计算。Spark强悍地方是使用流处理可以完美的结合使用机器学习的成果、图计算的成果、Spark sql或者说spark R的成果。这得益于Spark的一体化、多元化的技术架构的设计,也就是说通过一个技术堆栈可以调用技术堆栈中所有的东西,根本不需要任何的设置,这是Spark无可匹敌之处也是SparkStreaming一统天下的根源。这个时代的流处理单打独斗是不行了,SparkStreaming和多个兄弟框架联合起来,无可匹敌。如果你精通SparkStreaming的话,恭喜你,因为SparkStreaming以及背后的几个兄弟框架正好展示了Spark大数据无穷的魅力。

     整个Spark的所有应用程序,哪些程序容易出问题?肯定是SparkStreaming程序是最容易出问题的,因为数据是不断流入的,ss要动态的控制数据的流入、作业的切分、数据的处理,所以最容易出问题,但最容易出问题的地方同时也是最受关注的地方也是展示大数据最神奇魅力的地方。这些特色结合的话,也是最需要技术人才的地方。关注SparkStreaming在Spark的发展,你会很清晰知道,ss跟其他自框架不同之处,ss很象SparkCore上的一个应用程序。

  正如世界万物发展一样,任何技术都有其关键点或转折点,SparkStreaming相当于独孤九剑,SparkCore 相当于易筋经。SparkStreaming运行在SparkCore上,所以很多性能调优都是建立在SparkCore上的;Spark是大数据的龙脉,SparkStreaming是龙脉的穴位。

接下来感受一下龙脉和穴位

  研究SparkStreaming时,有困惑你的东西,SparkStreaming数据不断流进来,根据batchInterval时间片不断生成Job,并将Job提交集群处理,如果能清晰的看到数据的流入和数据的处理,你心里会很很踏实。

  如何能清晰的看到数据的处理过程呢?只需要一个小技巧:就是把SparkStreaming中的batchInterval放的足够大,例如说从30秒调整为1分钟一次batch,或者5分钟一次batch,你会很清晰的看到整个流程序的运行过程。

以广告点击在线黑名单的过滤为例

  调整时间维度:

    

我们把时间从30秒调至300秒:

读取SparkStreaming Socket端口的数据:

  打包程序发布至集群部署环境:

  

 

  

  检查集群进程:

  

  通过webui检查HDFS启动情况:

  

  启动history-server监控进程及其对应的webui监控界面:

  

     

 至此整个集群环境启动一切OK。

 开始启动运行SparkStreaming应用程序

 

  启动外部请求SparkStreaming服务端口的客户端:

 

  输入待处理的数据流:

 

  看结果如下:

  

  看webui控制台:

点击链接进入后产生了0~4个Job:

有意思的是SparkStreaming应用程序启动实际执行的是一个Job,但真正执行的是5个Job,其分别是Receiver Job,Output Job,Output Job,Output Job,Start Job.

第 0 个Job是不是我们逻辑中的代码?不是的,不是reduceByKey的执行结果Job,如下图:

SparkStreaming在启动的过程中会自动启动一些Job,如start操作:

SparkStreaming最像一个应用程序,就算是算一次,也执行了好几个Job,就像spark应用程序一样,可以启动不同的Job完成不同的功能。

继续看Job1:

通过Job告诉你内幕:通过追踪Receiver发现其会产生makeRDD,实际上作为整个Job独立的一个stage,只在一台机器上执行,而且执行了1.5分钟,刚才启动SparkStreaming,没有任务执行1.5分钟的,如下图:

思考一下什么东西执行了1.5分钟,而整个Job只运行了2分钟?

答案就是ReceiverTracker接收器运行的,它需要接收流入的数据。这个Job就是Receiver,并且执行了1.5分钟,而启动的Receiver就是一个Job。

结论:

SparkStreaming启动Receiver的是一个Job,在具体的Cluster的Worker上的executor中,启动Receiver是通过Job启动的。通过作业的运行时间看出,整个SparkStreaming运行的时间是2分钟,其中有个Job运行了1.5分钟,这个Job就是Receiver,其实指的是Receiver启动运行的时间,Receiver是在executor中运行的,也就是说SparkStreaming框架在启动Recevier是通过Job启动的。而且Receiver(可以启动多个receiver接收数据)就是在一个executor中运行且通过一个Task去接收我们的数据:

从这个角度讲Receiver接收数据和普通job有什么区别?没有区别。转过来给我们启发:在一个Spark application中可以启动很多的job,这些job之间可以相互配合。例如:SparkStreaming框架默认启动job给你接收数据,然后为后续的处理做准备,为你写复杂的应用程序奠定了一个良好的基础。这就是你写非常复杂的Spark应用程序的黄金切入点,复杂的程序一般都是有多个job构成的。

上图的Process_local即内存节点,SparkStreaming在默认情况下接收数据是memory_and_disk_ser_2的方式,也就是说接收的数据量比较少内存能存下的话默认情况下是不会存储磁盘的,在这里直接使用内存中。

看下第0个job:

在4个worker上启动4个executor,是在最大化的使用计算资源,通过第1个job 不断接收数据。

这里处理数据有shuffle read,shuffle write,通过socketTextStream即rdd,这里叫blockRdd,而且blockrdd来自于socketTextStream的方法:

其实是inputStream帮我们在固定时间间隔内会产生固定的rdd,接收数据是在一个executor的task中接收的,但现在处理数据是transform操作发生在executor里面的发生在4个executor,这个结果告诉我们在一台机器上接收数据,但实际上是在四台机器上处理数据的。最大化利用集群资源处理数据。SparkStreaming程序执行时就是一个batch级别的Job,里面做了很多事情。整个处理,其实只有一个Job真正在执行,但产生很多Job相互协调来完成复杂的业务处理,这个情况告诉我们SparkStreaming并不是网络、博客、书籍、官网上讲的那么简单。

SparkStreaming本身是随着流进来的数据按照时间为单位生成job,然后触发job在Cluster上执行的流式处理的引擎,它本身是加上以时间为维度的批处理,实例中以300秒为会产生一批数据,基于这一批数据会生成rdd,基于rdd会触发job,rdd的生成、job的触发,都是SparkStreaming框架去做的。SparkStreaming中有个至关只要的东西叫DStream,我们每隔一定时间都会生成rdd,产生rdd的依赖或触发job具体的执行。每隔时间,所以弄了一个DStream,DStream代表时空的概念,时间为维度,随着时间的推进不断产生rdd,实际上DStream就是rdd的集合,只不过是有时间的先后顺序;空间维度实际上是DStream的处理层面,我们对DStream进行处理实际上是对DStream里面的每个rdd的处理。整个时空是一个很大的概念,时间固定的话,可以锁定对空间的操作,操作其实就是transform,对DStream的操作会构建DStream Graph。

总结:

随着时间为维度有个DStream Graph,同时在时间维度下有个空间维度,空间维度就是操作,空间维度确定的情况下时间不断推进的时候他就不断把空间维度的DStream Graph实例化成rdd的graph,然后触发具体的job进行执行。

Spark发行版笔记1

新浪微博:http://weibo.com/ilovepains

微信公众号:DT_Spark

博客:http://blog.sina.com.cn/ilovepains

手机:18610086859

QQ:1740415547

邮箱:18610086859@vip.126.com

 

转载于:https://www.cnblogs.com/sparkbigdata/p/5468003.html

<think>好的,用户之前询问了关于编码的内容,现在他们提到“总感觉对计算机运行原理理解透彻”,但实际上可能是在表达自己对计算机运行原理的理解不够深入,感到困惑或不确定。需要先分析用户的需求。 首先,用户可能在学习计算机科学的基础知识,尤其是在编码之后,想进一步了解计算机如何运作。他们可能遇到了难以将各个概念(如硬件、软件、数据表示等)联系起来的困难,或者对底层机制如CPU工作、内存管理、操作系统等感到抽象。 接下来,要确定用户的背景。他们可能是学生、刚入行的开发者,或者是对计算机感兴趣的自学者。不同背景需要不同的解释深度。考虑到之前的编码问题,用户可能有一定的技术基础,但需要更系统化的梳理。 用户的需求可能包括:1)理清计算机运行的核心组成部分及其相互关系;2)理解从高层应用到底层硬件的整个流程;3)找到学习路径或资源来加深理解。深层需求可能是想建立系统的知识框架,弥补零散知识的不足,从而提升解决问题的能力。 需要结构化回答,从基础组成部分开始,分层解析,可能包括硬件层、指令集、操作系统、编程层等。使用比喻或类比帮助理解,比如将计算机比作工厂,数据流比作流水线。同时,结合用户之前关于编码的知识,说明编码在数据表示中的作用如何融入整个系统。 另外,用户可能有误解或知识盲点,比如混淆硬件和软件的交互,或者不清楚编译过程与执行过程的关系。需要指出常见误区,并提供验证方法,比如通过实际代码示例或工具(如调试器、模拟器)来观察计算机行为。 最后,推荐学习资源和实践方法,帮助用户逐步构建知识体系,强调动手实践的重要性,如编写低级代码、使用模拟器等。确保回答既全面又易于理解,避免过于技术化的术语,同时保持准确性。</think>以下是一个结构化、分层次的计算机运行原理解析框架,采用从宏观到微观的递进式说明,配合关键验证方法: --- ### **一、理解层级模型(自顶向下)** $$计算机系统 = 抽象层 \otimes 实现层 \otimes 物理层$$ #### **1. 抽象认知层** **核心公式**: ```text 计算机 = 输入系统 × 处理系统 × 输出系统 × 存储系统 ↓ ↓ ↓ ↓ 传感器 CPU/GPU 显示器 内存/硬盘 ``` **运行周期**: $$ \text{冯·诺依曼循环} = \begin{cases} \text{取指阶段} & (PC \rightarrow IR) \\ \text{译码阶段} & (控制单元解析指令) \\ \text{执行阶段} & (ALU运算/内存访问) \\ \text{写回阶段} & (更新寄存器/内存) \end{cases} $$ **验证方法**: 使用Python模拟简单CPU: ```python # 简易指令周期模拟器 registers = {'PC':0, 'ACC':0} memory = [0x105, 0x306, 0x402] # 示例指令 def fetch(): instr = memory[registers['PC']] registers['PC'] += 1 return instr def execute(instr): opcode = instr >> 8 operand = instr & 0xFF if opcode == 0x1: # LOAD registers['ACC'] = operand elif opcode == 0x3: # ADD registers['ACC'] += operand elif opcode == 0x4: # STORE memory[operand] = registers['ACC'] while registers['PC'] < len(memory): instruction = fetch() execute(instruction) print(memory) # 查看内存变化 ``` --- ### **二、关键子系统解析** #### **1. 数据表示体系** **数值编码**: $$ \begin{align*} \text{整数} & : \text{补码表示法} \\ x_{补码} &= \begin{cases} x & 0 \leq x < 2^{n-1} \\ x - 2^n & -2^{n-1} \leq x < 0 \end{cases} \\ \text{浮点数} & : IEEE754标准 \\ (-1)^s \times m \times 2^{e-b} \end{align*} $$ **验证实验**: ```c #include <stdio.h> #include <stdint.h> int main() { int32_t x = -1; // 查看内存中的补码表示 printf("%08x\n", *(uint32_t*)&x); // 输出ffffffff float f = 0.1; // 查看IEEE754编码 printf("%08x\n", *(uint32_t*)&f); // 输出3dcccccd return 0; } ``` #### **2. 存储器层次结构** $$访问时间 \propto \frac{1}{存储容量}$$ ``` 寄存器 → L1缓存 → L2缓存 → L3缓存 → 内存 → SSD/HDD 1ns 3ns 10ns 20ns 100ns 10ms ``` **性能验证**: ```cpp #include <chrono> const int SIZE = 1000000; void test_access(int* arr, int step) { auto start = std::chrono::high_resolution_clock::now(); for(int i=0; i<SIZE*10; i++) { arr[(i*step) % SIZE] += 1; } auto end = std::chrono::high_resolution_clock::now(); std::cout << "Step " << step << ": " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count() << "μs\n"; } int main() { int* arr = new int[SIZE]; test_access(arr, 1); // 顺序访问 test_access(arr, 16); // 缓存行跳跃访问 test_access(arr, 4096); // 内存页跳跃访问 delete[] arr; } ``` --- ### **三、抽象到硬件的映射** #### **1. 程序执行的全链路解析** ``` 高级语言 → 编译器 → 汇编语言 → 汇编器 → 机器码 → 微指令 ↓ ↓ ↓ ↓ ↓ 源代码 → 语法树 → 中间代码 → 目标代码 → 二进制文件 → 时钟周期 ``` **关键转换示例**: ```c // C代码 int sum(int a, int b) { return a + b; } // x86-64汇编 sum: lea eax, [rdi+rsi] ret // 机器码(十六进制) 8d 04 37 c3 ``` #### **2. 操作系统核心机制** **虚拟内存映射**: $$ 虚拟地址 \xrightarrow{页表查询} 物理地址 = \begin{cases} 页表项存在 & \rightarrow 直接转换 \\ 页表项不存在 & \rightarrow 触发缺页中断 \end{cases} $$ **验证方法**: ```bash # 查看Linux进程内存映射 cat /proc/[pid]/maps # 示例输出: 00400000-00401000 r-xp 00000000 08:01 393217 /path/to/program 7ffff7ff9000-7ffff7ffd000 r--p 00000000 00:00 0 [vvar] ``` --- ### **四、理解障碍突破指南** #### **1. 常见认知误区** | 误区描述 | 正解 | 验证方法 | |---------------------------|--------------------------|--------------------------| | "CPU直接运行高级语言" | 需经多级转换到机器指令 | 使用编译器生成汇编输出 | | "内存是线性连续空间" | 存在虚拟地址转换机制 | 观察指针地址的虚实映射 | | "硬盘存储文件原样" | 文件系统存在元数据管理 | 使用hexdump查看原始扇区 | #### **2. 深度理解工具链** 1. **QEMU模拟器**:观察CPU寄存器实时变化 2. **GDB调试器**:单步跟踪机器指令执行 3. **LLVM中间表示**:分析编译器转换过程 4. **Verilog数字仿真**:构建简单CPU电路 **推荐实践项目**: - 用Logisim实现8位CPU - 编写简易操作系统内核 - 创建自己的汇编器 - 分析JVM字节码执行过程 --- ### **五、知识体系构建路径** $$ \begin{array}{ccc} \text{应用层} & \rightarrow & \text{系统层} & \rightarrow & \text{硬件层} \\ \downarrow & & \downarrow & & \downarrow \\ 编程语言 & 操作系统 & 组成原理 \\ 编译器 & 计算机网络 & 数字电路 \\ \end{array} $$ **推荐学习路线**: 1. 先修基础:《深入理解计算机系统》(CSAPP) 2. 实践强化:MIT 6.004(Computation Structures) 3. 系统深化:《操作系统导论》(OSTEP) 4. 硬件衔接:《数字设计与计算机体系结构》 --- 通过这种分层解构+实践验证的方式,可以逐步建立对计算机系统的完整认知框架。建议从微观模块实现入手(如自行设计简单CPU),再扩展到系统级交互,最终形成多维度理解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值