这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

业务场景:

产品有个功能是设置主题。类似手机自动切换壁纸,以及其他功能颜色,icon,字体等。

管理员需要在后端管理系统多次下载不同主题,(至于要干啥就不说了...),主题中可能有 30 ~ 100个高清壁纸, icon 等。现在每次下载主题(31张高清图片)至少需要 10s。有什么方法能够优化下。

因为代码不具备可复用性,因此部分代码直接省略,思路为主

原始逻辑

  public async getZip(themeId: string, res: any) {
const theme = await this.model.findById(themeId); // 从数据库 // 这里需要借用一个服务器上的主题模板文件夹 template/, /*
theme = {
wallpapers: [
{ url: 'https://亚马逊云.com/1.jpg', ... },
...
]
}
*/ // for 循环遍历 theme.wallpapers , 并通过 fetch 请求 url,将其写进 template/static/wallpapers 文件夹中
theme.wallpapers.map((item) => {
const response = await fetch(item.url);
const buffer = new Uint8Array(await response.arrayBuffer());
await fs.writeFile(`template/wallpapers/${fileName}`, buffer);
}) // ... 还有其他一些处理 // 将 template 压缩成 zip 文件,发送给前端
}

思考 ing ...

1 利用图片可以被浏览器缓存

当一次下载主题从请求亚马逊云的图片数据,这步没有问题。 但是当重复下载的时候,之前下载过的图片又会再次下载,操作人员每次都需要等个十几秒,这就不太友好了。这部分时间花费还是挺多的。

可以利用下浏览器能够将图片缓存到 disk cache 中的特点,将这部分的代码逻辑放到前端完成,因为还需要对压缩包中的文件做一些处理,因此需要借助下 jszip 这个库。

看下改后的代码

onDownload () {
// 请求拿到 theme 数据
const theme = api.getTheme()
const template = api.getTemplate() // Blob const zip = new JSZip()
await zip.loadAsync(getTmplResp) // 读取 template.zip 文件数据 console.time('handle images')
const wallpaperList = theme.wallpapers
for (const wallpaper of wallpaperList) {
const response = await fetch(wallpaper.url) // 请求图片数据
const buffer = new Uint8Array(await response.arrayBuffer())
const fileName = wallpaper.url.split('/').pop()
zip.file(`static/wallpapers/${fileName}`, buffer, { binary: true }) // 写进压缩包
}
console.timeEnd('handle images') // 统计用时 // 还需要读取 template.zip 中的 config.json, 然后修改,重新保存到 template.zip 中
... // 导出 template.zip
zip.generateAsync({ type: 'base64' }).then(
(base64) => {
const link = document.createElement('a')
link.href = 'data:application/zip;base64,' + base64
link.download = 'template.zip'
link.target = '_blank'
link.style.display = 'none' document.body.appendChild(link)
link.click()
document.body.removeChild(link)
},
(err) => {
console.log('打包失败', err)
}
)
}

优化完成

当第一次下载时,handle images 步骤耗时 20 - 21 s,流程和后端差不多。

当第二次下载时,handle images 步骤耗时 0.35s - 0.45 s。会直接读取 disk cache 中的图片数据,50 ms 内就完成了。

速度快了 57 倍有余!!!, 你还能想到其他优化方式吗?继续往后看

第一次请求各个图片耗时

 第二次请求各个图片耗时

2 并发请求

我们都知道,浏览器会为每个域名维持 6 个 TCP 链接(再拓展还有域名分片知识),我们是否可以利用这个特点做些什么?

答案是:并发上传

通过上面的代码,可以看到,每个图片请求都是串行的,一个图片请求完了再进行下一个图片请求。我们一次请求 4 个图片,这样就更快了。

首先写一个能够管理并发任务的类

export class TaskQueue {
public queue: {
task: <T>() => Promise<T>
resolve: (value: unknown) => void
reject: (reason?: any) => void
}[]
public runningCount: number // 正在执行的任务数量
public tasksResloved?: (value: unknown) => void
public tasksRejected?: (reason?: any) => void public constructor(public maxConcurrency: number = 4) { // 最多同时执行 4 个任务
this.queue = [] // 任务队列
this.runningCount = 0
} // 添加任务
public addTask(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject })
})
} // 执行
public run() {
return new Promise((resoved, rejected) => {
this.tasksResloved = resoved
this.tasksRejected = rejected
this.nextTask()
})
} private nextTask() {
if (this.queue.length === 0 && this.runningCount === 0) {
this.tasksResloved?.('done')
return
} // 如果任务队列中还有任务, 并且没有到最大执行任务数,就继续取出任务执行
while (this.queue.length > 0 && this.runningCount < this.maxConcurrency) {
const { task, resolve, reject } = this.queue.shift()
this.runningCount++
task()
.then((res) => {
this.runningCount--
resolve(res)
this.nextTask()
})
.catch((e) => {
this.runningCount--
reject(e)
this.nextTask()
})
}
}
}

改造代码

onDownload () {
// 请求拿到 theme 数据
const theme = api.getTheme()
const template = api.getTemplate() // Blob const zip = new JSZip()
await zip.loadAsync(getTmplResp) // 读取 template.zip 文件数据 console.time('handle images')
const wallpaperList = theme.wallpapers // 注释之前的逻辑
// for (const wallpaper of wallpaperList) {
// const response = await fetch(wallpaper.url)
// const buffer = new Uint8Array(await response.arrayBuffer())
// const fileName = wallpaper.url.split('/').pop()
// zip.file(`static/wallpapers/${fileName}`, buffer, { binary: true })
// } const taskQueue = new TaskQueue() // 新建任务队列,默认同时执行 4 个
for (const wallpaper of wallpaperList) {
taskQueue
.addTask(() => fetch(wallpaper.url)) // 添加任务
.then(async (res) => { // 任务执行完后的回调
const buffer = new Uint8Array(await (res as Response).arrayBuffer())
const fileName = wallpaper.url.split('/').pop()
zip.file(`static/wallpapers/${fileName}`, buffer, { binary: true })
})
.catch((e) => console.log('壁纸获取失败', e))
}
await taskQueue.run() // 等待所有图片都拿到
console.timeEnd('handle images') // 统计用时 // 还需要读取 template.zip 中的 config.json, 然后修改,重新保存到 template.zip 中
... // 导出 template.zip
zip.generateAsync({ type: 'base64' }).then(
(base64) => {
const link = document.createElement('a')
link.href = 'data:application/zip;base64,' + base64
link.download = 'template.zip'
link.target = '_blank'
link.style.display = 'none' document.body.appendChild(link)
link.click()
document.body.removeChild(link)
},
(err) => {
console.log('打包失败', err)
}
)
}

大功告成!

当第一次下载时,handle images 步骤耗时 7 s,速度是之前的 3 倍。

当第二次下载时,handle images 步骤耗时 0.25s,速度是之前的 1.4 - 1.8

3 更多的可能

越来越感觉到计算机网络的重要性, 还有未实现的优化方式:

  1. 域名分片,更多的并发(也有劣势 ,比如 每个域都需要额外的 DNS 查找成本以及建立每个 TCP 连接的开销, TCP 慢启动带宽利用不足)
  2. 升级 HTTP2 这不是靠前端一人能够完成的

本文转载于:

https://juejin.cn/post/7267418197746270271

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--记录用前端代替后端生成zip的过程,速度快了 57 倍!!!的更多相关文章

  1. 从前端到后端实现弹幕的过程(jsp/Jquery.barrager.js)

    Jquery.barrager.js插件,可以去网上下载!下载完后,就把下载文件中的js文件.css文件.图片文件.等等等文件全部拷贝到你们自己的项目中去,千万别拷贝漏了,如果你怕拷贝漏了什么,那就把 ...

  2. Dockerfile + Nginx.conf文件记录(用于前端项目部署)

    Dockerfile + Nginx.conf文件记录(用于前端项目部署) 本教程依据个人理解并经过实际验证为正确,特此记录下来,权当笔记. 注:基于linux操作系统(敏感信息都进行了处理),默认服 ...

  3. 看完让你彻底理解 WebSocket 原理,附完整的实战代码(包含前端和后端)

    1.前言 最近有同学问我有没有做过在线咨询功能.同时,公司也刚好让我接手一个 IM 项目.所以今天抽时间记录一下最近学习的内容.本文主要剖析了 WebSocket 的原理,以及附上一个完整的聊天室实战 ...

  4. 基于SqlSugar的开发框架循序渐进介绍(15)-- 整合代码生成工具进行前端界面的生成

    在前面随笔<基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理>中我们已经介绍过,对于相关的业务表的界面代码,我们已经尽可能把不同的业务逻辑 ...

  5. 前端与后端数据交互的方式之ajax

    前端与后端数据交互的方式之Ajax 对于前端学习而言,CSS+HTML+JavaScript的学习在自我学习的情况下掌握也不是很难,但是想要实现前后端的数据交互在没有指导的情况下学习会是一头雾水.接下 ...

  6. 系统架构:Web应用架构的新趋势---前端和后端分离的一点想法

    最近研究servlet,看书时候书里讲到了c/s架构到b/s架构的演变,讲servlet的书都很老了,现在的b/s架构已经不是几年前的b/s架构,其实b/s架构就是web应用开发,对于这样的架构我们现 ...

  7. JAVA后端生成Token(令牌),用于校验客户端,防止重复提交

    转:https://blog.csdn.net/u011821334/article/details/79390980 转:https://blog.csdn.net/joshua1830/artic ...

  8. 前端和后端采用接口访问时的调用验证机制(基于JWT的前后端验证)(思路探讨)

    说明:基于前后端,尤其是使用Ajax请求的接口,现在市面上网页上调用的Ajax基本都是没有验证的,如果单独提取之后可以无线的刷数据. 继上一篇http://www.cnblogs.com/EasonJ ...

  9. Java生鲜电商平台-Java后端生成Token架构与设计详解

    Java生鲜电商平台-Java后端生成Token架构与设计详解 目的:Java开源生鲜电商平台-Java后端生成Token目的是为了用于校验客户端,防止重复提交. 技术选型:用开源的JWT架构. 1. ...

  10. day86:luffy:前端发送请求生成订单&结算页面优惠劵的实现

    目录 1.前端发送请求生成订单 1.前端点击支付按钮生成订单 2.结算成功之后应该清除结算页面的数据 3.后端计算结算页面总原价格和总的真实价格并存到数据库订单表中 2.优惠劵 1.准备工作 2.前端 ...

随机推荐

  1. 49从零开始用Rust编写nginx,我竟然在同一个端口上绑定了多少IP

    wmproxy wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 负载均衡, 静态文件服务器,websocket代理,四层TCP/UDP转发,内网穿透等,会将实 ...

  2. openai chatGPT 原理通俗介绍

    引言 近年来,随着深度学习技术的不断发展,自然语言处理(NLP)领域取得了长足的进步.ChatGPT(Generative Pre-trained Transformer)作为一种先进的语言生成模型, ...

  3. java 如何实现开箱即用的敏感词控台服务?

    sensitive-word-admin sensitive-word-admin 是基于 sensitive-word 实现的, 一款开箱即用的敏感词控台服务. 特性 基本的 CRUD 开箱即用的配 ...

  4. vue+jsPlumb制成的流程图项目

    本项目参考:https://github.com/wangduanduan/visual-ivr 主页面如下:

  5. 遭遇DDOS攻击忍气吞声?立刻报警!首都网警重拳出击,犯罪分子无所遁形

    公元2024年2月24日18时许,笔者的个人网站突然遭遇不明身份者的DDOS攻击,且攻击流量已超过阿里云DDos基础防护的黑洞阈值,服务器的所有公网访问已被屏蔽,由于之前早已通过Nginx屏蔽了所有国 ...

  6. jupyterlab安装和优化

    说明 JupyterLab(官网https://jupyter.org)是一个交互式的代码编辑器,打开它会打开一个网页,可以在其中编写代码,即时执行,快速得到结果(包括代码返回值.统计图和界面交互图) ...

  7. mongo重启、远程连接

    1.查看当前mongo启动进程 ps -ef | grep mongo 2.修改mongo启动远程连接配制文件 vi /etc/mongod.conf 将 bind_ip=127.0.0.1 这一行注 ...

  8. 【Azure 事件中心】Azure Event Hub中的数据能不能存储大于7天呢?如果7天之后是不是会自动删除呢?

    问题描述 Event Hub中有个retention的设置为7天,有没有办法增大这个Retention的时间? 如果没办法,是不是超过7天的数据就会被删除? 问题解答 因为Azure Event Hu ...

  9. 聊聊图数据库和图数据库的小知识 Vol.02

    2010 年前后,对于社交媒体网络研究的兴起带动了图计算的大规模应用. 2000 年前后热门的是 信息检索 和 分析 ,主要是 Google 的带动,以及 Amazon 的 e-commerce 所用 ...

  10. kotlin协程异常处理之-try catch

    kotlin协程小记 协程的async使用 kotlin协程异常处理之-try catch kotlin协程异常处理之-CoroutineExceptionHandler 一.try catch tr ...