Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染

为什么需要 Rendertron?

传统的 Web 页面,通常是服务端渲染的,而随着 SPA(Single-Page Application) 尤其是 React、Vue、Angular 为代表的前端框架的流行,越来越多的 Web App 使用的是客户端渲染。

使用客户端渲染有着诸多优势,比如节省后端资源、局部刷新、前后端分离等等,但也带来了一些挑战,比如本文要解决的 SEO 问题。

对于服务端渲染的页面,服务端可以直接将内容通过 HTML 的形式返回,搜索引擎爬虫可以轻易的获取页面内容,而对于客户端渲染的应用,客户端必须执行服务器返回的 Javascript 才能得到正确的网页内容。目前,除 Google、Bing 支持 Javascript 外(也会有一些限制),其他的大部分搜索引擎都不支持 Javascript,也就无法获取正确的网页内容。

Google 推出的 Rendertron 就是为了解决这样场景的一款工具。通过使用 Rendertron,SPA 也能够被不支持执行 Javascript 的搜索引擎爬取渲染后的内容。其原理主要是通过使用 Headless Chrome 在内存中执行 Javascript,并在得到完整内容后,将内容返回给客户端。

Rendertron 原理介绍

通常会将 Rendertron 部署为一个独立的 HTTP 服务,然后为 Web 应用框架配置 Google 官方提供的中间件或者在反向代理上添加相应路由规则,使得能够在检测到搜索引擎爬虫的 UA 时,可以将请求代理给 Rendertron 服务。

Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染

Rendertron 提供了两个主要 API,分别是 Render 以及 Screenshot。其中 Render 用于渲染网站内容,Screenshot 用于将网站内容截图。在 SEO 场景下使用的是 Render 接口。

举例来说,当客户端请求我们的网站时,我们搜线根据请求头 User Agent 发现包含了 Baiduspider/2.0 关键字,可以认定为当前的客户端是一个百度爬虫,然后又在 UserAgent 中发现 Mobile 关键字,可以认定这个爬虫是在做移动端内容的抓取。通过上面的判断,就可以将这个请求代理 Rendertron 服务的 /render/https://www.aliyun.com/?mobile 路由,让 Rendertron 帮助执行网页内的 Javascript,并将最终内容返回给搜索引擎爬虫。

效果一览

Google 官方提供了示例 https://render-tron.appspot.com/ ,可以直接体验效果。

我们也提供了部署在函数计算上的示例:http://renderton.mofangdegisn.cn

Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染

系统架构

基于函数计算,我们的服务架构如下:

Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染

性能测试

这里我们选择阿里云的性能测试PTS服务进行压测。

测试配置如下:
Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染

我们配置了 100 并发,测试 6 分钟,每分钟并发按照 20% 递增的规则进行压测。

我们要测试的网址网站为:http://renderton.mofangdegisn.cn/render/https://www.example.com/

该网址表示让 rendertron 请求 https://www.example.com/ 这个网站的内容,并返回渲染结果。

测试概览如下:

Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染

从上面的概览可以看到,由于会发生从函数到 https://www.example.com/ 的网络请求,所以最小延迟为 1106ms,99% 的请求可以在 2011ms 完成,90% 的请求可以在 1347ms 完成,75% 的请求可以在 1201ms 完成,50% 的请求可以在 1156ms 完成。我们是每分钟按照 20% 的并发递增,当并发增加时,函数计算会遇到冷启动,冷启动最大时间为 32261ms(可以使用预热、预留等方式可以缓解或完全免除冷启动的影响)。

在未优化的场景下,我们的压测结果也达到了 44.91 的 TPS,这对于大部分网站是绝对能够满足需求的。

压测明细如下:

Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染

上面箭头所指的位置表示并发突然增加,函数自动扩容时会遇到一些冷启动,当扩容完毕,后续的请求就非常平稳了。

部署步骤

将 Rendertron 部署到传统的 ECS 或者物理机上作为生产服务,并不是件容易的事。除了 Rendertron 本身需要一些安装配置外,还需要考虑当流量增加时如何扩容,以及配置搭建反向代理或负载均衡等与之配套的服务。

下面,我们介绍下函数计算如何解决这些问题的。

参考链接

  1. Fun 安装教程 可以直接在这里下载二进制版本的 Fun,解压后即可使用。
  2. Docker 安装教程:本地安装依赖需要借助于 docker,可以直接在这里下载 Docker 。

1. clone 项目:

git clone https://github.com/GoogleChrome/rendertron.git

PS: 这里直接基于官方项目进行改造,而不是提供一个示例模板,是为了演示如何平滑迁移 rendertron 到函数计算,同时,在官方版本更新后,也可以尽快更新到最新版本。

2. 创建 template.yml 配置文件:

template.yml 是 Fun 默认的描述文件,通过该描述文件描述的资源,可以通过 fun deploy 一键在部署到云端。

比如,我们下面的模板声明了一个名为 Rendertron 的服务以及名为 rendertron 的函数。

函数是函数计算系统调度和代码执行的基本单位,我们的 rendertron 项目就可以跑在函数里,而服务是管理函数计算资源管理的单位,一个服务可以包含多个函数。

对于初学者,可以直接将下面的模板放在项目根目录下,并且命名为 template.yml。

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
Rendertron: # 声明一个名为 Rendertron 的服务
Type: 'Aliyun::Serverless::Service'
Properties:
Description: This is Rendertron service
rendertron: # 声明一个名为 rendertron 的函数
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: index.handler
Runtime: custom # runtime,我们使用 custom
Timeout: 60
MemorySize: 1024
CodeUri: ./
Events:
httpTrigger: # 添加 http 触发器
Type: HTTP
Properties:
AuthType: ANONYMOUS
Methods:
- GET
- POST
- PUT
renderton.mofangdegisn.cn: # 添加自定义域名
Type: 'Aliyun::Serverless::CustomDomain'
Properties:
Protocol: HTTP
RouteConfig:
routes:
/*:
ServiceName: Rendertron
FunctionName: rendertron

3. 创建 bootstrap 文件

接下来在项目根目录创建一个名为 bootstrap 的文件,这个文件告诉函数计算如何启动 rendertron,文件内容如下:

#!/usr/bin/env bash
PORT=9000 HOST=0.0.0.0 npm run start

4. 安装依赖 & 编译项目

直接使用 fun install -d 可以一键安装依赖,相当于官方文档里的 npm install,只不过,fun install -d 除了安装 npm 依赖外,还可以检测到 rendertron 包含的 puppeteer 依赖,并且会自动安装 puppeteer 所必须的 apt 依赖,更多细节可以参考这篇文章

fun install -d

接着使用官方介绍的 npm run build 编译项目:

npm run build

5. 本地运行 rendertron

不需要修改原项目中的代码,我们可以直接通过 fun local start renderton.mofangdegisn.cn 在本地将函数启动,然后通过浏览器访问。

fun local start renderton.mofangdegisn.cn

演示效果如下:
Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染

6. 一键部署

当本地运行、调试确认没有问题了,就可以考虑部署到线上了。在部署前,要先将 template.yml 中的域名替换为自己的。

这里简单介绍下步骤:假如自己 Aliyun 的 AccountId 为 12345,那么就将自己的域名(国内集群需要备案)CNAME 到 12345.cn-shanghai.fc.aliyuncs.com,然后将自己的域名更新到 template.yml,执行 fun deploy 即可。更多详情可以参考这篇文档。

最后使用 fun deploy 一键部署即可。

Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染

总结

使用 Rendertron + 函数计算可以快速搭建一个可以直接用于生产的 Headless Chrome 渲染解决方案,以便于帮助网站更好的进行 SEO。

“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”