使用 Puppeteer 搭建统一海报渲染服务

阿里云幸运券

文 | 张敏 on 前端
背景
有赞微商城包括了 PC 端、H5 端和小程序端,每个端都有绘制分享海报的需求。最早的时候我们是在每个端通过 canvas API 来绘制的,通过 canvas 绘制有很多痛点,与本文要讲的海报渲染服务做了一个对比:

对比项 Canvas Node 海报渲染服务
上手门槛 需要掌握 canvas API 了解 HTML、CSS 语法即可
代码体积 占用小程序包体积 代码存放在服务端,无需下载
代码可读性 较差,调试复杂 可读,易于调试
代码复用性 多端重复编码 Node 端统一处理,无须重复编码
兼容性 小程序 canvas 存在兼容问题 无兼容问题
缓存策略 无缓存 基于 Redis 缓存
正是因为这些痛点问题,有同事就提出基于 Puppeteer 实现一个公共的海报渲染服务,使用方只需传入海报图片的 html,海报渲染服务绘制一张对应的图片作为返回结果,解决了 canvas 绘制的各种痛点问题。

一、Puppeteer 是什么
Puppeteer 是谷歌官方团队开发的一个 Node 库,它提供了一些高级 API 来通过 DevTools 协议控制 HeadlessChrome 或 Chromium。通俗的说就是提供了一些 API 用来控制浏览器的行为,比如打开网页、模拟输入、点击按钮、屏幕截图等操作,通过这些 API 可以完成很多有趣的事情,比如本文要讲的海报渲染服务,它用到的就是屏幕截图的功能。

二、Puppeteer 能做什么
Puppeteer 几乎能实现你能在浏览器上做的任何事情,比如:

生成页面的屏幕截图或 pdf

自动化提交表单、模拟键盘输入、自动化单元测试等

网站性能分析:可以抓取并跟踪网站的执行时间轴,帮助分析效率问题

抓取网页内容,也就是我们常说的爬虫

三、海报渲染服务
3.1 方案设计
首先我们来看一下海报渲染服务的流程图:

其实整个流程还是比较简单的,当有一个绘制请求时,首先看之前是否已经绘制过相同的海报了,如果绘制过,就直接从 Redis 里取出海报图片的 CDN 地址。如果海报未曾绘制过,则先调用 HeadlessChrome 来绘制海报,绘制完后上传到 CDN,最后 CDN 上传完后返回 CDN 地址。整个流程的大致代码实现如下:

3.2 遇到的问题
2.3.1 Chromium 启动和执行流程
最开始一个版本我们是直接 Puppeteer.launch()返回一个浏览器实例,每次绘制会用单独的一个浏览器实例,这个在使用过程中发现绘制海报会很慢,后面优化时找到了这篇文章:Puppeteer 性能优化与执行速度提升,这篇文章提到了两个优化点:1. 优化 Chromium 启动项;2. 优化 Chromium 执行流程。

先说优化 Chromium 启动项,这个就是为了我们启动一个最小化可用的浏览器实例,其他不需要的功能都禁用掉,这样会大大提升启动速度。

再来说说浏览器的执行流程,最开始我们是每次绘制都会用单独一个浏览器,也就是一对一,这个在压测的时候发现 CPU 和内存飙升,最后我们改用了复用浏览器标签的方式,每次绘制新建一个标签来绘制。

3.2.2 networkidle0
最开始我们的海报服务绘制海报时有时候会偶尔出现图片展示不出来的情况,我们排查后发现是因为我们 setContent 时,使用的是默认的 load 事件来判断设置内容成功,而我们期望的是所有网络请求成功后才算设置内容成功。

Puppeteer 在 setContent 和 goto 等方法里提供了一个 waitUntil 的参数,它就是用来配置这个判断成功的标准,它提供了四个可选值:

load:默认值, load 事件触发就算成功

domcontentloaded: domcontentloaded 事件触发就算成功

networkidle0:在 500ms 内没有网络连接时就算成功

networkidle2:在 500ms 内有不超过 2 个网络连接时就算成功

我们这里需要用到的就是 networkidle0:

当改成 networkidle0 后,使用方给我们反馈说整个绘制服务变慢了很多,随随便便都2s以上。变慢主要是因为加上 networkidle0 后,至少需要等待 500ms 以上,加上绘制的一些其他开销,基本上就需要 2s 了。所以我们期望这个 500ms 是可配置的,因为 500ms 实在太长了,我们的分享海报一般只有几张图片,不需要这么久。但是 Puppeteer 没有提供相关的参数,还好在 issue 中早已经有人提出了这个问题:Control networkidle wait time

3.2.3 Chromium定时刷新机制

为什么需要定时刷新 Chromium 呢?总不可能一直用同一个 Chromium 实例吧,万一变卡或者 crash 了,就会影响海报的绘制。所以我们需要定时的去刷新当前的浏览器实例。

这里还有一个点,我们给 replaceBrowserInstance 这个方法加了个重试次数的限制,当超出这个限制后不管有没有任务在进行都会关闭浏览器。这个是防止在某些特殊情况不能关闭掉浏览器,导致内存无法释放的情况。

腾讯云代金券

原文链接

https://mp.weixin.qq.com/s?__biz=MzAxOTY5MDMxNA%3D%3D&mid=2455759820&idx=1&sn=0eb9e19a176f01ecb2c1813521d97692

服务推荐

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值