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 服务。

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

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

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

我们配置了 100 并发,测试 6 分钟,每分钟并发按照 20% 递增的规则进行压测。
我们要测试的网址网站为:http://renderton.mofangdegisn.cn/render/https://www.example.com/
该网址表示让 rendertron 请求 https://www.example.com/ 这个网站的内容,并返回渲染结果。
测试概览如下:

从上面的概览可以看到,由于会发生从函数到 https://www.example.com/ 的网络请求,所以最小延迟为 1106ms,99% 的请求可以在 2011ms 完成,90% 的请求可以在 1347ms 完成,75% 的请求可以在 1201ms 完成,50% 的请求可以在 1156ms 完成。我们是每分钟按照 20% 的并发递增,当并发增加时,函数计算会遇到冷启动,冷启动最大时间为 32261ms(可以使用预热、预留等方式可以缓解或完全免除冷启动的影响)。
在未优化的场景下,我们的压测结果也达到了 44.91 的 TPS,这对于大部分网站是绝对能够满足需求的。
压测明细如下:

上面箭头所指的位置表示并发突然增加,函数自动扩容时会遇到一些冷启动,当扩容完毕,后续的请求就非常平稳了。
部署步骤
将 Rendertron 部署到传统的 ECS 或者物理机上作为生产服务,并不是件容易的事。除了 Rendertron 本身需要一些安装配置外,还需要考虑当流量增加时如何扩容,以及配置搭建反向代理或负载均衡等与之配套的服务。
下面,我们介绍下函数计算如何解决这些问题的。
参考链接
- Fun 安装教程 可以直接在这里下载二进制版本的 Fun,解压后即可使用。
- 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
演示效果如下:

6. 一键部署
当本地运行、调试确认没有问题了,就可以考虑部署到线上了。在部署前,要先将 template.yml 中的域名替换为自己的。
这里简单介绍下步骤:假如自己 Aliyun 的 AccountId 为 12345,那么就将自己的域名(国内集群需要备案)CNAME 到 12345.cn-shanghai.fc.aliyuncs.com,然后将自己的域名更新到 template.yml,执行 fun deploy 即可。更多详情可以参考这篇文档。
最后使用 fun deploy 一键部署即可。

总结
使用 Rendertron + 函数计算可以快速搭建一个可以直接用于生产的 Headless Chrome 渲染解决方案,以便于帮助网站更好的进行 SEO。
“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”
Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染解决方案的更多相关文章
- Headless Chrome:服务端渲染JS站点的一个方案【上篇】【翻译】
原文链接:https://developers.google.com/web/tools/puppeteer/articles/ssr 注:由于英文水平有限,没有逐字翻译,可以选择直接阅读原文 tip ...
- Rendertron:谷歌 Chrome 新的 headless 模式又贡献了一个新的技巧
摘自:https://zhuanlan.zhihu.com/p/31670033 Rendertron:JavaScript Web 富应用的一个老问题是如何使这些页面的动态渲染部分可供搜索引擎检索. ...
- Headless Chrome:服务端渲染JS站点的一个方案【中篇】【翻译】
接上篇 防止重新渲染 其实说不对客户端代码做任何修改是忽悠人的.在我们的Express 应用中,通过Puppteer加载页面,提供给客户端响应,但是这个过程是有一些问题的. js脚本在服务端的Head ...
- 使用 Headless Chrome 进行页面渲染 - 知乎专栏
使用 Headless Chrome 进行页面渲染 - 知乎专栏 使用 Headless Chrome 进行页面渲染 - 知乎专栏 这里我们使用 chrome-remote-interface 来远程 ...
- Web自动化之Headless Chrome编码实战
API 概览 && 编码Tips 文档地址 github Chrome DevTools Protocol 协议本身的仓库 有问题可以在这里提issue github debugger ...
- PuppeteerSharp: 更友好的 Headless Chrome C# API
前端就有了对 headless 浏览器的需求,最多的应用场景有两个 UI 自动化测试:摆脱手工浏览点击页面确认功能模式 爬虫:解决页面内容异步加载等问题 也就有了很多杰出的实现,前端经常使用的莫过于 ...
- Web自动化之Headless Chrome概览
Web自动化 这里所说的Web自动化是所有跟页面相关的自动化,比如页面爬取,数据抓取,页面内容检测,页面功能测试,页面加载性能测试,页面回归测试等等,当前主要由如下几种解决方式: 文本数据获取 这就是 ...
- Puppeteer: 更友好的 Headless Chrome Node API
很早很早之前,前端就有了对 headless 浏览器的需求,最多的应用场景有两个 UI 自动化测试:摆脱手工浏览点击页面确认功能模式 爬虫:解决页面内容异步加载等问题 也就有了很多杰出的实现,前端经常 ...
- Selenium及Headless Chrome抓取动态HTML页面
一般的的静态HTML页面可以使用requests等库直接抓取,但还有一部分比较复杂的动态页面,这些页面的DOM是动态生成的,有些还需要用户与其点击互动,这些页面只能使用真实的浏览器引擎动态解析,Sel ...
随机推荐
- android 点击无效验证
背景 在写一个东西滑动删除列表的时候,出现了一个问题.我的需求是,左滑然后出现delete,然后点击delete,让该滑块消失. 我在点列表的第一行的时候,左滑,出现delete,点击删除,ok的,完 ...
- luogu P1731 [NOI1999]生日蛋糕 |暴力枚举
题目背景 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层 生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1<=i<=M)层蛋糕是半径为Ri, 高度为Hi的圆柱 ...
- 使用java理解程序逻辑 试题分析
1.编译Java Applet源程序文件产生的字节码文件的扩展名为() A:.java B..class C:Html D:Exe 正确答案:B 试题分析: 本题考查的是Java程序的开发过程.J ...
- [TimLinux] myblog 首页创建
1. 设计 2. 结构 3. 实现 templates/common/layout.html: <!DOCTYPE html> <html lang="zh"&g ...
- 如何正确的使用Python解释器?你之前肯定用错了
作为python开发者,当我们编写Python代码时,我们得到的是一个包含Python代码的以.py为扩展名的文本文件.要运行代码,就需要Python解释器去执行.py文件.由于整个Python语言从 ...
- moment.js 默认使用服务器时间
在前端使用Date对象获取当前时间的时候,该时间是客户端的时间.但是该时间可以被用户修改,所以我们一般情况下并不想要这个时间.如果每一次获取时间的时候都请求一下服务器,那么将会对服务器造成不必要的压力 ...
- 如何在Tomcat服务器配置CGI运行Python
想通过请求触发部署在tomcat上的非java应用程序,需要用到Common Gateway Interface(CGI).Tomcat提供了Servlet CGI支持. 修改web.xml web. ...
- Linux之find命令操作技巧
一.前言 工作中,我们常用find命令查找某些文件或者删除一些旧的日志文件,所以学会find命令的操作技巧就极为重要. 二.find常用参数选项 -depth 从指定目录下最深层的子目录开始查找 -m ...
- Jenkins + Docker + dockerfile-maven-plugin + Harbor CI/CD spring-boot项目的最轻量级配置
说明 本文只为方便日后查阅,记录一些关键性的步骤和踩坑的情况. dockerfile-maven-plugin构建镜像配置 <plugin> <groupId>com.spot ...
- NodeJS4-4静态资源服务器实战_优化引入模板引擎
引入模板引擎(handlebars) cnpm i handlebars 结构大概是这样子的,新建模板dir.tpl文件和route.js dir.tpl <!DOCTYPE html> ...