
什么是 SSR
SSR 顧名思義就是 Server-Side Render, 即服務(wù)端渲染。原理很簡單,就是服務(wù)端直接渲染出 HTML 字符串模板,瀏覽器可以直接解析該字符串模版顯示頁面,因此首屏的內(nèi)容不再依賴 Javascript 的渲染(CSR - 客戶端渲染)。
SSR 的核心優(yōu)勢:
- 首屏加載時間:因?yàn)槭?HTML 直出,瀏覽器可以直接解析該字符串模版顯示頁面。
- SEO 友好:正是因?yàn)榉?wù)端渲染輸出到瀏覽器的是完備的 html 字符串,使得搜索引擎
能抓取到真實(shí)的內(nèi)容,利于 SEO。
SSR 需要注意的問題:
- 雖然 SSR 能快速呈現(xiàn)頁面,但是在 UI 框架(比如 React)加載成功之前,頁面是沒法進(jìn)行 UI 交互的。
- TTFB (Time To First Byte),即第一字節(jié)時間會變長,因?yàn)?SSR 相對于 CSR 需要在服務(wù)端渲染出更對的 HTML 片段,因此加載時間會變長。
- 更多的服務(wù)器端負(fù)載。由于 SSR 需要依賴 Node.js 服務(wù)渲染頁面,顯然會比僅僅提供靜態(tài)文件的 CSR 應(yīng)用需要占用更多服務(wù)器 CPU 資源。以 React 為例,它的
renderToString()方法是同步 CPU 綁定調(diào)用,這就意味著在它完成之前,服務(wù)器是無法處理其他請求的。因此在高并發(fā)場景,需要準(zhǔn)備相應(yīng)的服務(wù)器負(fù)載,并且做好緩存策略。
什么是 Serverless
Serverless,它是云計(jì)算發(fā)展過程中出現(xiàn)的一種計(jì)算資源的抽象,依賴第三方服務(wù),開發(fā)者可以更加專注的開發(fā)自己的業(yè)務(wù)代碼,而無需關(guān)心底層資源的分配、擴(kuò)容和部署。
特點(diǎn):
- 開發(fā)者只需要專注于業(yè)務(wù),無需關(guān)心底層資源的分配、擴(kuò)容和部署
- 按需使用和收費(fèi)
- 自動擴(kuò)縮容
更詳細(xì)的有關(guān) Serverless 介紹,推薦閱讀:精讀《Serverless 給前端帶來了什么》
Serverless + SSR
結(jié)合 Serverless 和 SSR 的特點(diǎn),我們可以發(fā)現(xiàn)他們簡直是天作之合。借助 Serverless,前端團(tuán)隊(duì)無需關(guān)注 SSR 服務(wù)器的部署、運(yùn)維和擴(kuò)容,可以極大地減少部署運(yùn)維成本,更好的聚焦業(yè)務(wù)開發(fā),提高開發(fā)效率。
同時也無需關(guān)心 SSR 服務(wù)器的性能問題,理論上 Serverless 是可以無限擴(kuò)容的(當(dāng)然云廠商對于一般用戶是有擴(kuò)容上限的)。
如何快速將 SSR 應(yīng)用 Serverless 化?
既然說 Serverless 對于 SSR 來說有天然的優(yōu)勢,那么我們?nèi)绾螌?SSR 應(yīng)用遷移到Serverless 架構(gòu)上呢?
本文將以 Next.js 框架為例,帶大家快速體驗(yàn)部署一個 Serverless SSR 應(yīng)用。
借助 Serverless Framework 的 Nextjs 組件,基本可以實(shí)現(xiàn)無縫遷移到騰訊云的 SCF 上。
1. 初始化 Next.js 項(xiàng)目
$ npm init next-app serverless-next
$ cd serverless-next
# 編譯靜態(tài)文件
$ npm run build
2. 全局安裝 serverless cli
$ npm install serverless -g
3. 配置 severless.yml
org: orgDemo
app: appDemo
stage: dev
component: nextjs
name: nextjsDemo
inputs:
src: ./
functionName: nextjsDemo
region: ap-guangzhou
runtime: Nodejs10.15
exclude:
- .env
apigatewayConf:
protocols:
- https
environment: release
4. 部署
部署時需要進(jìn)行身份驗(yàn)證,如您的賬號未 登錄 或 注冊 騰訊云,您可以直接通過 微信 掃描命令行中的二維碼進(jìn)行授權(quán)登陸和注冊。當(dāng)然你也可以直接在項(xiàng)目下面創(chuàng)建 .env 文件,配置騰訊云的 SecretId 和 SecretKey。如下:
TENCENT_SECRET_ID=123
TENCENT_SECRET_KEY=123
執(zhí)行部署命令:
$ serverless deploy
以下
serverless命令全部簡寫為sls.
部署成功后,直接訪問 API 網(wǎng)關(guān)生成的域名,這里是就可以了。
類似
https://service-xxx-xxx.gz.apigw.tencentcs.com/release/這種鏈接。
現(xiàn)有 Next.js 應(yīng)用遷移
如果你的項(xiàng)目是基于 Express.js 的自定義 Server,那么需要在項(xiàng)目根目錄新建 sls.js 入口文件,只需要將原來啟動 Node.js Server 的入口文件復(fù)制到 sls.js 中,然后進(jìn)行少量改造就好,默認(rèn)入口 sls.js 文件如下:
const express = require('express');
const next = require('next');
const app = next({ dev: false });
const handle = app.getRequestHandler();
// 將原來的服務(wù)邏輯放入到異步函數(shù) `createServer()`中
async function createServer() {
// 內(nèi)部內(nèi)容需要根據(jù)項(xiàng)目需求進(jìn)行修改就好,基本是你的 `server.js` 的原代碼
await app.prepare();
const server = express();
server.all('*', (req, res) => {
return handle(req, res);
});
// 定義返回二進(jìn)制文件類型
// 由于 Next.js 框架默認(rèn)開啟 `gzip`,所以這里需要配合為 `['*/*']`
// 如果項(xiàng)目關(guān)閉了 `gzip` 壓縮,那么對于圖片類文件,需要定制化配置,比如 `['image/jpeg', 'image/png']`
server.binaryTypes = ['*/*'];
return server;
}
// export 函數(shù) createServer()
module.exports = createServer;
添加入口文件后,重新執(zhí)行部署命令 sls deploy 就 OK 了。
Serverless 部署方案的優(yōu)化
至此,我們已經(jīng)成功將整個 Next.js 應(yīng)用遷移到騰訊云的 Serverless 架構(gòu)上了,但是這里有個問題,就是所有的靜態(tài)資源都部署到了 SCF 中,這就導(dǎo)致我們每次頁面請求的同時,會產(chǎn)生很多靜態(tài)源請求,對于 SCF 來說同一時間并發(fā)會比較高,而且很容易造成冷啟動。而且大量靜態(tài)資源通過 SCF 輸出,然后經(jīng)過 API 網(wǎng)關(guān)返回,會額外增加鏈路長度,也會導(dǎo)致靜態(tài)資源加載慢,無形中也會拖累網(wǎng)頁的加載速度。
云廠商一般會提供云對象存儲功能,騰訊云叫 COS(對象存儲),用它來存儲我們的靜態(tài)資源有天然的優(yōu)勢。而且開始使用有 50GB!!! 的免費(fèi)容量(用來存喜愛的高清電影也是不錯的吧~)。
要是在我們項(xiàng)目部署時,將靜態(tài)資源統(tǒng)一上傳到 COS,然后靜態(tài)頁面通過 SCF 渲染,這樣既支持了 SSR ,也解決了靜態(tài)資源訪問問題。而且 COS 也支持 CDN 加速,這樣靜態(tài)資源優(yōu)化就更加方便。
那么我們?nèi)绾螌㈧o態(tài)資源上傳到 COS 呢?
普通青年做法
登錄 [騰訊云 COS 控制臺](https://console.cloud.tencent.com/cos5) -> 創(chuàng)建存儲桶 -> 獲取 cos 訪問鏈接 -> 構(gòu)建 Next.js 項(xiàng)目 -> 點(diǎn)擊 COS 上傳按鈕 -> 選擇上傳文件 -> 開始上傳 -> 完成
文藝青年做法
配置 COS 組件 -> 構(gòu)建 Next.js 項(xiàng)目 -> 執(zhí)行部署cos組件命令 -> 完成
接下來我們一起學(xué)習(xí)下文藝青年是如何做的。
在項(xiàng)目下創(chuàng)建 cos 文件夾,創(chuàng)建 cos/serverless.yml 配置文件:
org: orgDemo
app: appDemo
stage: dev
component: cos
name: serverless-cos
inputs:
# src 配置成你的next項(xiàng)目構(gòu)建的目標(biāo)目錄
src: ../.next/static
# 由于 next框架在訪問靜態(tài)文件會自動附加 _next 前綴,所以這里需要配置上傳 COS 的目標(biāo)目錄為 /_next
targetDir: /_next/static
bucket: serverless-bucket
region: ap-guangzhou
protocol: https
acl:
permissions: public-read
根據(jù) COS 訪問鏈接生成規(guī)則:
<protocol>://<bucket-name>-<appid>.cos.<region>.myqcloud.com
可以直接推斷出部署后的訪問 URL 為:https://serverless-bucket-1251556596.cos.ap-guangzhou.myqcloud.com
然后在項(xiàng)目更目錄新建 next.config.js 文件,配置 assetPrefix 為該鏈接:
const isProd = process.env.NODE_ENV === 'production';
module.exports = {
assetPrefix: isProd
? 'https://serverless-bucket-1251556596.cos.ap-guangzhou.myqcloud.com'
: '',
};
注意:如果你是直接給該 COS 配置了 CDN 域名,那么這里直接改成 CDN 域名就行。
然后執(zhí)行構(gòu)建:
$ npm run build
然后部署命令新增部署到 cos 命令執(zhí)行就好:
$ sls deploy --target=./cos && sls deploy
然后我們就可以耐心等待部署完成。
Serverless + Next.js 部署流程圖
優(yōu)化后項(xiàng)目整體部署流程圖如下:

起初雖然看起來步驟很多,但是項(xiàng)目配置一次后,之后部署,只需要執(zhí)行構(gòu)建和部署命令,
就可以了。
性能分析
依賴 Serverless Component, 雖然我們可以快速部署 SSR 應(yīng)用。但是對于開發(fā)者來說,性能才是最重要的。那么 Serverless 方案的性能表現(xiàn)如何呢?
為了跟傳統(tǒng)的 SSR 服務(wù)做對比,我專門找了一臺 CVM (騰訊云服務(wù)器),然后部署相同的 Next.js 應(yīng)用。分別進(jìn)行壓測和性能分析。
壓測配置如下:
| 起始人數(shù) | 每階段增加人數(shù) | 每階段持續(xù)時間(s) | 最大人數(shù) | 發(fā)包間隔時間(ms) | 超時時間(ms) |
|---|---|---|---|---|---|
| 5 | 5 | 30 | 100 | 0 | 10000 |
本文壓測使用的是 騰訊 WeTest。
頁面訪問性能對比
均使用 Chrome 瀏覽器
| 方案 | 配置 | TTFB | FCP | TTI |
|---|---|---|---|---|
| 騰訊云 CVM | 2 核,4G 內(nèi)存,10M 帶寬 | 50.12ms | 2.0s | 2.1s |
| 騰訊云 Serverless | 128M 內(nèi)存 | 69.88ms | 2.0s | 2.2s |
壓測性能對比
1.響應(yīng)時間:


| 方案 | 配置 | 最大響應(yīng)時間 | P95 耗時 | P50 耗時 | 平均響應(yīng)時間 |
|---|---|---|---|---|---|
| 騰訊云 CVM | 2 核,4G 內(nèi)存,10M 帶寬 | 8830ms | 298ms | 35ms | 71.05 ms |
| 騰訊云 Serverless | 128M 內(nèi)存 | 1733ms | 103ms | 73ms | 76.78 ms |
2.TPS:


| 方案 | 配置 | 平均 TPS |
|---|---|---|
| 騰訊云 CVM | 2 核,4G 內(nèi)存,10M 帶寬 | 727.09 /s |
| 騰訊云 Serverless | 128M 內(nèi)存 | 675.59 /s |
價格預(yù)算對比
直接上圖:

對比分析
從單用戶訪問頁面性能表現(xiàn)來看 Serverless 方案略遜于服務(wù)器方案,但是頁面性能指標(biāo)是可以優(yōu)化的。從壓測來看,雖然 Serverless 的 平均響應(yīng)時間 略大于 CVM,但是 最大響應(yīng)時間 和 P95耗時 均優(yōu)于 CVM 很多,CVM 的最大響應(yīng)時間甚至接近 Serverless 的 3倍。而且當(dāng)并發(fā)量逐漸增大時,CVM 的響應(yīng)時間變化明顯,而且越來越大,而 Serverless 則表現(xiàn)平穩(wěn),除了極個別的冷啟動,基本能在 200ms 以內(nèi)。
由此可以看出,隨著并發(fā)的增加,SSR 會導(dǎo)致服務(wù)器負(fù)荷越來越大,從而會加大服務(wù)器的響應(yīng)時間;而 Serverless 由于具有自動擴(kuò)縮的能力,所以相對比較平穩(wěn)。
當(dāng)然由于測試條件有限,可能會有考慮不夠全面的地方,但是從壓測圖形來看,是完全符合理論預(yù)期的。
但是從價格對比來看,接近配置的 Serverless 方案基本不怎么花錢,甚至很多時候,免費(fèi)額度就已經(jīng)可以滿足需求了,這里為了增加 Serverless 費(fèi)用,估計(jì)調(diào)大了調(diào)用次數(shù),內(nèi)存大小,但是即便如此,服務(wù)器方案還是接近 Serverless 方案的 10 倍!!!!!。
最后
寫到這,作為一名前端開發(fā),我的內(nèi)心是無比激動的。記得以前在項(xiàng)目中為了優(yōu)化首屏?xí)r間和 SEO,就做個好幾個方案的對比,但是最終因?yàn)楣具\(yùn)維團(tuán)隊(duì)的不夠配合,最后放棄了 SSR,最后選擇了前端可掌控的 預(yù)渲染方案?,F(xiàn)在有了 Serverless,前端終于不用受運(yùn)維的限制,可以基于 Serverless 來大膽的嘗試 SSR。而且借助 Serverless,前端還可以做的更多。
當(dāng)然真正的 SSR 并不止如此,要達(dá)到頁面極致體驗(yàn)我們還需要做很多工作,比如:
- 靜態(tài)資源部署到 CDN
- 頁面緩存
- 降級處理
- ...
但是這些無論是部署到服務(wù)器還是 Serverless,都是我們需要做的工作。并不會成為我們將 SSR 部署到 Serverless 的絆腳石。
如果你對 Serverless Component 開發(fā)感興趣,歡迎一起學(xué)習(xí)討論,也可以訪問 https://sls.plus 網(wǎng)站,找到你所需要的組件。