WEB-WORKER进阶学习(二)
由于JS单线程模型的原因,虽然可以通过异步来处理请求。但是最终还是需要由主线成处理
出于希望将渲染 / (请求、计算) 解耦的想法,所以对现在由axios构建的api请求层做改造,所有的数据请求交予web-work处理。达到渲染与请求分开的目的
问题
- 同时存在多少个Worker比较合适 ?
理论上worker没有上限,开启多少个都可以,根据实际情况即可。不建议按照CPU(
navigator.hardwareConcurrency
)核心数开启对应的数量像目前做的请求/渲染分离就是开启4个作为守护线程。因为除了IE6,7最少支持4个并发请求
- 多个Worker如何协同工作 ?
需要考虑开启多个worker的统一调配的问题,与负载均衡的问题。
- 开启的worker可以通过数组存储
- 负载均衡可以使用轮询,最有可用等算法来处理
- 消息该如何处理 ?
与axios不同的是,worker处理请求跨越不同的线程,真正的实现异步的请求。那如何保证正确的触发回调
- 内部通过Promise构建,返回调用者Promise对象,同时为任务分配唯一ID。将任务ID, resolve,reject 同时存储记录
- worker将返回数据/异常与接受到的任务ID一并打包返回
- 通过任务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的代码混淆压缩?
通过
对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$/)
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 } }) }
不足
- 超时等问题如何处理?
- 文件上传如何处理?
- 增加轮询之外的负载策略?
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
服务端高并发分布式架构演进之路
1. 概述本文以淘宝作为例子,介绍从一百个到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原则。特别说明:本文以淘宝为例仅仅是为了便于说明演进过程可能遇到的问题,并非是淘宝真正的技术演进路径 2. 基本概念在介绍架构之前,为了避免部分读者对架构设计中的一些概念不了解,下面对几个最基础的概念进行介绍: 分布式系统中的多个模块在不同服务器上部署,即可称为分布式系统,如Tomcat和数据库分别部署在不同的服务器上,或两个相同功能的Tomcat分别部署在不同服务器上 高可用系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性 集群一个特定领域的软件部署在多台服务器上并作为一个整体提供一类服务,这个整体称为集群。如Zookeeper中的Master和Slave分别部署在多台服务器上,共同组成一个整体提供集中配置服务。在常见的集群中,客户端往往能够连接任意一个节点获得服务,并且当集群中一个节点掉线时,其他节点往往能够自动的接替它继续提供服务,这时候说明集群具有高可用性 负载均...
- 下一篇
用Junit测试多线程的正确姿势
上回说到了猿哥(程序猿)遇到了Pagehelper分页问题,最后大师(架狗师)帮其解惑,猿哥茅塞顿开,对Mybatis的分页插件有了更加深入的了解。猿哥孜孜不倦对技术的渴望那是非常人所能及的,力求完全搞懂源码背后的原理。此时猿哥又脑洞大开,又要开始折腾自己了,想要写个测试代码来验证大师所说的那些技术问题。有趣的事情又发生了... 线程池模拟发起数据查询任务 首先,猿哥想到的是怎么样去模拟发起多次请求而且这2次请求还要分给同一个线程去执行,猿哥想到了JDK里面的线程池,于是二话不说,就开干,龙飞凤舞的写出了下面一段用Junit写的测试代码段如下,该代码段用了ExecutorService来创建一个固定的只有一个线程的线程池executor,然后写了2个任务,这2个任务一个只执行了PageHelper.startPage(1,10);分页操作,就没有继续数据库查询操作,另外一个是没有分页操作的查询全部数据的业务代码块,这样测试代码就准备好了,接下来就执行并验证本来没有执行分页的查询全部的数据最后返回的结果的条数。 @Test public void test() throws Interr...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程