从零搭建 Node Faas(六)静态页面

静态页面是指内容在服务器端生成后不再改变的网页。这些页面通常是用 HTML、CSS 和 JavaScript 编写的,并且不依赖于服务器端处理或动态内容生成。静态页面的内容是固定的,所有用户访问时看到的内容都是相同的。 – ChatGPT

这篇文章就以 ChatGPT 给的定义为开头,来讲一下 Faas 是如何支持用户部署自己的静态网站功能。

经常可能会有一些用户需要部署自己的博客,文档站点,产品官网等等的需求。Faas 也希望可以提供这样的能力,让用户可以通过 Faas 部署自己的静态网站。

一、整体流程

1. 用户流程

对于用户来讲,整体的使用流程如下图所示:

用户在本地开发完成之后,将编译出来的产物上传到 Faas 平台,Faas 平台会将这些产物上传到 USS(对象存储)上,并且同时将这些产物的信息记录。

2. 请求流程

那么从请求的视角来看,一个 http://xxx/1/blog 的请求进来之后,在主进程中根据应用的信息,SPA 或者普通的静态页面,会有不同的处理方式。然后最后返回给用户从 USS 上下载的内容。

整体设计还是很简单的,也很好理解。下面来看一下具体的实现。

二、具体实现

下面这个是一段伪代码,只用于演示整体思路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
app.use(
"/:appId/:functionName",
async (req, res, next) => {
// Function
// if (xxx === 'function') {
// // ...
// }

// Static Page
const baseURL = path.join("/", appId, rootPath)
let filepath = req.originalUrl.replace(baseURL, "").replace(/^\//, "")
if (!filepath) {
filepath = "index.html"
}

res.setHeader(
"content-type",
mime.lookup(filepath) || "application/octet-stream"
)

const data = await safeGetFile(
path.join(faasStaticPath, appId, rootPath, filepath)
)
if (!data) {
return res.status(404).send("Not found")
}
;(data as any).pipe(res)
},
)

export async function safeGetFile(key: string) {
try {
const res = await client.getObject({
Bucket: bucket,
Key: key,
})
return res.Body
} catch (e) {
return null
}
}

核心就是从 USS 获取的文件流,然后通过 pipe 的方式返回给用户。当然这里面还有一些细节可能需要处理,比如 content-type 的设置,404 的处理等等。

这样 Faas 通过构建了一层用户和 USS 服务之间的桥梁,让用户可以通过 Faas 平台来部署自己的静态网站。

三、优化

当然这里还是有一些可以再进行优化的点的。比如缓存和代理。

1. 缓存

缓存很容易理解,也是面试中常见的问题,这里给它设置了一个 Last-Modified

1
res.setHeader('Last-Modified', new Date(res.locals.page.updateTime).toUTCString())

2. 代理

目前的设计还有一个问题,一个静态页面除了 Html 文件以外,可能会有很多个资源请求。请求进来走到 Faas 的主进程中,如果还要在主进程中去处理这些资源请求,会增加主进程的负担。比如每次需要查询数据库找到对应的信息,可以尝试优化解决这一部分问题。

这里加了一层网关,我们直接给我们的服务设置一层代理,当请求进来的时候,直接代理到 USS 上,这样就不需要走到主进程中,减少了主进程的负担。

如下代码所示,在我们的主进程中,对于非 Html 的请求,我们用 301 直接重定向到我们的服务,然后会被网关代理到 USS 上。

1
2
3
if (!key.endsWith('.html') && process.env.SERVER_ENDPOINT) {
return res.redirect(301, `${process.env.SERVER_ENDPOINT}${key}`)
}

这样就能减少主进程的负担。

四、总结

静态页面功能并非 Faas 的核心功能,但是同样重要。下一篇文章会讲一下 Faas 的本地调试能力,敬请期待。