开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 21 天,点击查看活动详情
START
- 解释是什么的问题。
- 解释这个技术的应用点、应用场景在哪里。
- 整理一下这个问题的优缺点是什么。
前言
见上上篇:review1[1-11]
见上一篇:review2[12-20]
21、正向代理和反向代理的区别?
- (解释是什么的问题。)
- 正向代理:就是客户端代理,帮助客户端方位无法访问的服务端资源
- 反向代理:是服务器的代理,帮助服务器做负载均衡,安全防护等。比如nginx
- 区别:
- 正向代理:用来解决访问限制问题,服务端不知道真正的客户端是谁
- 反向代理:提供负载均衡、安全防护等作用。客户端不知道真正的服务端是谁
22、域名解析过程是怎样的?
以www.baidu.com为例,主要通过五个步骤进行域名解析
- 浏览器访问 www.baidu.com,询问本地 DNS 服务器是否缓存了该网址解析后的 IP 地址。
- 如果本地 DNS 服务器没有缓存的话,就去 root-servers.net 根服务器查询该网址对应的 IP 地 址。
- 根服务器返回顶级域名服务器的网址 gtld-servers.net,然后本地 DNS 服务器去顶级域名服务 器查询该网址对应的 IP 地址。
- 顶级域名服务器返回 www.baidu.com 主区域服务器的地址,然后本地 DNS 服务器去 www.ba idu.com 主区域服务器查询此域名对应的 IP 地址。
- 本地 DNS 服务器拿到 www.baidu.com 解析后的 IP 地址后,缓存起来以便备查,然后把解析 后的 IP 地址返回给浏览器。
23、TCP协议三次握手、四次挥手的过程,为什么挥手要4次?
第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
如果TCP传输过程中ACK丢失会怎样?
由于server端发送syn_ack报文后进入SYN_RCVD状态,就会启动tcp重传计时器,超时时就会重新发送syn_ack报文,总共发送 net.ipv4.tcp_synack_retries次;客户端在没有收到重传的syn_ack之前,发送数据给server端,由于server端的状态为SYN_RCVD状态,而不是ESTABLISHED状态,就会发送RST报文给client端,客户端就能接收到服务器侧未收到ack报文。
- 第一次挥手:客户端发出释放FIN=1,自己序列号seq=u,进入FIN-WAIT-1状态
- 第二次挥手:服务器收到客户端的后,发出ACK=1确认标志和客户端的确认号ack=u+1,自己的序列号seq=v,进入CLOSE-WAIT状态
- 第三次挥手:客户端收到服务器确认结果后,进入FIN-WAIT-2状态。此时服务器发送释放FIN=1信号,确认标志ACK=1,确认序号ack=u+1,自己序号seq=w,服务器进入LAST-ACK(最后确认态)
- 第四次挥手:客户端收到回复后,发送确认ACK=1,ack=w+1,自己的seq=u+1,客户端进入TIME-WAIT(时间等待)。客户端经过2个最长报文段寿命后,客户端CLOSE;服务器收到确认后,立刻进入CLOSE状态。
为什么三次握手和四次挥手?
- 三次握手时,服务器同时把ACK和SYN放在一起发送到了客户端那里
- 四次挥手时,当收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方 ACK 和 FIN 一般都会分开发送。
(三次)
建立连接---- C发包,S接收。S:C有消息
S发包,C接收。C:S在了
C发包,S接收
(四次)
断开连接 ---- C发包,S接收。S:C请求结束
S发包,C接收。C:S已收到,等待S关闭
S发包,C接收。C:S可以关闭了
C发包,S接收。S:已关闭
24、nextTick, setTimeout/setInterval 以及 setImmediate 三者有什么区别?
- (解释是什么的问题)
- setTimeout:
- setInterval:不断重复
- setImmediate: 一旦poll阶段完成,就立即执行的脚本代码。**即时计时器立即执行工作,它是在事件轮询之后执行,为了防止轮询阻塞,每次只会调用一个。**nextTick()的回调函数执行的优先级要高于setImmediate();
- nextTick: 它和setImmediate()执行的顺序不一样,它是在事件轮询之前执行,为了防止I/O饥饿,所以有一个默认process.maxTickDepth=1000来限制事件队列的每次循环可执行的nextTick()事件的数目。
- 总结:
nextTick()
的回调函数执行的优先级要高于setImmediate()
;
- 区别
- process.nextTick(),效率最高,消费资源小,但会阻塞CPU的后续调用;
- setTimeout(),精确度不高,可能有延迟执行的情况发生,且因为动用了红黑树,所以消耗资源大;
- setImmediate(),消耗的资源小,也不会造成阻塞,但效率也是最低的。
25、说一下你在项目的安全性做了哪些工作?
- (解释是什么的问题。)
- 在前端做安全性的工作要先知道恶意攻击人员从哪几个方面入手。前端主要被攻击的方式有下面几个
XSS攻击【代码注入问题】
- xss分为存储型(持久型):存储在服务器 反射型(非持久型):改变url
- xss就是攻击者把一些
可执行的代码
嵌入到代码中,比如<script>...</script>
,这里面可以写一些函数获取到我们的cookie,sessionid等。- 如下图,用户评论的时候可以这样写
解决
-
过滤转义
- 把输入评论时候的内容关于符号类的东西。比如:‘<’,'>'...转义
【对于URL地址的转义可以使用
encodeURI
,当你需要编码URL中的参数的时候,那么encodeURIComponent
是最好方法。】const signs = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' } const signReg = /[&<>"']/g function escape(string) { return (string && reUnescapedHtml.test(string)) ? string.replace(reUnescapedHtml, (chr) =>htmlEscapes[chr]) : string }
2. 使用xss.js插件
-
Cookie设置HttpOnly
CSRF攻击【http问题】
- 跨站点请求伪造(Cross-Site Request Forgeries),也被称为 one-click attack 或者 session riding。冒充用户发起请求(在用户不知情的情况下), 完成一些违背用户意愿的事情(如修改用户信息,删除评论等)。
- 特点
- 通常在第三方网站上(点击广告链接)
- 攻击者不能获取cookie,只能使用
- 解决(防御)
- 验证码,强制要求人工交互再发请求
- 使用post请求,避免攻击者在url上拿到参数
- 服务端校验referer
- 随机token 每次页面访问生成一个token 请求时带上
- 如何解决:
- 请求参数加密,使用sm3(国家标准)
- 验证码登录-图形验证码、手机验证码
- 使用https
- 输入区域转义代码内容
26、当一张表数据量比较多的时候,为了提高查询速度,你们一般会使用哪些方式做优化?
- 先沟通
- 分页
- 触底滚动加载
- 虚拟列表
27、webSocket与传统的http相比有什么优势?
- (解释是什么的问题)
- webSocket: 是Web浏览器和服务器之间的一种全双工通信协议。一旦Web客户端与服务端建立起连接,之后的全部数据通信都通过这个连接进行。
通信过程中,可互相发送JSON、XML、HTML或图片等任意格式的数据。
- webSocket: 是Web浏览器和服务器之间的一种全双工通信协议。一旦Web客户端与服务端建立起连接,之后的全部数据通信都通过这个连接进行。
- 与http比较
- 相同点
- 都是基于TCP的应用层协议;
- 都使用Request/Response模型进行连接的建立;
- 在连接的建立过程中对错误的处理方式相同,在这个阶段WS可能返回和HTTP相同的返回码;
- 都可以在网络中传输数据。
- 不同点
- WS使用HTTP来建立连接,但是定义了一系列新的header域,这些域在HTTP中并不会使用;
- WS是HTML5中的协议,支持持久连接;而Http协议不支持持久连接。
- WS的连接不能通过中间人来转发,它必须是一个直接连接;
- WS连接建立之后,通信双方都可以在任何时刻向另一方发送数据;
- WS连接建立之后,数据的传输使用帧来传递,不再需要Request消息;
- WS的数据帧有序。
- 相同点
- WS 优点
- websocket则允许我们在一条ws连接上同时并发多个请求
- http协议的头部太大,websocket则因为复用长连接而没有这一问题
- websocket支持服务器推送消息,这带来了及时消息通知的更好体验,也是ajax请求无法达到的。
- 解决了传统轮询,长轮询带的问题——服务器过载,延迟等
- WS 缺点
- 服务器长期维护长连接需要一定的成本
- 各个浏览器支持程度不一
- websocket 是长连接,受网络限制比较大,需要处理好重连,比如用户进电梯或电信用户打个电话网断了,这时候就需要重连
- WS 应用
- 实时通讯领域较多,聊天,弹幕,游戏,
28、如何用同一套代码部署到服务器中,怎么区分当前本地开发环境还是线上环境?是测试环境还是生产环境呢,怎么去区分?
node中提供的变量:process.env.NODE_ENV 根据域名判断
29、待支付的订单,到期后主动取消这个功能你会怎么设计去做?
- 定时器,倒计时时间为0后,改变订单状态
- 优点:实现容易,成本低
- 缺点:时间可能不准确,数据库压力大
- 后端监听
30、如果要做音视频的安全性,你能想到哪些方案?
31、多台服务器部署定时任务怎么保证一个任务只会做一遍呢?
32、你觉得程序员除了提升技术能力之外,其他什么能力你比较看重?
- 沟通能力
- 学习能力
- 希望能提高一下自己的管理协调能力
33、用过koa吗?简要阐述一下koa的洋葱模型。
Koa基于Node.js平台的下一代web开发框架
- 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理
- Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。
在洋葱模型中,每一层相当于一个中间件,用来处理特定的功能。
Koa
的洋葱模型指的是以 next()
函数为分割点,先由外到内执行 Request
的逻辑,再由内到外执行 Response
的逻辑。通过洋葱模型,将多个中间件之间通信等变得更加可行和简单。其实现的原理并不是很复杂,主要是 compose
方法。
34、用过promise吗?它的使用是为了解决一个什么问题?promise底层是怎么设计的?
Promise 对象是一个可用于存储异步操作结果状态和数据的容器。
-
promise 作用
- promise主要要解决一个if else 回调地狱的问题。
- promise可以直接多个并发请求,获取并发请求中的数据,可以解决异步的问题
- 将业务逻辑与数据处理分隔开使代码更优雅,方便阅读,更有利于代码维护
-
promise 用法
- 有三种状态:pending、fulfilled、rejected,只有异步操作的结果,可以决定当前是哪一种状态,任何操作都无法改变这个状态
- 状态一旦改变就不会改变
- 构造函数promise必须接收一个函数作为参数,函数包括resolve和reject
- then、catch支持链式调用
-
promise 方法
- Promise.all()
-
Promise.race()
-
Promise.resolve()
-
Promise.reject()
-
Promise.prototype.catch()
-
Promise.prototype.finally()
-
Promise.prototype.then()
-
promise 底层怎么设计的
/**
* promise手写思路
* @type {string}
*
* 以容器概念作为切入点,实现Promise对象的基本结构
* 分析Promise容器和异步操作的关系,实现Promise的构造方法constructor
* 理清Promise容器中数据的写入方式,实现Promise的resolve和reject方法
* 理清Promise容器中数据的读取方式,实现Promise的then方法
* 给then方法加个需求,支持链式调用,方便处理异步操作流
*/
const REJECTED = 'REJECTED';
const FULFILLED = 'FULFILLED';
const PENDING = 'PENDING';
class newPromise {
constructor(executor) {
/*先定义容器中需要用到的变量*/
this.state = PENDING // promise的状态
this.value = undefined //resolve下的返回值
this.reason = undefined //reject下的返回值
this.onResolvedTodoList = [] //resolve的存储数组
this.onRejectedTodoList = [] //reject的存储数组
const resolve = (value) => {}
const reject = (reason) => {}
try {
//执行入参函数
executor(resolve, reject);
} catch(err) {
reject(err);
}
}
static all = () => {}
static race = () => {}
static finally = () => { }
static catch = () => {}
then = (onResolved, onRejected) => {
return new Promise((resolve, reject) => {
// 代码
})
}
}