您的位置:首页 > Web前端 > Vue.js

WEB-WORKER进阶学习(二)

2019-12-19 12:05 3179 查看

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

由于JS单线程模型的原因,虽然可以通过异步来处理请求。但是最终还是需要由主线成处理

出于希望将渲染 / (请求、计算) 解耦的想法,所以对现在由axios构建的api请求层做改造,所有的数据请求交予web-work处理。达到渲染与请求分开的目的

问题

  • 同时存在多少个Worker比较合适 ?

理论上worker没有上限,开启多少个都可以,根据实际情况即可。不建议按照CPU(navigator.hardwareConcurrency)核心数开启对应的数量

像目前做的请求/渲染分离就是开启4个作为守护线程。因为除了IE6,7最少支持4个并发请求

  • 多个Worker如何协同工作 ?

需要考虑开启多个worker的统一调配的问题,与负载均衡的问题。

  1. 开启的worker可以通过数组存储
  2. 负载均衡可以使用轮询,最有可用等算法来处理
  • 消息该如何处理 ?

与axios不同的是,worker处理请求跨越不同的线程,真正的实现异步的请求。那如何保证正确的触发回调

  1. 内部通过Promise构建,返回调用者Promise对象,同时为任务分配唯一ID。将任务ID, resolve,reject 同时存储记录
  2. worker将返回数据/异常与接受到的任务ID一并打包返回
  3. 通过任务ID将任务从任务池中pop出,执行对应的回调
  • 请求使用什么技术? 仍然使用axios还是fetch ?

这点其实比较纠结

  • axios使用方便
  • 但是不使用axios可以缩减压缩包体积
  • 如果是项目改造需要将axios的格式与fetch的api格式做转换处理

最终选用fetch, 在内部实现时,增加拦截器对数据做处理。

另外考虑的一点是, fetch脱离了xhr的。理论上性能更好,但是也有缺点

  • fetch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
  • fetch默认不会带cookie,需要添加配置项
  • fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
  • fetch没有办法原生监测请求的进度,而XHR可以
  • 全局配置如何处理,例如请求头?

通过拦截器前置处理

  • 跨域的问题 ?

不建议使用jsonp, 可以增加cors头或者增加一个node做中转

  • 如何将worker的代码混淆压缩?
  1. 通过

    对web-worker做处理,配置如下

    // 整合worker-loader,基于vue-cli3
    config.module
    .rule('worker')
    .test(/\.worker\.js$/)
    .use('worker-loader')
    .loader('worker-loader')
    .end()
    config.module.rule('js').exclude.add(/\.worker\.js$/)
  2. webpack外链(未测试)

实现

整体构思将分为两块

  • 线程组 工作组内部持有所有活动work的实例,提供消息转发,均衡算法支持,回调机制实现,内部包含

    interceptor 拦截器 参考axios的前后置拦截机制,提供默认的请求、相应的处理并对外暴露
export const interceptors = {
// 前置适配转换数据类型
transfer(config) {
return {
url: `${window.location.origin}${process.env.VUE_APP_BASE_API}${config.url}${config.params ? '?' + param(config.params) : ''}`,
options: {
body: config.data ? JSON.stringify(config.data) : undefined,
cache: config.cache,
headers: config.headers || {},
method: config.method || 'GET'
}
}
},
// 请求过滤
request(config) {
if (store.getters.token) {
config.options.headers['Auth'] = getToken()
}
return config
},
// 响应过滤
async response(res) {
return res
}
}
  • balance 负载均衡 内部实现负载均衡的实现
const balance = (function() {
let index = 0
/** 均衡算法 */
const BALANCE_ALGORITHM = {
/** 轮询下标 */
ROUND_ROBIN() {
const next = workers[index % workers.length]
index++
return next
}
}
const tasks = {}
const addTask = (option, resolve, reject) => {
const id = uuid()
option.id = id
tasks[id] = {
resolve: resolve,
reject: reject
}
}
return {
next() {
return BALANCE_ALGORITHM.ROUND_ROBIN()
},
postMessage(option, resolve, reject) {
addTask(option, resolve, reject)
this.next().postMessage(option)
},
popTask(id) {
const task = tasks[id]
delete task[id]
return task
}
}
})()
  • workers worker线成组,持有对应实例
const workers = new Array(4)
for (let i = 0; i < workers.length; i++) {
const worker = new Worker()
worker.onmessage = receive
workers[i] = worker
}

  • 工作组 工作组内部实现队列机制,接受到的任务逐一消费回复,通过队列机制防止高并发的请求峰值,通过合理的机制对请求削峰,并且浏览器对单域请求存在上限(以Chrome为例,上限为6)。所以内部实际是一个状态机,其内部包含:

    queue 请求任务队列
const queue = []
  • state 当前运行状态, 默认空闲
/** 运行中 */
const RUNNING = 'RUNNING'
/** 空闲 */
const IDLE = 'IDLE'
/** 当前状态 */
let state = IDLE
  • request 请求函数
/**
* 执行请求
* 通过fetch发送请求,将反馈发送到主线程
* 结束后检查队列
* @param event event main-thread message
*/
const request = (event) => {
state = RUNNING
const { data } = event
fetch(data.url, data.options)
.then(response => response.json())
.then(json => {
postMessage({ success: true, response: json, id: data.id })
})
.catch(reason => {
postMessage({ error: true, message: reason.message, id: data.id })
})
.finally(() => {
const next = queue.pop()
if (next) {
request(next)
} else {
state = IDLE
}
})
}

不足

  • 超时等问题如何处理?
  • 文件上传如何处理?
  • 增加轮询之外的负载策略?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  XHR Axios WebWork Vue CLI Next