前端全栈

1. Nginx使用proxy_pass反向代理时,session丢失的问题
      如果只是host,端口转换,则session不会丢失。例如:
      location /testwx {
             proxy_pass   http://127.0.0.1:8080/testwx;
      }
如果路径也变化了,则需要设置cookie的路径转换,nginx.conf的配置如下
location /testwx {
             proxy_pass   http://127.0.0.1:8080/wx;
             proxy_cookie_path  /wx /testwx;
      }

2. rpc之gRPC
gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。
基于HTTP/2,提供了连接多路复用、双向流、服务器推送、请求优先级、首部压缩等机制
可以节省带宽、降低TCP链接次数、节省CPU,帮助移动设备延长电池寿命等。
gRPC 的协议设计上使用了HTTP2 现有的语义,请求和响应的数据使用HTTP Body 发送,其他的控制信息则用Header 表示。
IDL使用ProtoBuf,gRPC使用ProtoBuf来定义服务,ProtoBuf是由Google开发的一种数据序列化协议(类似于XML、JSON、hessian)。
HTTP/2 传输的数据是二进制的。相比 HTTP/1.1 的纯文本数据,二进制数据一个显而易见的好处是:更小的传输体积。这就意味着更低的负载。

3. 其他rpc
thrift,dubbo,motan等,底层协议归纳为有:motan协议,tcp长连接,Dubbo 协议、 Rmi 协议、 Hessian 协议、 
HTTP 协议、WebService 协议、Dubbo Thrift 协议、Memcached 协议,tpc/http/frame,http2,另外在分布式服务治理、消息序列化、负载均衡、
容错、注册中心等方面,各个RPC框架配合相应的组件服务,各有特点。

4. 手写jsonp
function jsonp(url, data = {}, callback = 'callback') {
  data.callback = callback
  let params = []
  for (let key in data) {
     params.push(key + '=' + data[key])
  }
  let script = document.createElement('script')
  script.src = url + '?' + params.join('&')
  document.body.appendChild(script)
  return new Promise((resolve, reject) => {
    window[callback] = (data) => {
      try {
        resolve(data)
      } catch (e) {
        reject(e)
      } finally {
        script.parentNode.removeChild(script)
        console.log(script)
      }
    }
  })
}

5. Vue虚拟DOM(VDOM)和diff算法
虚拟DOM是利用 了js的对象的Object的对象模型来模拟真实DOM, 那么它的结构是一个树形结构。
diff算法是同级比较的,diff算法的比较,四种情况:
1)此节点是否被移除 -> 添加新的节点
2)属性是否被改变 -> 旧属性改为新属性
3)文本内容被改变-> 旧内容改为新内容
4)节点要被整个替换 -> 结构完全不相同 移除整个替换

6. 关于vue的props
父子组件传值时,父组件传递的参数,数组和对象,子组件接受之后可以直接进行修改,并且会传递给父组件相应的值也会修改。
如果传递的值是字符串,直接修改会报错。不推荐子组件直接修改父组件中的参数,避免这个参数多个子组件引用,无法找到造成数据不正常的原因。
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。额外的,每次父级组件发生更新时,
子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

7. MVVM 之 Vue.js
MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,其核心是提供对View 和 ViewModel 的双向数据绑定,
这使得ViewModel 的状态改变可以自动传递给 View,即所谓的数据双向绑定。Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 Javascript 库,
专注于View 层。它的核心是 MVVM 中的 VM,也就是 ViewModel。 ViewModel负责连接 View 和 Model,保证视图和数据的一致性。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 
因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
Vue.js 是采用 Object.defineProperty 的 getter 和 setter,并结合观察者模式来实现数据绑定的。
当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

8. Web安全之CSRF攻击
CSRF攻击是源于Web的隐式身份验证机制!Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。
CSRF攻击的一般是由服务端解决:
1)尽量使用POST,限制GET
2)浏览器Cookie策略(拦截第三方本地Cookie的发送),第三方cookie的使用场景通常是iframe,例如www.a.com潜入了一个www.ad.com的广告iframe,
   那么www.ad.com设置的cookie不属于www.a.com,被称作第三方cookie。
3)加验证码(出于用户体验考虑,网站不能给所有的操作都加上验证码)
4)Referer Check(服务器并不是什么时候都能取到Referer)
5)Anti CSRF Token,现在业界对CSRF的防御,一致的做法是使用一个Token(Anti CSRF Token):
    a. 用户访问某个表单页面。
    b. 服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。
    c. 在页面表单附带上Token参数。(此处是重点,token在表单中提交参与验证,而不是cookies中
    d. 用户提交请求后, 服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致,一致为合法请求,不是则非法请求。
    这个Token的值必须是随机的,不可预测的。由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。
    另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。
注意:
CSRF的Token仅仅用于对抗CSRF攻击。当网站同时存在XSS漏洞时候,那这个方案也是空谈。所以XSS带来的问题,应该使用XSS的防御方案予以解决。

9. 同源策略 
由NetScape提出的一个著名的安全策略。所谓的同源,指的是协议,域名,端口相同。浏览器处于安全方面的考虑,只允许本域名下的接口交互,
不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。
补一点:为什么form表单提交没有跨域问题,但ajax提交有跨域问题
因为原页面用 form 提交到另一个域名之后,原页面的脚本无法获取新页面中的内容。所以浏览器认为这是安全的。
而 AJAX 是可以读取响应内容的,因此浏览器不能允许你这样做。如果你细心的话你会发现,其实请求已经发送出去了,你只是拿不到响应而已。
所以浏览器这个策略的本质是,一个域名的 JS ,在未经允许的情况下,不得读取另一个域名的内容。但浏览器并不阻止你向另一个域名发送请求。

10. vue中的slot与slot-scope
插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定,父组件把的slot部分用UI内容填充。其中,作用域插槽(slot-scope)要求在slot上面绑定数据,这个数据来自子组件,但是传到父组件,交给父组件渲染

11. 消息队列
消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,由消息系统来确保消息的可靠传递
RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMq。甚至现在部分NoSQL也可做消息队列,如 Redis
使用场景:异步处理、应用解耦、流量削峰。
RabbitMQ默认15672端口,登陆管理界面,打开浏览器输入:http://localhost:15672
使用消息队列需要考虑下面几个问题:
如何保证消息的幂等性(消息重复),对于消息中间件的幂等性问题,一般通用的处理方案是给消息一个唯一的ID,每次做业务处理之前判断是否消费过。如果消费过,那么直接抛弃该消息,否则进行业务处理。
如何保证消息的顺序性(消息有序),一般通用的处理方案是保证局部的消息有序。对于订单消息、支付消息的例子,我们一般会将订单消息和支付消息里的用户ID作为key,将其分配到同一个 partition 中,这样它们就是有序的。
如何保证消息的可靠性(消息丢失),对于 Kafka 来说,可能发生消息丢失的几个节点分别是:生产者丢失消息、broker丢失消息、消费者丢失消息。
那么如何解决数据丢失的问题呢?目前来说有两种方式,一种是在消息中间件层面解决,另一方面是在业务层面通过消息补偿解决。
消息中间件层面。选择不会丢失的消息中间件,例如 RocketMQ 对于消息的确认机制比较严格,可以保证消息不丢失,而 Kafka 则无法保证消息不丢失。
业务层面(消息补偿)。意思是允许中间件出现消息丢失,但是通过业务层面来做消息补偿。不同的业务场景,消息补偿的形式不一样,需要具体情况具体分析。
那么,为什么RocketMQ可以保证消息不丢失
1)从Producer分析:如何确保消息正确的发送到了Broker?
状态超时或者失败,则会触发默认的2次重试。如果消息发送Ack失败的话,此消息会存储在CommitLog当中,但是对ConsumerQueue是不可见的。
可以在日志中查看到这条异常的消息,严格意义上来讲,也并没有完全丢失。

2)从Broker分析:如果确保接收到的消息不会丢失?
消息支持持久化到Commitlog里面,即使宕机后重启,未消费的消息也是可以加载出来的,
Broker自身支持同步刷盘、异步刷盘的策略,可以保证接收到的消息一定存储在本地的内存中,
Broker集群支持 1主N从的策略,支持同步复制和异步复制的方式,同步复制可以保证即使Master 磁盘崩溃,消息仍然不会丢失。

3)从Consumer分析:如何确保拉取到的消息被成功消费?
消费者可以根据自身的策略批量Pull消息
Consumer自身维护一个持久化的offset,标记已经成功消费或者已经成功发回到broker的消息下标,
如果Consumer消费失败,那么它会把这个消息发回给Broker,发回成功后,再更新自己的offset,
如果Consumer消费失败,发回给broker时,broker挂掉了,那么Consumer会定时重试这个操作,
如果Consumer和broker一起挂了,消息也不会丢失,因为consumer 里面的offset是定时持久化的,重启之后,继续拉取offset之前的消息到本地。

12. Git分支误删了怎么办?
git本地分支误删,同时把远程分支也误删了,怎么办?很简单!
step1:查找误删的分支上次提交的commit id
使用命令  git log -g 
step2:用上一步查找到的commit id 恢复分支即可
使用命令 git branch 新的分支名 + commit id
PS: git brache xxxx   3eac14d05bc1264cda54a7c21f04c3892f32406a
step3: 切换到xxxx分支即可

13. base64和png文件间的互转
1)用nodejs,将base64转化成png文件
const fs = require('fs');
    const path = 'xxx/'+ Date.now() +'.png';
    const base64 = data.replace(/^data:image\/\w+;base64,/, "");
    const dataBuffer = new Buffer(base64, 'base64');
    fs.writeFile(path, dataBuffer, function(err){
        if(err){
            console.log(err);
        }else{
            console.log('写入成功!');
        }
    })
2)实现一下将png文件,转化成base64
const fs = require("fs");
const util = require("util");
const imageData = await util.promisify(fs.readFileSync(fileUrl));
const imageBase64 = imageData.toString("base64");
const imagePrefix = "data:image/png;base64,";
console.log(imagePrefix + imageBase64);

14. 运维
1)对于使用 yarn 安装的插件,如果不被识别,则把以下路径添加到系统的环境变量Path中,然后重开控制台即可:
C:\Users\用户名\AppData\Local\Yarn\Data\global\node_modules\.bin
C:\Users\用户名\AppData\Local\Yarn\bin
如果用npm安装yarn之后,yarn命令本身不被识别,则添加
C:\Users\用户名\AppData\Roaming\npm\node_modules\yarn\bin 到系统的环境变量Path中。
以上路径,可通过 npm bin -g、yarn global dir、yarn global bin 等获得。

2)Error: Cannot find module '.../lib/node_modules/pm2/lib/ProcessContainerFork.js'
rm -rf ~/.pm2

15. 在nodejs开发的时候,处理过什么windows和mac的平台兼容性问题
    比如:
    兼容环境变量设置,用cross-env,因为export和set有系统差异性
    windows不支持 & ,并行执行npm-script用npm-run-all或者concurrently
    异步同步化: util.promisify + async/await
    不要直接用官网的fs.readFileSync,用fs-extra去代替
    拼接路径要用path.join,Unix系是/,Windows是\

16. 如果你用nodejs实现的爬虫服务器的IP被指定网站封了,如何解封?
    可以做几层ip池,当抓取失败后,通过自动随机切换ip代理池去绕过封锁

17. 浏览器怎么处理cookie
    服务器向浏览器设置cookie (http响应的Set-Cookie字段)
    浏览器向服务器提交cookie(http请求的Cookie字段)
    一般,通过http请求的信息获取Set-Cookie的信息,然后再次提交时写入Cookie信息就可实现。
    
18. Webpack DLL 用法
    在用 Webpack 打包的时候,对于一些不经常更新的第三方库,比如 react,lodash,我们希望能和自己的代码分离开,
    Webpack 社区有两种方案:CommonsChunkPlugin、DLLPlugin,对于 CommonsChunkPlugin,webpack 每次打包实际还是需要去处理这些第三方库,
    只是打包完之后,能把第三方库和我们自己的代码分开。而 DLLPlugin 则是能把第三方代码完全分离开,即每次只打包项目自身的代码。
    要使用 DLLPlugin,需要额外新建一个配置文件。所以对于用这种方式打包的项目,一般会有下面两个配置文件:
    webpack.config.js,webpack.dll.config.js

19. vue中的this
    


    

20. 内存回收(js,node.js,go,java)
1)java
    引用计数算法,目前已废弃,原因是难解决两个对象间循环引用的问题。
    可达性分析算法(目前java使用),这个算法的基本思路就是通过一系列的名为“GC Roots”的对象作为起始点,
    从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,
    即使很多个这样的对象之前是相互引用,也会被GC。在Java语言中,可作为GC Roots的对象包括下面几种:
    A) 虚拟机栈(栈帧中的本地变量表)中引用的对象。
    B) 方法区中类静态属性引用的对象。
    C) 方法区中常量引用的对象。
    D) 本地方法栈中JNI(Native方法)引用的对象。
    标记/清除算法,效率问题,标记和清除两个过程的效率都不高,会产生大量不连续的内存碎片,
    导致需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
    复制算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,
    就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
    致命的缺点是堆的可利用空间只有一半。
    标记/整理算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,
    而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
    “标记-整理”回收策略较大的问题在于执行效率,因为大都需要多次扫描堆,容易造成gc卡顿时间较长
    分代收集算法,当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,这种算法并没有什么新的思想,
    只是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,
    这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,
    那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
    而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。

2)js
    现在各大浏览器通常用采用的垃圾回收有两种方法:标记清除、引用计数。

3)node.js
    Node程序运行中,常驻内存由以下部分组成:
    代码区(Code Segment):存放即将执行的代码片段
    栈(Stack):存放局部变量
    堆(Heap):存放对象、闭包上下文
    堆外内存:不通过V8分配,也不受V8管理。Buffer对象的数据就存放于此。
    除堆外内存,其余部分均由V8管理。
    V8将堆中的对象分为两类:
    新生代:年轻的新对象,未经历垃圾回收或仅经历过一次
    老年代:存活时间长的老对象,经历过一次或更多次垃圾回收的对象
    默认情况下,V8为老年代分配的空间,大概是新生代的40多倍。
    新对象都会被分配到新生代中,当新生代空间不足以分配新对象时,将触发新生代的垃圾回收。
    新生代的垃圾回收:Scavenge算法进行垃圾回收,这是一种采用复制的方式实现内存回收的算法.
    老年代的垃圾回收:老年代垃圾回收主要采用标记清除(Mark-Sweep)和标记整理(Mark-Compact).
    可见,node.js的垃圾回收java十分类似。
    增量标记:早期V8在垃圾回收阶段,采用全停顿(stop the world),也就是垃圾回收时程序运行会被暂停。
    这在JavaScript还仅被用于浏览器端开发时,并没有什么明显的缺点,前端开发使用的内存少,
    大多数时候仅触发新生代垃圾回收,速度快,卡顿几乎感觉不到。但是对于Node程序,使用内存更多,
    在老年代垃圾回收时,全停顿很容易带来明显的程序迟滞,标记阶段很容易就会超过100ms,因此V8引入了增量标记,
    将标记阶段分为若干小步骤,每个步骤控制在5ms内,每运行一段时间标记动作,就让JavaScript程序执行一会儿,
    如此交替,明显地提高了程序流畅性,一定程度上避免了长时间卡顿。
    内存管理和优化:
    某些变量也许在某一时刻后就没有用处了,因此建议手动设置为null,断开引用链接,使得V8可以及时GC释放内存。
    前端开发中全局对象为window,Node中全局对象为global,如果global中有属性已经没有用处了,
    一定要设置为null,因为全局作用域只有等到程序停止运行,才会销毁。
    Node中,当一个模块被引入,这个模块就会被缓存在内存中,提高下次被引用的速度。
    也就是说,一般情况下,整个Node程序中对同一个模块的引用,都是同一个实例(instance),这个实例一直存活在内存中。
    所以,如果任意模块中有变量已经不再需要,最好手动设置为null,不然会白白占用内存,成为“活着的死对象”。

4)go
    各个版本的垃圾回收机制:
    v1.1 STW,Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。
    stop the world是gc的最大性能问题,对于gc而言,需要停止所有的内存变化,即停止所有的goroutine,等待gc结束之后才恢复。
    v1.3 Mark STW, Sweep 并行
    v1.5 三色标记法
    v1.8 hybrid write barrier(写屏障)

    go垃圾回收触发方式
    阈值:默认内存扩大一倍,启动gc
    定期:默认2min触发一次gc,src/runtime/proc.go:forcegcperiod
    手动:runtime.gc()

    GO的GC是并行GC, 也就是GC的大部分处理和普通的go代码是同时运行的, 这让GO的GC流程比较复杂。
    
    但是 golang 并没有分代收集,所以对于巨量的小对象还是很苦手的,会导致整个 mark 过程十分长,
    在某些极端情况下,甚至会导致 GC 线程占据 50% 以上的 CPU。因此,当程序由于高并发等原因造成大量小对象的gc问题时,
    最好可以使用 sync.Pool 等对象池技术,避免大量小对象加大 GC 压力。

你可能感兴趣的:(Web)