当 SSR 赶上 FaaS

前言

为何 SSR ?

我所在的部门是阿里巴巴国际站,国际站是一个全球 B 类跨境贸易平台,平台服务的买卖家是来自于全球一百多个国家和地区,这些国家和地区的发展和贸易环境差别较大例如:一些亚非拉地区的网络环境和终端设备较差,网站性能在这些地区堪忧。同时因为 B 类贸易较 C 类更为严肃,很大部分地区(欧美)更习惯使用 WEB,网站很大部分流量来自于 SEO,针对于这种状况,咱们须要支持服务端渲染,来提升首屏性能,保证用户访问网站的体验和提高 SEO 流量。(下图是客户端渲染 CSR  和 服务端渲染 SSR 的首屏渲染差别)
javascript

前端 SSR 历程

传统的异构服务端渲染

互联网发展之初没有前端这一角色,早期的页面基本都是由后端一把梭.随着 ajax 的诞生,而替代传统 web 交互模式的 ajax 技术使得动态更新网页变成可能,因为页面交互的复杂度提高,基于专业性的要求致使先后端的分工,前端开始承接视图的开发工做。但页面容器仍是由传统的 PHP 和 java 等去承载:php

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
<link rel="stylesheet" type="text/css" href="xxx">
</head>
<body>
  <?php $searchQuery = $_GET['q']; ?>
  <h1>You searched for: <?php echo $searchQuery; ?></h1>
  <p>this is a demo</p>
</body>
</html>
复制代码

相似还有 ASP、JSP 等脚本语言,将页面的动态内容嵌入到页面中。css

BFF 架构前端接管 UI 层

随着 13 年 react 的出现并火热,其基于 vdom 的设计使UI逻辑和渲染分离,可以将一套代码多端渲染,至此拉开了前端框架的 SSR 之路,同时这一阶段随着前端工程化的不断发展,为了提高前端开发效率,传统的异构渲染服务已经成为开发效率的瓶颈。同时随着Sam Newman 提出了 Backends For Frontends ,根据服务自治的原则,前端为了开发的灵活性,开始承接整个 UI 层(接口和页面 SSR)
html

BFF SSR 困境

虽然 BFF 在业界和阿里有较多的实践和沉淀但仍是局限在一些特定领域。如阿里不少前台场景已有多年的 java 服务运行稳定,改形成本较高且风险大,落地不太现实,最终仍是在一些内部效能平台,卖家平台等场景有较多落地。同时加一层 BFF 还有如下一些挑战:
前端

渲染服务

为何提供渲染服务

回头来看咱们须要 SSR 的场景,大多都是流量较大,直面大量用户(买家)的场景,而这些业务场景不少只是部分页面须要有 SSR 的能力,同时前端没有能力所有使用 BFF 来承接流量。在这个过程当中,咱们也探索和实践其余方案:java 的js 引擎 nashorn 来提供 SSR 渲染,最后发现总体性能远低于 node ;同机部署 node 来支持 SSR ,同机部署和 java 进程的资源竞争和运维复杂度都带来了挑战。
基于以上缘由,咱们在思考,能不能把渲染能力做为一个服务,须要渲染模板(组件)直接调用返回 html 片断便可,不须要对业务应用侵入性这么强。java

渲染服务实现

肯定了问题后,渲染服务就须要提供一种轻量级的接入方式来知足不通场景的渲染诉求。那咱们就须要知足如下诉求:node

  1. 低成本开发&部署:咱们指望同构的代码不须要进行较大的改造,最好是零改造就能够直接支持渲染能力。
  2. 多模板引擎支持:因为不通业务方对渲染有本身的自定义诉求,除了单纯的 react 渲染之外,支持移动端的 rax 渲染,支持搭建场景的基于特定 schema 的渲染诉求。
    针对以上诉求,咱们提供了一个 RPC 服务,只要调用方提供相应的渲染基础信息和渲染数据,咱们便可返回渲染结果,配置信息以下:
{
  app: "silkworm-render-demo",        // 应用名
  loader: "webpack",                  // 加载方式
  module: "xxx/pages/demo/index.js", // 资源入口
  deps: [],                          // 依赖资源
  version: "0.0.1",
  engine: "react",                   // 渲染引擎
  props: {                           // 渲染数据
    count: 100,
  },
}
复制代码

当业务方调用咱们的渲染服务后,咱们根据资源描述信息从 CDN 或者静态资源服务器上拉取资源到服务器本地,而后根据不一样的加载器执行文件的加载和解析,以后根据用户选择的渲染引擎,执行不一样的渲染逻辑。整个架构图以下:
react

在保障性能和稳定性上:咱们针对资源作了本地文件和内存级别的缓存策略,同时针对不通页面渲染作了基于 vm 沙箱的渲染隔离,提供单独的上下文兼容 window 等客户端书写问题,同时作了超时处理webpack

渲染服务效果&问题

渲染服务上线后,咱们支持了六七条业务线,囊括了大部分买家场景的渲染诉求,同时也支持了邮件推送的诉求(离线渲染邮件模板触达用户),总体运行平稳,主要效果仍是在于如下几点:web

  1. 接入成本低:前端应用(react)基本没有改形成本,只须要后端调用渲染服务回填页面便可。
  2. 稳定性
  • 非侵入:服务做为能力提供方,即便出现问题也不影响业务应用的运行。
  • 运维保障:提供了统一的监控、日志、错误处理能力,来保障渲染稳定性。

但渲染服务实际上是一个中心化服务,随着应用的接入愈来愈多,中心化的风险愈来愈高,接入方的声音也愈来愈多:

  • 页面流量大了会不会撑不住?
  • 应用多了会不会互相影响?
  • 能不能定制加载策略、缓存优化?

    虽然渲染服务下降了接入和运维的成本,可是还有一些问题没法解决:
  • 弹性扩容
  • 容器隔离
  • 灵活定制

SSR 赶上 FaaS

serverless 简介

无服务器架构(Serverless architectures)是指一个应用大量依赖第三方服务(后端即服务,Backend as a Service,简称“BaaS”),或者把代码交由托管的、短生命周期的容器中执行(函数即服务,Function as a Service,简称“FaaS”)。serverless 潜在的几个优势:

  • (理论上)无限可用的计算资源
  • 用户不再须要承担服务器运维的工做和责任
  • 服务的按需付费成为可能
  • 超大型数据中心的使用成本显著下降
  • 经过资源虚拟化管理,运维操做的难度大大下降
  • 得益于分时复用,物理硬件的利用率大大提升

以 web 应用为例,如下是一个 serverless web 的架构:

渲染服务迁移 FaaS

针对 serverless 的特色,结合以前提到的咱们渲染服务在伸缩性上和渲染容器的隔离性上的不足,咱们开始着手改造将渲染服务迁移至 FaaS 平台,在迁移前咱们评估了渲染服务和 FaaS 在如下几点十分契合:

  • 基于事件触发是非长连接的服务处理
  • 渲染自己是纯粹的 CPU 计算,无状态服务
  • FaaS 的伸缩性很适合调用量波动较大的渲染服务
    基于以上的特色,咱们将不通的引擎进行 FaaS 部署提供对外服务,总体架构以下:

在迁移以后,咱们借助 FaaS 的扩缩容能力,能够有效的提升资源利用率,下降成本,同时下降了邮件离线渲染对在线渲染服务的影响。

FaaS SSR 一体化应用

那除了已有的页面想快速接入 SSR 能力,像已有的 BFF 应用,或者但愿能借助  serverless 来承接整个 UI 层的前端,咱们该如何借助 FaaS 来改造升级。
以 BFF 应用为例,咱们前端承接了客户端的开发同时也负责服务端视图层接口的开发工做,咱们能够将服务端的接口以 FaaS 形式进行部署,减小运维成本。而须要 SSR 的页面咱们也发布到 FaaS 平台,以 page as function 的形式进行部署,开发和构建以下:


当页面和接口发布后咱们经过网关层,来路由页面到不一样的页面模板和 function 来提供 SSR 渲染和接口服务。

# 应用的网关配置
basePath: /demo
appName: demo

# 应用的路由
router:
	render:
    # 页面内容ssr
 path: /render/home-list   ## 页面内容 ssr
 method: get
 parameters:
 - name: page
 in: path
    # 接口的具体实现
 integration:
 type: function
 uri: render
 getPage:
    # 页面的 http 
 path: /home
 method: get
 integration: 
 type: template
 uri: 
 body:
          $ref: template/index.ejs
复制代码

最终,咱们的用户请求 SSR 渲染路径以下:

更多探索

除了现有的 FaaS 平台外,像阿里云提供的 EdgeRoutine(ER):支持在CDN边缘执行客户编写/编译的JavaScript(WebAssembly),也能够支持 SSR 在边缘节点进行渲染,可以更快的返回给用户页面内容。

因为自己边缘节点对运行耗时有更短的限制,同时受限的 js 运行时(service worker)对 node 开发不太友好,并且边缘节点渲染比服务端渲染优点没有那么明显,暂时不太建议在边缘节点上作 SSR。

可是咱们能够经过将页面容器发布到边缘节点,当用户访问时上将页面非 SSR 部分流式返回给用户,同时向服务端发起 SSR 请求,最终返回给用户页面。这样好处是:让用户更早看到页面部份内容,同时资源的请求和 SSR 渲染是并行执行,能更快的提高首屏速度。

以上升级改造在进行中

总结

回到 SSR 初衷,本质上咱们仍是为了给用户更好的交互体验,如更好的性能。随着 serverless 相关技术的不断发展,更轻量级的业务开发变成可能,须要咱们更深刻业务场景,提供贴合深刻的体验优化。共勉

相关文章
相关标签/搜索