首页 文章 精选 留言 我的

精选列表

搜索[面试],共4912篇文章
优秀的个人博客,低调大师

金三银四,HTTP面试手册

互联网中的数据是通过数据包来传输的,IP通过DNS查询IP地址进而把数据包送达目的主机,UDP接过数据包,通过端口号把数据包送往具体的应用,而使用TCP可保证数据的完整性 当传输层TCP/IP协议将数据传输到网络上时,浏览器可通过HTTP协议进行文本传输,物联网可通过MQTT协议进行交互 一、UDP UDP传输非常快,适合在线视频、互动游戏这类强交互的场景 对于数据可靠性有要求的场景则不太适合,它有个大缺点:不能保证数据可靠性 不提供重发机制,直接丢弃当前的包 发送之后不售后,无法确认是否到达目的地 无法还原数据包成完整的文件 但它的兄弟TCP可以代劳 二、TCP TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。解决了丢失数据包的问题,并且提供了组装数据包的能力。这不得不感谢发送端不仅给它提供了源端口号和目标端口号,还提供了序列号,引入了数据包排列机制 1、连接过程 1.1、建立连接 TCP是面向连接的,在数据通信之前就做好两端的准备工作,在客户端和服务端通过三个数据包来确认连接的建立 一开始,客户端和服务端都处于 close状态,接着服务端主动监听某个端口,处于listen状态 客户端生成初始序列号client_isn置于TCP首部的序列号中,同时更改SYN标志为1向服务器发起连接,之后处于SYN-SENT状态 服务端收到SYN报文同样生成初始序列号server_isn置于TCP首部的序列号中,并将客户端序列号+1置于TCP首部的确认应答号中,同时更改SYN和ACK标志为1,发送SYN + ACK报文并更改状态为SYN_RCVD 客户端收到SYN + ACK报文,将服务端序列号+1填入确认应答号并回复ACK应答报文变更状态为established 服务器收到应答后也进入established状态 只有第三次握手可以携带数据,前面两次是不可携带数据的 1.2、为什么三次握手? 通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号 两次握手无法仿制历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号 四次握手:三次握手就已经理论上最少可靠连接建立,因此不需要使用更多的通信次数 2、传输数据 TCP是可靠的,接收端必须对每个数据包进行确认操作,即重发机制 当发送端发送了一个数据包之后,在规定时间内没有接收到反馈的确认信息,则判断数据包丢失,触发重发机制 TCP是基于字节流的,接收端可通过TCP提供的序号进行排序,进而保证数据的完整性,即排列机制 3、断开过程 3.1、断开连接 客户端主动关闭连接,发送FIN报文,即更改FIN标志为1同时进入FIN_WAIT_1状态 服务端收到报文后发出ACK应答报文,接着进入CLOSED_WAIT状态 客户端收到报文后,进入FIN_WAIT_2状态 服务器处理完成后发出FIN报文,并进入LAST_ACK状态 客户端收到报文后发出ACK应答报文,接着进入TIME_WAIT状态 服务器收到ACK应答报文后完成连接关闭 客户端 等待两倍报文最大生存时间(MSL)后 自动进入close状态,完成连接关闭 主动关闭连接的,才有TIME_WAIT状态 3.2、为什么四次挥手? 关闭连接时,客户端向服务端发送FIN时,仅代表客户端不再发送数据但能接收数据 服务端接收到FIN时,回复一个ACK,仅代表收到报文,但服务器可能还有数据需要处理和发送,确保不再发送数据时才发送FIN给到客户端表示同意现在关闭连接 三、HTTP HTTP 协议以 ASCII 码传输,构建于 TCP/IP 协议之上的应用层协议,默认端口号是 80,它是无连接无状态的超文本传输协议 1、HTTP 报文 1.1、请求报文 规范把 HTTP 请求分为三个部分:请求行、请求头 和 消息主体 [method] [url] [version] [headers] [body] HTTP 中的GET、POST、PUT、DELETE对应着资源的查、增、改、删4个操作 1.1.1、GET 只读操作,是安全且幂等的 安全:请求方法不会破坏服务器上的资源 幂等:多次执行相同的操作,结果都是相同的 1.1.2、POST 读写操作,是不安全且不幂等的 1.1.3、PUT 不同于POST,PUT是幂等的 1.1.4、OPTIONS 用以从服务器获取更多信息 1.2、响应报文 同样HTTP响应分为三个部分:状态行、响应头 和 响应正文 [version] [status code] [status msg] [headers] [body] 常见的状态码有 状态码 状态描述 备注 206 Partial Content 范围响应,主体包含所请求的数据区间<br/>断点续传时通过 Range 指定区间 301 Moved Permanently 请求永久重定向 302 Moved Temporarily 请求临时重定向 304 Not Modified 未修改,使用缓存文件(协商缓存) 400 Bad Request 客户端请求有语法错误 401 Unauthorized 请求未经授权(同WWW-Authenticate一起使用)<br/>在后续请求中携带 Authorization用于验证用户代理身份的凭证 403 Forbidden 服务器拒绝提供服务,通常在响应正文给出原因 404 Not Found 请求资源不存在 500 Internal Server Error 服务器发生不可预期的错误 503 Service Unavailable 服务器当前无法处理请求,需等待服务器恢复正常 2、HTTP 演变 版本 核心诉求 新增特性 HTTP/1.0 支持多种类型的文件下载 引入请求头、响应头、状态码 HTTP/1.1 提高对带宽的利用率 1、持久连接(每个域名最多同时维护 6 个 TCP 持久连接) 2、使用 CDN 实现域名分片机制 3、提供虚拟主机的支持(Host 字段) 4、增加缓存策略 5、安全机制(CORS) HTTP/2.0 提升网络速度 1、多路复用 2、设置请求的优先级 3、服务器推送 4、头部压缩 5、二进制格式 HTTP/3.0 构建高效网络 1、甩掉TCP、TLS 的包袱,使用UDP协议 2、QUIC协议 HTTPS 构建安全HTTP 引入SSL 、混合加密、摘要算法 、数字证书 2.1、持久连接 HTTP/1.1 中增加了持久连接的方法,即在一个 TCP 连接上可以传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开连接,那么该 TCP 连接会一直保持,提升了整体 HTTP 的请求时长。目前浏览器中对于同一个域名,默认允许同时建立 6 个 TCP 持久连接 Connection: Keep-Alive; // HTTP/1.1默认使用持久连接,如需关闭,请求头Connection设置为close Keep-Alive: timeout=5, max=100; // HTTP 长连接不可能一直保持,timeout=5 表示这个TCP通道可以保持5秒,max=100,表示这个长连接最多接收100次请求就断开 2.2、使用 CDN 实现域名分片机制 2.3、提供虚拟主机的支持 Host表示当前的域名地址,服务器可以根据不同的 Host 值做不同的处理 Host: <host>:<port>; // host: 服务器的域名(用于虚拟主机) port: 服务器监听的 TCP 端口号 2.4、缓存策略 2.5、安全机制 2.5.1、会话跟踪 HTTP是无状态协议,即浏览器对于事务的处理没有记忆能力,可通过Cookie和JWT机制来进行会话跟踪 Cookie机制 服务端第一次收到请求时创建session对象生成对应的sessionID,将其放进Set-Cookie发送给客户端,下一次访问时,客户端携带sessionID请求服务端,服务端可通过sessionID识别用户信息 Cookie 的过期时间、域、路径、有效期、适用站点都可以根据需要来指定 功能 属性 例子 补充说明 定义 Cookie 的生命周期 Expires <br/> Max-Age Set-Cookie: key=value; Expires=Wed, 21 Oct 2022 07:28:00 GMT 设定的日期和时间只与客户端相关,会话期 Cookie 仅在会话期内有效 限制访问 Cookie HttpOnly<br/>Secure Set-Cookie: key=value; Secure; HttpOnly HttpOnly:仅作用于服务器<br/>Secure仅适用于 HTTPS 协议加密过的请求 Cookie 的作用域 Domain<br/>Path Set-Cookie:Domain=mozilla.org;Path=/docs Domain 指定了哪些主机可以接受 Cookie<br/>Path 指定了主机下的哪些路径可以接受 Cookie SameSite None<br/>Strict<br/>Lax Set-Cookie: key=value; SameSite=Strict None:浏览器会在同站请求、跨站请求下继续发送 cookies(旧版本浏览器默认选项)<br/>Strict:浏览器将只在访问相同站点时发送 cookie<br/>Lax:与 Strict 类似,但用户从外部站点导航至URL时除外(新版本浏览器默认选项) JWT机制 (JSON Web Token) Cookies 只适用于单节点的域 或 节点的子域,若通过第三个节点访问会被禁止。而JWT机制则支持跨域认证,可通过多个节点进行用户认证 服务端第一次收到请求时,进行认证后生成一个 Token(签名后的JSON 对象)发送给客户端。客户端可将收到的jwt存储在Cookie或localStorage上,之后每次与服务端通信都携带上,可通过Cookie自动发送,但这种方式不能跨域,比较推荐通过 POST 请求的数据体 或 Authorization进行传递 注意喔,JWT 的 Cookie 信息存储在客户端,即服务端是无状态的 2.5.2、跨源资源共享(CORS) 规范要求那些可能产生副作用的请求,浏览器必须首先使用OPTIONS方法发起一个预检请求,从而获知服务端是否允许跨域请求。服务器确认允许后才发起实际的HTTP请求。在预检请求的返回中,服务端可通知客户端是否需要携带身份凭证 简单请求 若请求满足下述所有条件,则称之为简单请求,它不会触发预检请求 1、使用GET、HEAD和POST请求方法 2、Content-Type的值仅限于text/plain、multipart/form-data和application/x-www-form-urlencoded 3、请求中没有注册任何事件监听器,没有使用 ReadableStream 对象 // 附带身份凭证的简单请求 withCredentials:true; // 向服务器发送 Cookies Access-Control-Allow-Credentials: true; // 服务端允许附带身份凭证 复杂请求 响应头 例子 说明 Access-Control-Allow-Origin Access-Control-Allow-Origin: <origin>/* Vary: Origin origin:指定允许访问该资源的URI<br/>若指定了具体的域名,则Vary的值必须包含Origin,表明服务端按URI返回对应内容 Access-Control-Expose-Headers Access-Control-Expose-Headers: X-My-Custom-Header 服务器把允许浏览器访问的头放入白名单 Access-Control-Allow-Credentials Access-Control-Allow-Credentials: true 指定了credentials:true时是否允许浏览器读取 response 的内容<br/>在预检请求的响应时,指定实际的请求是否可以使用 credentials Access-Control-Max-Age Access-Control-Max-Age: 86400 预检请求的结果在多少秒内有效 Access-Control-Allow-Methods Access-Control-Allow-Methods: <method>[, <method>]* 预检请求的响应,指明了实际请求所允许使用的 HTTP 方法 Access-Control-Allow-Headers Access-Control-Allow-Headers: <field-name>[, <field-name>]* 预检请求的响应,指明了实际请求中允许携带的首部字段 2.6、支持动态生成内容 服务器会将数据分割成若干个任意大小的数据块,每个数据块发送时会附上上个数据块的长度,最后使用一个零长度的块作为发送数据完成的标志,因此对于下载请求来说,是没有办法实现进度的 Transfer-Encoding: gzip, chunked; // 分块:chunked 压缩算法:compress、deflate、gzip 2.7、多路复用 一个域名只使用一个TCP长连接来传输数据,这样整个页面资源的下载过程只需要一次慢启动,避免了多个 TCP 连接竞争带宽的问题。移除了串行请求,顺应的解决了队头阻塞问题 2.8、设置请求的优先级 每个数据流都标记着独一无二的编号,客户端可以指定数据流的优先级 2.9、服务器推送 服务端主动向客户端发送消息,即:当用户请求一个 HTML 页面之后,服务器知道该 HTML 页面会引用几个重要的 JavaScript 文件和 CSS 文件,那么在接收到 HTML 请求之后,附带将要使用的 CSS 文件和 JavaScript 文件一并发送给浏览器,这样当浏览器解析完 HTML 文件之后,就能直接拿到需要的 CSS 文件和 JavaScript 文件,大大提升了页面首次渲染速度 2.10、头部压缩 HTTP/2.0引入HPACK算法:在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表生成一个索引号,相同字段只发送对应的索引号,即:同时发出多个请求,请求头一样或相似,则协议会将重复部分消除 2.11、二进制格式 HTTP/2.0 全面采用二进制格式,头信息和数据体都是二进制,统称为「帧」提高了数据传输的效率 2.12、QUIC协议 实现了类似 TCP 的流量控制、传输可靠性的功能 集成了 TLS 加密功能,减少了握手所花费的 RTT 个数 实现了 HTTP/2 中的多路复用功能 不同于 TCP,QUIC 实现了在同一物理连接上可以有多个独立的逻辑数据流,实现了数据流的单独传输,避免了 TCP 中队头阻塞的问题 实现了快速握手功能,基于 UDP 的 QUIC 可使用 0-RTT|1-RTT 来建立连接 3、HTTPS HTTPS在HTTP和TCP之间加了一层用于加解密的SSL/TLS协议,通过信息加密、校验机制 和 身份证书 保证通信的安全性 客户端 发送 对称加密套件列表、非对称加密套件列表 和 客户端随机数 给到服务端 服务端 保存 客户端随机数 和 私钥,回复 选中的对称加密套件、非对称加密套件 和 服务端随机数 以及 数字证书 客户端向 CA机构验证数字证书,证实服务端身份并获取公钥 客户端利用两端的随机数计算出pre-master,并用获取到的公钥进行加密,发送加密后的pre-master 服务端拿出私钥进行解密,得到pre-master 服务端和客户端使用这三组随机数生成会话密钥,并返回确认消息 之后使用对称加密进行通讯 3.1、混合加密 HTTPS通过非对称加密交换「会话密钥」后续通信使用对称加密,这是由于 非对称加密使用两个密钥:公钥和私钥,公钥可保存在CA机构同时保存私钥。可以安全的进行密钥交换,但速度慢 对称加密只使用一个密钥,无法做到安全的密钥交换,但速度快 3.2、摘要算法 摘要算法通过生成唯一的指纹,用于校验数据的完整性。客户端在进行通信前会通过摘要算法得出明文的指纹,请求时将指纹和明文一并加密,服务端收到密文后进行解密,比对携带的指纹和当前计算的指纹是否一致,一致则说明数据完整 3.3、数字证书 权威机构CA签发认证的数字证书【包含了公钥、组织信息、CA信息、有效时间、证书序列号、CA生成的数字签名等】这些信息是明文的,同时可向浏览器证明服务器的身份 四、MQTT MQTT是基于二进制消息的发布/订阅编程模式的消息协议,非常适合需要低功耗和网络带宽有限的IoT场景 设备连接 设备通过MQTT协议连接到物联网云服务,进而可以进行设备管理及数据管理 消息类型 MQTT拥有14种不同的消息类型,比如CONNECT表示客户端连接到MQTT代理,CONNACK表示连接确认 主题 MQTT提供了主题对消息进行分类,消息是一个UTF-8的字符串,通过类似正则的规则进行匹配分类,比如:+可以过滤一个层级,*可以过滤任意级别的层级(必须在主题最后) 服务质量 MQTT提供级别0、级别1和级别2三种服务质量 服务质量 消息可靠性 解释 级别0 尽力而为 不提供重发 级别1 至少一次 提供重发,并发可能造成重复消息 级别2 恰好一次 不丢失不重复,但增加延时减少并发

优秀的个人博客,低调大师

面试系列五 之 项目涉及技术Spark

# 一、Spark ### 1.1 Spark有几种部署方式?请分别简要论述 - 1)Local:运行在一台机器上,通常是练手或者测试环境。 - 2)Standalone:构建一个基于Mster+Slaves的资源调度集群,Spark任务提交给Master运行。是Spark自身的一个调度系统。 - 3)Yarn: Spark客户端直接连接Yarn,不需要额外构建Spark集群。有yarn-client和yarn-cluster两种模式,主要区别在于:Driver程序的运行节点。 - 4)Mesos:国内大环境比较少用。 ### 1.2 Spark任务使用什么进行提交,javaEE界面还是脚本 Shell 脚本。 ### 1.3 Spark提交作业参数(重点) 1)在提交任务时的几个重要参数 - executor-cores —— 每个executor使用的内核数,默认为1,官方建议2-5个,我们企业是4个 - num-executors —— 启动executors的数量,默认为2 - executor-memory —— executor内存大小,默认1G - driver-cores —— driver使用内核数,默认为1 - driver-memory —— driver内存大小,默认512M 2)边给一个提交任务的样式 ```shell spark-submit \ --master local\[5\] \ --driver-cores 2 \ --driver-memory 8g \ --executor-cores 4 \ --num-executors 10 \ --executor-memory 8g \ --class PackageName.ClassName XXXX.jar \ --name "Spark Job Name" \ InputPath \ OutputPath ``` ### 1.4 简述Spark的架构与作业提交流程(画图讲解,注明各个部分的作用)(重点) 参考: https://blog.csdn.net/wuxintdrh/article/details/70956686 #### 1.4.1、standlone ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620220315942.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) #### 1.4.2、yarn-cluster ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620220419919.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ### 1.5 如何理解Spark中的血统概念(RDD)(笔试重点) 参考:https://blog.csdn.net/wuxintdrh/article/details/70840323 `RDD`在`Lineage`依赖方面分为两种`Narrow Dependencies`与`Wide Dependencies`用来解决数据容错时的高效性以及划分任务时候起到重要作用。 ### 1.6 简述Spark的宽窄依赖,以及Spark如何划分stage,每个stage又根据什么决定task个数? (笔试重点) Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620220618234.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) Task:Stage是一个TaskSet,将Stage根据分区数划分成一个个的Task。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620220643695.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ### 1.7 请列举Spark的transformation算子(不少于8个),并简述功能(重点) **参考: https://blog.csdn.net/wuxintdrh/article/details/80815731** 1)map(func):返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成. 2)mapPartitions(func):类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RD上运行时,func的函数类型必须是Iterator\[T\] => Iterator\[U\]。假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区。 3)reduceByKey(func,\[numTask\]):在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用定的reduce函数,将相同key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置。 4)aggregateByKey (zeroValue:U,\[partitioner: Partitioner\]) (seqOp: (U, V) => U,combOp: (U, U) => U: 在kv对的RDD中,,按key将value进行分组合并,合并时,将每个value和初始值作为seq函数的参数,进行计算,返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出。 5)combineByKey(createCombiner: V=>C, mergeValue: (C, V) =>C, mergeCombiners: (C, C) =>C): 对相同K,把V合并成一个集合。 1.createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的元素,combineByKey()会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值 2.mergeValue: 如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并 3.mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。 … 根据自身情况选择比较熟悉的算子加以介绍。 ### 1.8 请列举Spark的action算子(不少于6个),并简述功能(重点) **参考: https://blog.csdn.net/wuxintdrh/article/details/80815731** 1)reduce: 2)collect: 3)first: 4)take: 5)aggregate: 6)countByKey: 7)foreach: 8)saveAsTextFile: ### 1.9 请列举会引起Shuffle过程的Spark算子,并简述功能。 reduceBykey: groupByKey: … ByKey: ### 1.10 简述Spark的两种核心Shuffle(HashShuffle与SortShuffle)的工作流程(包括未优化的HashShuffle、优化的HashShuffle、普通的SortShuffle与bypass的SortShuffle)(重点) 未经优化的HashShuffle: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620221116623.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) 优化后的Shuffle: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620221221471.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) 普通的SortShuffle: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620221316761.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) 当 `shuffle read task` 的 数 量 小 于 等 于 `spark.shuffle.sort`。 `bypassMergeThreshold` 参数的值时(默认为 200),就会**启用 bypass 机制**。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620221412460.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70#pic_center) ### 1.11 Spark常用算子reduceByKey与groupByKey的区别,哪一种更具优势?(重点) reduceByKey:按照key进行聚合,**在shuffle之前有combine(预聚合)操作**,返回结果是RDD\[k,v\]。 groupByKey:按照key进行分组,直接进行shuffle。 开发指导:reduceByKey比groupByKey,建议使用。但是需要注意是否会影响业务逻辑。 ### 1.12 Repartition和Coalesce关系与区别 1)关系: 两者都是用来改变RDD的partition数量的,repartition底层调用的就是coalesce方法:`coalesce(numPartitions, shuffle = true)` 2)区别: **repartition一定会发生shuffle,coalesce根据传入的参数来判断是否发生shuffle** 一般情况下 - 增大rdd的partition数量使用repartition - 减少partition数量时使用coalesce ### 1.13 分别简述Spark中的缓存机制(cache和persist)与checkpoint机制,并指出两者的区别与联系 都是做RDD持久化的 `cache`:内存,不会截断血缘关系,使用计算过程中的数据缓存。 `checkpoint`:磁盘,**截断血缘关系**,在ck之前必须没有任何任务提交才会生效,ck过程会额外提交一次任务。 ### 1.14 简述Spark中共享变量(广播变量和累加器)的基本原理与用途。(重点) **累加器(accumulator)**是Spark中提供的一种分布式的变量机制,其原理类似于mapreduce,即分布式的改变,然后聚合这些改变。累加器的一个常见用途是在调试时对作业执行过程中的事件进行计数。而广播变量用来高效分发较大的对象。 共享变量出现的原因: 通常在向 Spark 传递函数时,比如使用 map() 函数或者用 filter() 传条件时,可以使用驱动器程序中定义的变量,但是集群中运行的每个任务都会得到这些变量的一份新的副本,更新这些副本的值也不会影响驱动器中的对应变量。 Spark的两个共享变量,累加器与广播变量,分别为结果聚合与广播这两种常见的通信模式突破了这一限制。 ### 1.15 当Spark涉及到数据库的操作时,如何减少Spark运行中的数据库连接数? 使用foreachPartition代替foreach,在foreachPartition内获取数据库的连接。 ### 1.16 简述SparkSQL中RDD、DataFrame、DataSet三者的区别与联系? (笔试重点) **1)RDD** 优点: - 编译时类型安全 - 编译时就能检查出类型错误 - 面向对象的编程风格 - 直接通过类名点的方式来操作数据 缺点: - 序列化和反序列化的性能开销 - 无论是集群间的通信, 还是IO操作都需要对对象的结构和数据进行序列化和反序列化。 - GC的性能开销,频繁的创建和销毁对象, 势必会增加GC **2)DataFrame** DataFrame引入了`schema`和`off-heap` schema : RDD每一行的数据, 结构都是一样的,这个结构就存储在schema中。 Spark通过schema就能够读懂数据, 因此在通信和IO时就只需要序列化和反序列化数据, 而结构的部分就可以省略了。 **3)DataSet** DataSet结合了RDD和DataFrame的优点,并带来的一个新的概念`Encoder`。 当序列化数据时,Encoder产生字节码与off-heap进行交互,能够达到按需访问数据的效果,而不用反序列化整个对象。Spark还没有提供自定义Encoder的API,但是未来会加入。 **三者之间的转换:** ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620222320481.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1eGludGRyaA==,size_16,color_FFFFFF,t_70) ### 1.17 SparkSQL中join操作与left join操作的区别? join和sql中的inner join操作很相似,返回结果是前面一个集合和后面一个集合中匹配成功的,过滤掉关联不上的。 leftJoin类似于SQL中的左外关联left outer join,返回结果以第一个RDD为主,关联不上的记录为空。 部分场景下可以使用left semi join替代left join: 因为 left semi join 是 in(keySet) 的关系,**遇到右表重复记录,左表会跳过****,****性能更高**,而 left join 则会一直遍历。**但是left semi join 中最后 select 的结果中只许出现左表中的列名**,因为右表只有 join key 参与关联计算了 ### 1.18 请手写出wordcount的Spark代码实现(Scala)(手写代码重点) ```scala val conf: SparkConf = new SparkConf().setMaster("local\[*\]").setAppName("WordCount") val sc = new SparkContext(conf) sc.textFile("/input") .flatMap(_.split(" ")) .map((_,1)) .reduceByKey(_+_) .saveAsTextFile("/output") sc.stop() ``` ### 1.19、 如何使用[Spark实现topN](https://blog.csdn.net/wuxintdrh/article/details/72810306)的获取(描述思路或使用伪代码)(重点) 方法1: - (1)按照key对数据进行聚合(groupByKey) - (2)将value转换为数组,利用scala的sortBy或者sortWith进行排序(mapValues)数据量太大,会OOM。 方法2: - (1)取出所有的key - (2)对key进行迭代,每次取出一个key利用spark的排序算子进行排序 方法3: - (1)自定义分区器,按照key进行分区,使不同的key进到不同的分区 - (2)对每个分区运用spark的排序算子进行排序 ### 1.20 京东:调优之前与调优之后性能的详细对比(例如调整map个数,map个数之前多少、之后多少,有什么提升) 这里举个例子。比如我们有几百个文件,会有几百个map出现,读取之后进行join操作,会非常的慢。这个时候我们可以进行coalesce操作,比如240个map,我们合成60个map,也就是窄依赖。这样再shuffle,过程产生的文件数会大大减少。提高join的时间性能。 # 二、SparkStreaming 参考: https://chbxw.blog.csdn.net/article/details/80809898 ### 2.1、 SparkStreaming有哪几种方式消费Kafka中的数据,它们之间的区别是什么? **1、基于Receiver的方式** 这种方式使用Receiver来获取数据。Receiver是使用Kafka的高层次Consumer API来实现的。receiver从Kafka中获取的数据都是存储在Spark Executor的内存中的(如果突然数据暴增,大量batch堆积,很容易出现内存溢出的问题),然后Spark Streaming启动的job会去处理那些数据。 然而,在默认的配置下,这种方式可能会因为底层的失败而丢失数据。如果要启用高可靠机制,让数据零丢失,就必须启用Spark Streaming的预写日志机制(Write Ahead Log,WAL)。该机制会同步地将接收到的Kafka数据写入分布式文件系统(比如HDFS)上的预写日志中。所以,即使底层节点出现了失败,也可以使用预写日志中的数据进行恢复。 **2、基于Direct的方式** 这种新的不基于Receiver的直接方式,是在Spark 1.3中引入的,从而能够确保更加健壮的机制。替代掉使用Receiver来接收数据后,这种方式会周期性地查询Kafka,来获得每个topic+partition的最新的offset,从而定义每个batch的offset的范围。当处理数据的job启动时,就会使用Kafka的简单consumer api来获取Kafka指定offset范围的数据。 **优点如下**: **简化并行读取**:如果要读取多个partition,不需要创建多个输入DStream然后对它们进行union操作。Spark会创建跟Kafka partition一样多的RDD partition,并且会并行从Kafka中读取数据。所以在Kafka partition和RDD partition之间,有一个一对一的映射关系。 **高性能**:如果要保证零数据丢失,在基于receiver的方式中,需要开启WAL机制。这种方式其实效率低下,因为数据实际上被复制了两份,Kafka自己本身就有高可靠的机制,会对数据复制一份,而这里又会复制一份到WAL中。而基于direct的方式,不依赖Receiver,不需要开启WAL机制,只要Kafka中作了数据的复制,那么就可以通过Kafka的副本进行恢复。 **一次且仅一次的事务机制**。 **3、对比:** 基于receiver的方式,是使用Kafka的高阶API来在ZooKeeper中保存消费过的offset的。这是消费Kafka数据的传统方式。这种方式配合着WAL机制可以保证数据零丢失的高可靠性,但是却无法保证数据被处理一次且仅一次,可能会处理两次。因为Spark和ZooKeeper之间可能是不同步的。 基于direct的方式,使用kafka的简单api,Spark Streaming自己就负责追踪消费的offset,并保存在checkpoint中。Spark自己一定是同步的,因此可以保证数据是消费一次且仅消费一次。 **在实际生产环境中大都用Direct方式** ### 2.2 简述SparkStreaming窗口函数的原理(重点) 窗口函数就是在原来定义的SparkStreaming计算批次大小的基础上再次进行封装,每次计算多个批次的数据,同时还需要传递一个滑动步长的参数,用来设置当次计算任务完成之后下一次从什么地方开始计算。 图中time1就是SparkStreaming计算批次大小,虚线框以及实线大框就是窗口的大小,必须为批次的整数倍。虚线框到大实线框的距离(相隔多少批次),就是滑动步长。 # 三、SparkSQL # 关注我的公众号【宝哥大数据】,更多干货 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2021062022354047.png)

优秀的个人博客,低调大师

收藏,吊打面试官的kafka知识!

1 什么是kafka Kafka是分布式发布-订阅消息系统,它最初是由LinkedIn公司开发的,之后成为Apache项目的一部分,Kafka是一个分布式,可划分的,冗余备份的持久性的日志服务,它主要用于处理流式数据。 2 为什么要使用 kafka,为什么要使用消息队列 缓冲和削峰:上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka在中间可以起到一个缓冲的作用,把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。 解耦和扩展性:项目开始的时候,并不能确定具体需求。消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守约定,针对数据编程即可获取扩展能力。 冗余:可以采用一对多的方式,一个生产者发布消息,可以被多个订阅topic的服务消费到,供多个毫无关联的业务使用。 健壮性:消息队列可以堆积请求,所以消费端业务即使短时间死掉,也不会影响主要业务的正常进行。 异步通信:很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。 3.Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么 ISR:In-Sync Replicas 副本同步队列 AR:Assigned Replicas 所有副本 ISR是由leader维护,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度, 当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。 4.kafka中的broker 是干什么的 broker 是消息的代理,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉取指定Topic的消息,然后进行业务处理,broker在中间起到一个代理保存消息的中转站。 5.kafka中的 zookeeper 起到什么作用,可以不用zookeeper么 zookeeper 是一个分布式的协调组件,早期版本的kafka用zk做meta信息存储,consumer的消费状态,group的管理以及 offset的值。考虑到zk本身的一些因素以及整个架构较大概率存在单点问题,新版本中逐渐弱化了zookeeper的作用。新的consumer使用了kafka内部的group coordination协议,也减少了对zookeeper的依赖, 但是broker依然依赖于ZK,zookeeper 在kafka中还用来选举controller 和 检测broker是否存活等等。 6.kafka follower如何与leader同步数据 Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。完全同步复制要求All Alive Follower都复制完,这条消息才会被认为commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,Follower异步的从Leader复制数据,数据只要被Leader写入log就被认为已经commit,这种情况下,如果leader挂掉,会丢失数据,kafka使用ISR的方式很好的均衡了确保数据不丢失以及吞吐率。Follower可以批量的从Leader复制数据,而且Leader充分利用磁盘顺序读以及send file(zero copy)机制,这样极大的提高复制性能,内部批量写磁盘,大幅减少了Follower与Leader的消息量差。 7.什么情况下一个 broker 会从 isr中踢出去 leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护 ,如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除 。 8.kafka 为什么那么快 Cache Filesystem Cache PageCache缓存 顺序写 由于现代的操作系统提供了预读和写技术,磁盘的顺序写大多数情况下比随机写内存还要快。 Zero-copy 零拷技术减少拷贝次数 Batching of Messages 批量量处理。合并小的请求,然后以流的方式进行交互,直顶网络上限。 Pull 拉模式 使用拉模式进行消息的获取消费,与消费端处理能力相符。 9.kafka producer如何优化打入速度 增加线程 提高 batch.size 增加更多 producer 实例 增加 partition 数 设置 acks=-1 时,如果延迟增大:可以增大 num.replica.fetchers(follower 同步数据的线程数)来调解; 跨数据中心的传输:增加 socket 缓冲区设置以及 OS tcp 缓冲区设置。 10.kafka producer 打数据,ack 为 0, 1, -1 的时候代表啥, 设置 -1 的时候,什么情况下,leader 会认为一条消息 commit了 1(默认) 数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了。在这种情况下,如果leader宕机了,则会丢失数据。 0 生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据可靠性确是最低的。 -1 producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。当ISR中所有Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请求中的消息都commit了。 11.kafka unclean 配置代表啥,会对 spark streaming 消费有什么影响 unclean.leader.election.enable为true的话,意味着非ISR集合的broker 也可以参与选举,这样有可能就会丢数据,spark streaming在消费过程中拿到的 end offset 会突然变小,导致 spark streaming job挂掉。如果unclean.leader.election.enable参数设置为true,就有可能发生数据丢失和数据不一致的情况,Kafka的可靠性就会降低;而如果unclean.leader.election.enable参数设置为false,Kafka的可用性就会降低。 12.如果leader crash时,ISR为空怎么办 kafka在Broker端提供了一个配置参数:unclean.leader.election,这个参数有两个值:true(默认):允许不同步副本成为leader,由于不同步副本的消息较为滞后,此时成为leader,可能会出现消息不一致的情况。false:不允许不同步副本成为leader,此时如果发生ISR列表为空,会一直等待旧leader恢复,降低了可用性。 13.kafka的message格式是什么样的 一个Kafka的Message由一个固定长度的header和一个变长的消息体body组成 header部分由一个字节的magic(文件格式)和四个字节的CRC32(用于判断body消息体是否正常)构成。 当magic的值为1的时候,会在magic和crc32之间多一个字节的数据:attributes(保存一些相关属性,比如是否压缩、压缩格式等等);如果magic的值为0,那么不存在attributes属性。body是由N个字节构成的一个消息体,包含了具体的key/value消息 14.kafka中consumer group 是什么概念 同样是逻辑上的概念,是Kafka实现单播和广播两种消息模型的手段。同一个topic的数据,会广播给不同的group;同一个group中的worker,只有一个worker能拿到这个数据。换句话说,对于同一个topic,每个group都可以拿到同样的所有数据,但是数据进入group后只能被其中的一个worker消费。group内的worker可以使用多线程或多进程来实现,也可以将进程分散在多台机器上,worker的数量通常不超过partition的数量,且二者最好保持整数倍关系,因为Kafka在设计时假定了一个partition只能被一个worker消费(同一group内)。 15.Kafka中的消息是否会丢失和重复消费? 要确定Kafka的消息是否丢失或重复,从两个方面分析入手:消息发送和消息消费。 1、消息发送 Kafka消息发送有两种方式:同步(sync)和异步(async),默认是同步方式,可通过producer.type属性进行配置。Kafka通过配置request.required.acks属性来确认消息的生产: 0---表示不进行消息接收是否成功的确认;1---表示当Leader接收成功时确认;-1---表示Leader和Follower都接收成功时确认; 综上所述,有6种消息生产的情况,下面分情况来分析消息丢失的场景: (1)acks=0,不和Kafka集群进行消息接收确认,则当网络异常、缓冲区满了等情况时,消息可能丢失; (2)acks=1、同步模式下,只有Leader确认接收成功后但挂掉了,副本没有同步,数据可能丢失; 2、消息消费 Kafka消息消费有两个consumer接口,Low-level API和High-level API: Low-level API:消费者自己维护offset等值,可以实现对Kafka的完全控制; High-level API:封装了对parition和offset的管理,使用简单; 如果使用高级接口High-level API,可能存在一个问题就是当消息消费者从集群中把消息取出来、并提交了新的消息offset值后,还没来得及消费就挂掉了,那么下次再消费时之前没消费成功的消息就“诡异”的消失了; 解决办法: 针对消息丢失:同步模式下,确认机制设置为-1,即让消息写入Leader和Follower之后再确认消息发送成功;异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态; 针对消息重复:将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可。 16.为什么Kafka不支持读写分离? 在 Kafka 中,生产者写入消息、消费者读取消息的操作都是与 leader 副本进行交互的,从 而实现的是一种主写主读的生产消费模型。 Kafka 并不支持主写从读,因为主写从读有 2 个很明 显的缺点: (1)数据一致性问题。数据从主节点转到从节点必然会有一个延时的时间窗口,这个时间 窗口会导致主从节点之间的数据不一致。某一时刻,在主节点和从节点中 A 数据的值都为 X, 之后将主节点中 A 的值修改为 Y,那么在这个变更通知到从节点之前,应用读取从节点中的 A 数据的值并不为最新的 Y,由此便产生了数据不一致的问题。 (2)延时问题。类似 Redis 这种组件,数据从写入主节点到同步至从节点中的过程需要经 历网络→主节点内存→网络→从节点内存这几个阶段,整个过程会耗费一定的时间。而在 Kafka 中,主从同步会比 Redis 更加耗时,它需要经历网络→主节点内存→主节点磁盘→网络→从节 点内存→从节点磁盘这几个阶段。对延时敏感的应用而言,主写从读的功能并不太适用。 17.Kafka中是怎么体现消息顺序性的? kafka每个partition中的消息在写入时都是有序的,消费时,每个partition只能被每一个group中的一个消费者消费,保证了消费时也是有序的。整个topic不保证有序。如果为了保证topic整个有序,那么将partition调整为1. 18.消费者提交消费位移时提交的是当前消费到的最新消息的offset还是offset+1? offset+1 19.kafka如何实现延迟队列? Kafka并没有使用JDK自带的Timer或者DelayQueue来实现延迟的功能,而是基于时间轮自定义了一个用于实现延迟功能的定时器(SystemTimer)。JDK的Timer和DelayQueue插入和删除操作的平均时间复杂度为O(nlog(n)),并不能满足Kafka的高性能要求,而基于时间轮可以将插入和删除操作的时间复杂度都降为O(1)。时间轮的应用并非Kafka独有,其应用场景还有很多,在Netty、Akka、Quartz、Zookeeper等组件中都存在时间轮的踪影。 底层使用数组实现,数组中的每个元素可以存放一个TimerTaskList对象。TimerTaskList是一个环形双向链表,在其中的链表项TimerTaskEntry中封装了真正的定时任务TimerTask. Kafka中到底是怎么推进时间的呢?Kafka中的定时器借助了JDK中的DelayQueue来协助推进时间轮。具体做法是对于每个使用到的TimerTaskList都会加入到DelayQueue中。Kafka中的TimingWheel专门用来执行插入和删除TimerTaskEntry的操作,而DelayQueue专门负责时间推进的任务。再试想一下,DelayQueue中的第一个超时任务列表的expiration为200ms,第二个超时任务为840ms,这里获取DelayQueue的队头只需要O(1)的时间复杂度。如果采用每秒定时推进,那么获取到第一个超时的任务列表时执行的200次推进中有199次属于“空推进”,而获取到第二个超时任务时有需要执行639次“空推进”,这样会无故空耗机器的性能资源,这里采用DelayQueue来辅助以少量空间换时间,从而做到了“精准推进”。Kafka中的定时器真可谓是“知人善用”,用TimingWheel做最擅长的任务添加和删除操作,而用DelayQueue做最擅长的时间推进工作,相辅相成。 历史好文推荐 Kafka实战宝典:Kafka的控制器controller详解 Kafka实战宝典:一文带解决Kafka常见故障处理 Kafka实战宝典:监控利器kafka-eagle kafka实战宝典:手动修改消费偏移量的两种方式 Kafka实战宝典:如何跨机房传输数据 本文分享自微信公众号 - 数据社(DataClub)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。