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 ...
随机推荐
- .NET进阶篇06-async异步、thread多线程4
知识需要不断积累.总结和沉淀,思考和写作是成长的催化剂 梯子 一.锁1.lock2.Interlocked3.Monitor4.SpinLock5.Mutex6.Semaphore7.Events1. ...
- Spring Cloud系列-Zuul网关集成JWT身份验证
前言 这两三年项目中一直在使用比较流行的spring cloud框架,也算有一定积累,打算有时间就整理一些干货与大家分享. 本次分享zuul网关集成jwt身份验证 业务背景 项目开发少不了身份认证,j ...
- php踩过的那些坑(5)浮点数计算
一.前方有坑 php在使用加减乘除等运算符计算浮点数的时候,经常会出现意想不到的结果,特别是关于财务数据方面的计算,给不少工程师惹了很多的麻烦.比如今天工作终于到的一个案例: $a = 2586; $ ...
- spring boot 一个项目启动多个实例
0.前言 在开发中,我们经常需要以不同端口启动同一个项目的多个实例,IDEA中启动多个实例很简单 1.方法 1.1.在项目中,选择编辑配置,然后点选允许并行运行,如下图: 1.2.调出RunDashb ...
- 神奇的 SQL 之 联表细节 → MySQL JOIN 的执行过程(一)
开心一刻 我:嗨,老板娘,有冰红茶没 老板娘:有 我:多少钱一瓶 老板娘:3块 我:给我来一瓶,给,3块 老板娘:来,你的冰红茶 我:玩呐,我要冰红茶,你给我个瓶盖干哈? 老板娘:这是再来一瓶,我家卖 ...
- 全面解析JVM加载中初始化的时机
JVM类加载过程 JVM类加载过程分为几个阶段,分别是加载.验证.准备.解析和初始化.加载是把二进制字节码载入内存,验证是校验字节流中包含的信息是否符合当要求,准备是为静态变量分配内存并设置静态变量初 ...
- ActiveMQ配置策略
1.消息发送 1.异步发送 消息生产者使用持久(persistent)传递模式发送消息的时候,Producer.send() 方法会被阻塞,直到 broker 发送一个确认消息给生产者,这个确认消息暗 ...
- KETTLE单表同步,写入EXCEL和TXT
以下操作都在5.0.1版本下进行开发,其余版本可以进行自动比对 在平时工作当中,会遇到这种情况,而且很常见.比如:1.自动生成文件TXT或者EXCEL(电信行业该需求居多),上传至某服务器:2.双方数 ...
- Java修炼——IO流的概念以及其分类
IO流的基本概念: 流的原理: 1) 在 Java 程序中,对于数据的输入/输出操作以"流" (stream) 方式进行: 2) J2SDK 提供了各种各样的"流&quo ...
- MyBatis三个查询方法_selectList_selectOne_selectMap
mybatis-cfg.xml的配置: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE co ...