Loading...
墨滴

Gaohonglong

2021/10/21  阅读:71  主题:自定义主题1

10.21

简介

服务端渲染(Server Side Renderinig,SSR)是一项可以在服务端预先生成页面的 DOM 结构(而不是在浏览器端生成)的技术。SSR 的优势就是容易 SEO,首屏加载快,对搜索引擎友好,因此 SSR 适合于重页面内容而非交互的页面,比如新闻站点和大型活动页面等。SSR 对这些项目的用户体验有极大的提升。但世界上没有免费的午餐,SSR 伴随着一些不可避免的缺点,包括维护成本增加、服务器成本提升、单服务器能承受的 QPS 降低、技术复杂度提高等。

下面是通过压测初步得到的一些 SSR 服务在接口时延不大幅增长情况下的单机 QPS 承载能力:

项目
机器配置
粗估 QPS 承载量
基准项目*
1c2g
60
next.js 基准项目*
1c2g
25
业务A 官网
4c8g
22
业务A 重构版官网
4c8g
44
业务B 歌曲回流页
1c2g
34

注:

  1. 基准项目采用公司内部的 React 前端脚手架初始化,渲染的页面包含 5000 个 div 的 dom 节点,通过 Node 中间件__提供服务

  2. Next.js 基准项目通过 Next.js 脚手架初始化,渲染的页面包含 5000 个 div 的 dom 节点,通过 next start 指令提供服务

我们可以发现在实际业务场景中 SSR 服务的单机 QPS 承载能力确实是比较低,当服务流量突增的时候会导致 SSR 集群承担比较大的压力。

SSR 是服务中最核心的接口,一旦挂了就会直接导致网站不可访问,为了提升 SSR 服务的稳定性,我们需要一套通用的 SSR 降级到 CSR 的方案,来保证 SSR 集群能提供足够大的弹性来应对突增的流量,保证核心服务的可用性,并且可以方便的在现有的 SSR 服务中快速落地。

降级方案选型

Node 中间件降级

这是最便捷的一种降级方案,但是一般不建议采用通过直接在 Node 服务中增加中间件处理降级的方式来实现。原因是到当服务异常时,Node 业务进程本身就无法及时的响应请求,那相关的降级策略也就失效了。

比如,我们通过一个定时器定时获取当前 CPU 的使用率,通过 CPU 阈值进行 CSR 降级的方案,当 Node 进程本身 CPU 负载高的时候,定时器本身可能就无法及时响应回调了。

Nginx 配置兜底

通过 Nginx 上的 error_page 配置,在 ssr 的路由响应出错的时候进行处理,返回 csr 的结果

优:实现较为简单,Nginx 作为最外层的兜底,可以兜住大部分异常的场景。

劣:需要一个额外的 CSR 集群用于在异常时进行响应,并且无法做更多定制化的处理。

具体实现

注:项目需要支持 CSR 和 SSR 同构

  1. 新建一个 CSR 集群,只负责响应服务的 HTML 静态资源(或者配置一个兜底的 CDN 域名)。
  2. 在 Nginx 上针对对应的服务端渲染接口的路由,配置 error_page 规则。
  3. 新建一个兜底路由 @fallback,对应的后端服务集群选择新建的 CSR 集群/兜底的 CDN 域名。

独立网关降级

通过一个独立的网关服务处理 CSR/SSR 流量的调度,在 SSR 服务不可用或超时情况下采用 CSR 的兜底。

请求流转流程如下:

优:拥有最大的自由度,可以将大部分服务治理的策略(熔断,限流等)添加到网关服务。

劣:请求链路额外增加了中心化的网关服务,大幅增加部署模型复杂度,同时需要引入网关服务自身额外的运维成本。

分布式网关降级

去中心化的 Sidecar 分布式网关与业务进程部署在同一个实例,所有的客户端流量都通过网关接入业务服务,在网关层处理所有的非业务功能,如通用参数解析、协议转换等。目前我们的服务都接入了 Sidecar 分布式网关。

优:由于业务流量都会先经过分布式网关再进入业务服务,因此在 sidecar 中配置降级逻辑可以以业务无感知的方式低成本的配置部署,对业务代码透明,无侵入,且现有服务都已经接入分布式网关,接入方式简单,不需要单独部署服务,不影响现有的上线流程。

劣:分布式网关的处理逻辑依然会占用业务 CPU 资源,对整体性能有一定的影响。

分布式网关降级方案

考虑到可扩展性和升级成本,最终我们选择通过分布式网关实现通用的 SSR 降级。分布式网关支持以 Loader 的形式加载自定义的逻辑,因此我们开发并发布了一个包含降级方案的自定义 Loader 。通过在对应的服务中加载 Loader 即可完成降级方案的接入。

处理流程

其中 LB 为前置的负载均衡层,流量通过 LB 转发到 Sidecar 分布式网关,再由 Sidecar 传给业务服务 SSR_Service。

业务改造

业务改造只需要保证服务是 SSR 和 CSR 同构的,在服务降级到 CSR 的模式下可以正常的运行即可,分布式网关的接入应该对于业务是无感知的。

自适应限流

在自定义 Loader 中,我们通过一个滑动窗口动态计算 CPU 的负载来进行限流,核心目的是在流量高峰期避免单实例由于 CPU 过载而引发宕机事故。

对于 SSR 服务而言,在 CPU 超过阈值触发自适应限流时不直接返回错误的状态码而是降级到 CSR 的 HTML 页面,这样可以大幅降低 CPU Load,显著提升服务的 QPS 承载能力。

主动降级

我们支持通过全局的配置开关或者添加特定 query 的方式主动触发 Sidecar 的 SSR 降级,从而支持在特殊场景下的主动降级。

MPA/SPA 模式支持

业务形态是多种多样的,可能是多页应用也可能是单页应用。对于单页应用,只需要直接降级到用户配置的 HTML 页面路径即可;对于多页应用,我们额外增加了一个路由配置文件来提供不同路由到对应的降级 HTML 的路由的映射关系。

示例的路由配置格式如下:

// ssrfallback.config.json
{
  "routes": [
    {
      "path""^/track(/\\S)?",
      "file""./track.html"
    },
    {
      "path""^/album",
      "file""./album.html"
    }
  ]
}

缓存

由于 Sidecar 会随着业务实例的重启而重启,并且需要加载的降级 Html 数量是可控的,因此可以放心的对读取的 HTML 文件添加内存缓存,减少文件 IO 的消耗。

性能测试

项目
机器配置
无降级 QPS 上限
添加降级后 QPS 上限
有效 SSR QPS
基准项目*
1c2g
60
1500
36
Next.js 基准项目
1c2g
25
1000+
14
业务A 重构版官网
4c8g
44
1000+
30
业务B 歌曲回流页
1c2g
34
1000+
24

其中 有效 SSR QPS 指的是在触发降级后,可以基本维持的 SSR 渲染的 QPS,大致在加压 QPS = 500 左右的情况下测得,当加压的 QPS 进一步上升时,有效 SSR QPS 会逐步降低,直到服务无法响应。

性能分析

以基准项目的结果为例

由于接入了自适应限流的组件,在 SSR 场景下,当 CPU 过载时可以自适应切换到 CSR 模式,降低 CPU 负载,从而保证可以处理 QPS 激增的场景,最大的 QPS 承载可以到 1500QPS 左右(此处只展示了到 500 的 QPS)。

观察响应延迟可以发现整体的延迟水平较低,只有在刚触发自适应限流的一段时间内由于 CPU 压力会有一段时间较高的延迟,后续可以平稳的保持正常的响应。

通过打点统计,SSR 非降级请求的 QPS 可以基本稳定在 30+,在高负载的情况下,依然可以充分利用实例的性能,完成一定程度的 SSR 渲染请求,而不会全量降级为 CSR。

有效 SSR QPS,随着加压 QPS 逐步增加会有一定降低,发压 QPS 稳定后,SSR 的 QPS 也基本稳定
降级 QPS,随着压测 QPS 升高逐步升高

其中 99.6% 以上的请求都能在 800ms 之内返回。

小结

为了提升 SSR 服务的稳定性,我们选择在分布式网关上实现了 SSR 降级的逻辑来提供通用的基于分布式网关的 SSR 降级能力。通过在多个业务场景的 SSR 服务的实践和性能测试,本方案可以保证 SSR 集群在流量突增的情况下,能够基于当前的 CPU 资源动态的降级一定比例的请求到 CSR,大幅提升 SSR 服务集群的承载能力,并且对于业务的改造成本较低,方便快速落地接入。

Gaohonglong

2021/10/21  阅读:71  主题:自定义主题1

作者介绍

Gaohonglong