into-piece / Step-By-Step

每天一题向前端架构师前进
4 stars 1 forks source link

20200428 #22

Open into-piece opened 4 years ago

into-piece commented 4 years ago

URLSearchParams

var paramsString = "q=URLUtils.searchParams&topic=api"
var searchParams = new URLSearchParams(paramsString);

for (let p of searchParams) {
  console.log(p);
}

searchParams.has("topic") === true; // true
searchParams.get("topic") === "api"; // true
searchParams.getAll("topic"); // ["api"]
searchParams.get("foo") === null; // true
searchParams.append("topic", "webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=api&topic=webdev"
searchParams.set("topic", "More webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=More+webdev"
searchParams.delete("topic");
searchParams.toString(); // "q=URLUtils.searchParams"

手动实现

class URLSearchParams {
  constructor(obj) {
    this.params = new Map();
    if (Object.prototype.toString.call(obj) === "[object String]") {
      obj.split("&").forEach((item) => {
        const [key, value] = item.split("=");
        this.params.set(key, value);
        console.log(this.params);
      });
    }
    if (obj instanceof Object) {
      Object.keys(obj).forEach((item) => {
        this.params.set(item, obj[item]);
      });
    }
  }

  get(k) {
    return this.params.get(k);
  }

  set(k, v) {
    return this.params.set(k, v);
  }

  has(k) {
    return this.params.has(k);
  }

  append() {}

  toString() {
    // for (let [key, value] of this.params.entries()) {

    // }
    // [...map.entries()]
    // this.params.forEach(function(value, key, map) {
    //   str+= `${key}=${value]}`
    // });

    const arr = [...this.params];
    const str = arr.reduce((res, [key, value], index) => {
      res += `${key}=${value}${index === arr.length - 1 ? "" : "&"}`;
      return res;
    }, "");
    return str;
  }

  // 可遍历
  *[Symbol.iterator]() {
    yield* this.params;
  }
}

PWA

What are Progressive Web Apps?

node中的vm

nodejs有一个叫vm的模块,这是用来运行代码的模块。怎么说呢,我们知道浏览器只要你将代码放在script标签中,或script src远程引用它,代码就会执行,这些代码会执行一些我们没有定义但会预先传入的对象或方法,比如window, document, location, fetch等等。我们知道其实document, location, fetch这些东西都在window上,window是一个上下文。 在iframe中也有自己的window,各自独立。而在nodejs,怎么形成这个"window"呢,这就需要vm来提供。

我们看到nodejs通过require也能运行其他模块,这是因为它们用到vm中的方法。

Safeify node vm沙盒实现

const vm = require('vm');
const script = new vm.Script('m + n');
const sandbox = { m: 1, n: 2 };
const context = new vm.createContext(sandbox);
script.runInContext(context);

【nodejs】eventloop + 线程池

线程池

频繁创建和销毁线程对性能的影响显而易见,同时这样的设计并不能撑其瞬时峰值流量。 线程池就是进行线程的生命周期管控,达成=》线程复用

libuv threadPool 源码地址=》 源码地址

线程池利用死循环让线程无法结束,在等待任务期间处于阻塞状态,利用阻塞唤醒来让线程接收任务(本质上阻塞唤醒基于信号量),从而达到线程复用,结束当前任务后进入下一次循环,周而复始。

eventloop + 线程池 = 异步非阻塞

可爱的你发起了一个 IO 调用,从 《大前端进阶 Node.js》系列 异步非阻塞中讲过,一个 IO 调用要么是阻塞调用,要么是先非阻塞的发起 IO,再在需要看结果的时候阻塞的去获取,显然这两种模式都不是我们想要的。

我们要的是异步非阻塞,所以这个 IO 调用一定不是在主线程中执行,这个时候我们就能联想到上面的线程池。

主线程不能被阻塞,但线程池里面的线程可以,主线程只需要把 IO 调用交给线程池来执行,自己就可以愉快的玩耍,以此达到了我们的第一个目标:非阻塞。

在线程池 IO 处理结束后,会主动的把结束的请求放入 eventloop 的观察者(watcher)中,也就是我们的 queue 中,eventloop 处于不断循环的状态,当下一次循环 check 到 queue 里有请求的时候,就会取出来然后执行回调,这样我们想要的异步就达到了。

最终通过线程池和 eventloop 结合,呈现出的效果就是,当你发起一次 IO 调用,你无需阻塞的等待 IO 结束,也无需在想利用 IO 结果的时候不断的轮询,整个 IO 过程对主线程而言非阻塞,并且自动结束时执行回调,达到我们想要的异步非阻塞。

核心总结:Node 利用线程池来执行 IO 调用,避免阻塞主线程,执行结束后把结果和请求放入一个队列,利用事件循环来取出队列的请求,最后执行回调,达到了异步非阻塞的效果。

系统架构

前端层面

在抢单系统中,前端页面如若面临成千上万个需求,需要做的是限制用户频繁出发请求事件。对触发事件进行节流固然可以,但直接拿接口来发送请求,这个时候我们要防止恶意触发,需要做好防csrf攻击(跨站点请求伪造)。

  1. Referer验证
  2. 在请求地址中添加 token 并验证
  3. set cookie的samesite。
    Set-Cookie: key=value; SameSite=Strict

    Nginx层

    当我们做一个抢单系统的时候,面对成千上万个请求首先考虑的,便是怎么分担服务器的压力,这时候负载均衡

referer-policy

Referer这个http header的参数应用得当的话,是可以提高安全性的,比如,可以这个参数其实就告诉了链接的请求来源于哪个网站,所以可以根据这个特性,限制一些接口只能本网站的才能调,外部网站不能调

请求头中

Referrer-Policy: origin

meta标签

<meta name="referrer" content="origin">

a标签

<a href="..." referrerpolicy="origin" target="_blank">xxx</a>