函数计算中使用 puppeteer 的过程分析

本文涉及的产品
简介: 本文通过实现网页截图功能案例来讲解在函数计算中如何使用 Puppeteer。另外,读者如果遇到同类问题,如:在函数计算中安装自己的共享库,同样可以参考本文章。

该文章已经过期

最新的方案请参考: fc-puppeteer-demo

puppeteer.js github 地址:https://github.com/GoogleChrome/puppeteer
API: https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md
函数计算文档:https://help.aliyun.com/product/50980.html?spm=a2c4g.750001.2.6.uO2VV4

简介

本文通过实现网页截图功能案例来讲解在函数计算中如何使用 Puppeteer。另外,读者如果遇到同类问题,如:在函数计算中安装自己的共享库,同样可以参考本文章。

快速开始

如果您不想知道技术细节,而是想快速的将 Puppeteer 在函数计算中用起来,我这里提供了一个快速开始项目,基于这个项目,您可以马上写业务相关的代码,还提供快速打包和本地调试脚本(强烈推荐)。项目地址:https://github.com/awesome-fc/puppeteer-fc-starter-kit。接下来,我会介绍 Puppeteer 能在函数计算中使用的实现方法。

Puppeteer 简介

Puppeteer 是一个 node 库,他提供了一组用来操纵 Chrome 的 API, 通俗来说就是一个 headless chrome 浏览器 (当然你也可以配置成有UI的,默认是没有的)。既然是浏览器,那么我们手工可以在浏览器上做的事情 Puppeteer 都能胜任, 另外,Puppeteer 翻译成中文是”木偶”意思,所以听名字就知道,操纵起来很方便,你可以很方便的操纵他去实现:

  • 生成网页截图或者 PDF
  • 高级爬虫,可以爬取大量异步渲染内容的网页
  • 模拟键盘输入、表单自动提交、登录网页等,实现 UI 自动化测试
  • 捕获站点的时间线,以便追踪你的网站,帮助分析网站性能问题

在函数计算中使用遇到的问题

  • 尺寸太大: Puppeteer 默认下载的 chrome headless 文件尺寸太大( 180左右 MB,压缩后 50 MB 以上),超过了函数计算的限制大小(限制:压缩后 50 MB ,解压后 250 MB)
  • 依赖动态链接库: chrome headless 自身依赖了一些动态链接库,在函数计算运行环境中没有提供
  • 目录只读: 函数计算运行环境只有 /tmp 目录有写的权限,也无法在运行时直接安装依赖包到系统目录

解决方案

将项目中依赖的 deb 包安装(或者解压)到指定目录,由于依赖的动态库文件只有 4 MB 多,所以可以安装到项目根目录下的 lib 目录中, chrome headless 文件默认尺寸会比较大,我们可以自己编译 chrome headless,自己编译的 chrome headless 文件大小为 109.6 MB,压缩后才 45.6 MB。项目代码、chrome headless 和相关动态链接库一起打包,压缩包的大小也没有超过 50 MB 的限制。如果依赖的动态链接库超过 50 MB,可以打包上传到 oss通过 oss 下载安装到 /tmp 目录中。在本案例中,chrome headless 和 chrome headless 的相关第三方依赖库文件尺寸没有超过 50 MB。方案具体实施步骤如下所述。

实施步骤

创建项目

cd ~
mkdir screenshot
cd screenshot
npm init

注意: 项目用到了一些比较新的语法 await 和 async,所以需要函数计算运行环境选择 nodejs8

进入沙箱环境

cd ~/screenshot
fcli shell            #进入 fcli 交互模式
sbox -d . -t nodejs8  #将当前目录(~/screenshot)挂载到沙盒环境的 /code 位置

注:fcli 为您提供了一个本地的沙盒环境,和函数计算服务中的函数运行环境保持一致。在沙盒环境中,您可以方便的安装第三方库,进行本地调试等操作。

fcli 工具文档

安装项目依赖

package.json 如下:

{
    ...
    "dependencies": {
        "puppeteer": "^1.4.0",
        "tar": "^4.4.4"
    }
    ...
}
export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true  #设置跳过下载 chrome headless 的环境变量
npm install
#或者直接 npm install --ignore-scripts

安装 chrome headless 依赖的动态链接库

##在沙箱环境中
cd /code

mkdir debs
#下面的命令参数 -d 是让 apt-get 只下载安装包,不进行安装
apt-get install -d -o=dir::cache=/code/debs libnspr4 libnss3
#创建 lib 目录,用于安装 chrome headless 依赖的动态链接库
mkdir lib
#安装上面下载的库文件到指定目录
for file in $(ls /code/debs/archives)
do
    dpkg -x  /code/debs/archives/$file /code/lib/
done
r​m -rf debs

说明:动态链接库的关联需要设置环境变量 LD_LIBRARY_PATH,在下面 nodejs 代码中设置了,具体请看编写函数

编译 chrome headless

我已经编译过 chrome headless,下载地址:https://github.com/muxiangqiu/puppeteer-fc-starter-kit/blob/master/chrome/headless_shell.tar.gz?raw=true,如果需要自己编译,可以参考我写好的脚本,或者直接运行我的脚本。

编写函数

基于 pupeteer.js 实现对网页截图,并将图片返回,代码如下:

//index.js
const puppeteer = require('puppeteer');
const tar = require('tar');
const fs = require('fs');
const path = require('path');

//判断是否存在 /tmp/headless 目录
const existsExecutableChrome = () => {
    return  new Promise((resolve, reject) => {
        fs.exists('/tmp/headless_shell', exists => {
            resolve(exists);
        });
    });
};

//从 oss 下载安装 chrome headless 和第三方依赖库文件,如果安装过了,则跳过
const setupChromeIfNecessary = async (context, callback) => {
    if (await existsExecutableChrome()) {
        return;
    }

    return new Promise((resolve, reject) => {
        //解压到 /tmp 目录中
        fs.createReadStream(config.localChromePath)
            .on('error', (err) => reject(err))
            .pipe(tar.x({
              C: 'tmp',
            }))
            .on('error', (err) => reject(err))
            .on('end', () => resolve());
    });
    
};

module.exports.handler =  async (event, context, callback) => {
    await setupChromeIfNecessary(context, callback);
    const ldLibraryPath = `${process.env['FC_FUNC_CODE_PATH']}/lib/usr/lib/x86_64-linux-gnu/`;
    const browser = await puppeteer.launch({
        headless: true,
        //指定 chrome headless 的执行路径
        executablePath: '/tmp/headless_shell',
        args: ['--no-sandbox', '--disable-setuid-sandbox'],
        env: {
            //chrome headless 的第三方依赖库安装到项目的 lib 目录下面,
            //所以需要设置 LD_LIBRARY_PATH 环境变量,告诉系统如何加载这部分的动态库
            LD_LIBRARY_PATH: `${process.env['LD_LIBRARY_PATH']}:${ldLibraryPath}`
        }
    });

    const page = await browser.newPage();
    await page.goto('https://fc.console.aliyun.com');
    try {
        const screenshot = await page.screenshot({
          clip: {
            x: 200,
            y: 60,
            width: 780,
            height: 450
          }
        });
        await page.close();
        callback(null, screenshot))
    } catch (err) {
        callback(err);
    }
};

总结

通过上面的步骤,项目代码压缩后大小为 47 MB 左右 ,包含 chrome headless 和相关动态链接库。冷启动运行时间: 2700 ms 左右,热启动运行时间:300 ms 左右,内存使用:350 MB 左右(注意:在创建函数的时候,内存最好设置为 512 MB)。

本解决方案适用于很多类似的场景,适用规则如下:

  • 需要给函数计算运行环境安装动态链接库
  • 项目尺寸超过了函数计算的限制大小

查阅参考

感谢

感谢如下同学在本案列制作和文档撰写过程中给予的建议和帮助

@倚贤 @不瞋 @夏莞 @敬畏

联系方式

电子邮件: subo.ysb@alibaba-inc.com

相关实践学习
基于函数计算一键部署掌上游戏机
本场景介绍如何使用阿里云计算服务命令快速搭建一个掌上游戏机。
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
3月前
|
关系型数据库 MySQL Serverless
高顿教育:大数据抽数分析业务引入polardb mysql serverless
高顿教育通过使用polardb serverless形态进行数据汇总,然后统一进行数据同步到数仓,业务有明显高低峰期,灵活的弹性伸缩能力,大大降低了客户使用成本。
|
5月前
|
缓存 关系型数据库 Serverless
数据库内核那些事,PolarDB HTAP Serverless,打造经济易用的实时分析系统
下本从IMCI Serverless核心优势角度的介绍各优化工作内容。
数据库内核那些事,PolarDB HTAP Serverless,打造经济易用的实时分析系统
|
6月前
|
Cloud Native 安全 关系型数据库
一起架构-某实时分析项目云原生 serverless 架构的设计思路和poc代码实现
一起架构-某实时分析项目云原生 serverless 架构的设计思路和poc代码实现
|
8月前
|
SQL 运维 DataWorks
EMR Serverless StarRocks + DataWorks 开启极速分析新体验
EMR Serverless StarRocks + DataWorks ,开启极速分析体验
999 0
EMR Serverless StarRocks + DataWorks 开启极速分析新体验
|
10月前
|
存储 SQL 数据可视化
EMR Serverless StarRocks 5000CU*时免费试用——体验极致性能和全面 OLAP 分析
EMR Serverless StarRocks 5000CU*H 计算资源,48000GB*H存储资源免费试用进行中,提供极致的性能和丰富的 OLAP 场景模型,包括 OLAP 多维分析、数据湖分析、高并发查询以及实时数据分析,快来体验吧!
1408 0
EMR Serverless StarRocks 5000CU*时免费试用——体验极致性能和全面 OLAP 分析
|
数据采集 Serverless
《函数计算最佳实践:快速开发一个分布式 Puppeteer 网页截图服务》电子版地址
Serverless 开发实战-快速开发一个分布式 Puppeteer 网页截图服务
110 0
《函数计算最佳实践:快速开发一个分布式 Puppeteer 网页截图服务》电子版地址
|
消息中间件 缓存 JSON
Serverless架构实现CDN预热实践分析
预热是源站将会主动将对应的资源缓存到CDN节点,当您首次请求时,就能直接从CDN节点缓存中获取到最新的请求资源,提高缓存命中率。在游戏等行业,业务高峰前对热门资源进行预热,提高缓存命中率,降低访问延时。
Serverless架构实现CDN预热实践分析
|
消息中间件 数据采集 运维
在游戏运营行业,Serverless 如何解决数据采集分析痛点?
众所周知,游戏行业在当今的互联网行业中算是一棵常青树。在疫情之前的 2019 年,中国游戏市场营收规模约 2884.8 亿元,同比增长 17.1%。2020 年因为疫情,游戏行业更是突飞猛进。玩游戏本就是中国网民最普遍的娱乐方式之一,疫情期间更甚。据不完全统计,截至 2019 年,中国移动游戏用户规模约 6.6 亿人,占中国总网民规模 8.47 亿的 77.92%,可见游戏作为一种低门槛、低成本的娱乐手段,已成为大部分人生活中习以为常的一部分。
在游戏运营行业,Serverless 如何解决数据采集分析痛点?
|
消息中间件 数据采集 运维
Serverless在游戏运营行业进行数据采集分析的最佳实践
这个架构不光适用于游戏运营行业,其实任何大数据采集传输的场景都是适用的,目前也已经有很多客户正在基于Serverless的架构跑在生产环境,或者正走在改造Serverless 架构的路上。
4383 0
Serverless在游戏运营行业进行数据采集分析的最佳实践
|
SQL 分布式计算 大数据
9.29直播预告|数据湖分析DLA之Serverless SQL(兼容Presto)技术解析
本次分享将向您介绍DLA SQL基于Presto引擎做的一系列优化比如多Coordinator、租户隔离、Coonector方面的优化细节,以及为何DLA SQL比您自建Presto性价比更高的奥秘。
867 0
9.29直播预告|数据湖分析DLA之Serverless SQL(兼容Presto)技术解析

相关产品

  • 函数计算