雲開發 For Web:一站式開發下一代 Serverless Web 應用

近兩年來,Serverless 無疑是前端圈裏最火熱的話題之一,在各種技術峯會、各種技術文章裏都能看到它的身影。如果你是一名前端開發者,一定很奇怪:

“我們這些前端切圖仔,爲什麼要關注 Serverless 這種雲計算的概念?”

我們就從這個話題開始聊起吧。

爲什麼前端開發者要關注 Serverless ?

這個問題用一句話回答就是:

“Serverless 解放了端開發者(不僅僅是 Web 開發者)的生產力,讓端開發者可以更快、更好、更靈活地開發各種端上應用,不需要投入太多精力關注於後端服務的實現。”

下面我們就來慢慢解釋這句話。

在前端的史前時代,那個時候甚至還沒有”前端工程師“這個職位分類,所有人都是後臺開發,所有的網頁和 Web 應用都是來自於後臺渲染,CGI、PHP (甚至你可能都沒聽說過的 Perl)便是當時 Web 技術的代名詞,那個時候的 JS 和 CSS,不過是“切圖仔”在網頁裏寫動效和彈窗的小玩具而已。

後來,AJAX 技術出現了,最早在 Outlook Web Access 中出現,隨後隨着 Google Map、Gmail 等大型 Web 應用的實踐,逐步爲人所知。這項技術讓頁面內的 JS 也能異步地向服務器發起各種請求,並且把數據渲染到頁面上。這個時候,前端工程師們開始接管視圖層邏輯:

再後來,Node.js 誕生,大大降低了前端開發者開發一個後臺服務的難度,這也讓前端開發者逐漸接管了接管了渲染邏輯,在這期間,各大前端框架(代表性的 React、Vue)的服務器端渲染也逐步成熟。

既然能用 Node.js 來做服務端渲染,那麼拿 Node.js 來寫後臺業務邏輯、實現各種 HTTP API 當然也不在話下,所以在一些公司裏,前端接管業務邏輯,後臺只負責各種底層微服務的架構就出現了,這也是目前很多大公司在實行的架構:

細心的你可能已經注意到了,在這個時候,渲染、HTTP API、後臺業務邏輯這些東西,從端的角度看是屬於服務端的,但是從分工角度看卻屬於前端開發的範疇,這就是 BFF(Backend for Frontend)的概念,它的優勢在於:

  • 把核心業務邏輯完全交給前端工程師,讓業務邏輯可以更加靈活快速地變更;
  • 讓後端工程師可以更加關注於海量服務的穩定性和可靠性,提升服務質量。

這就是爲什麼大公司的很多業務,都開始越來越多地招 Node.js 全棧工程師的原因。

但是 BFF 並不是銀彈,它還是有一定問題的,比如在國內通曉前後端開發的全棧工程師太少,很難撐起大體量的業務開發。而且國內大多數前端工程師缺少服務端開發的經驗,讓他們運維上百臺服務器和一整套海量服務,有點強人所難:

  • 每個服務器實例應該有多少核?多少內存?
  • 怎麼優化 Linux 網絡棧的各種參數?
  • 服務器宕機怎麼辦?雙活、多活、同城、異地怎麼搞?

而 Serverless 正是幫助前端工程師解決運維開發 BFF 的良藥。

Serverless 這個單詞,直譯成中文的話,應該是“無服務器”。當然,這裏的“無服務器”絕對不是說不需要任何服務器資源了,而是說不需要關心服務器的具體運維和管理,只需要寫代碼然後發佈即可。

它包含了 FaaS(函數即服務)和 BaaS(後端即服務)兩大塊功能,把各種基礎設施進行了抽象,即使不熟悉後端的開發者,也能快速高質量地開發出海量、穩定的後端服務,而這對於前端工程師維護 BFF 服務來說,幾乎是量身定做的。

enter image description here

舉一個最簡單的例子,你現在需要上線一個新的 HTTP API(比如 /getUser?id=123),如果使用 Serverless 服務的話,有多快呢?

你只需要寫下面這個雲函數:

module.exports = async function(events, context) {
     const { id } = events.query.id
     const userInfo = await fetchUserInfo(id) // 調用後端微服務,拉取用戶信息
     return userInfo
}

然後發佈這個雲函數(假設命名爲 getUser),並且爲它設置一條路由:

cloudbase service:create -f getUser -p /getUser

然後你就可以通過 https://xxx.com/getUser 來拉取數據了,同時還附贈海量彈性伸縮、異地多活、日誌監控等多方位的能力。

如果你覺得這樣很酷炫,那麼不如來試一試 雲開發 Cloudbase 吧!

雲開發是什麼?

雲開發(Cloudbase)是騰訊雲 TCB 團隊(Tencent Cloudbase)出品的雲端一體化產品方案,爲廣大的小程序、Web、移動端開發者提供一站式的 Serverless 服務。

當然這是官方的說法,用人話講就是:

用了雲開發,各個端的開發者就可以一站式地解決後端服務問題了!

實際上早在 2018 年,雲開發就聯合微信團隊推出了「小程序·雲開發」,如果你對它還不怎麼了解,可以看這兩篇文章:

而現在,雲開發 For Web 也正式上線了!

雲開發有哪些能力?

雲開發提供了雲數據庫、雲函數、雲存儲、用戶登錄體系等一系列的後端能力,並且提供了各端的 SDK,讓各個端的開發者能夠基於這些能力,快速、優質地開發出功能豐富的應用。

Talk is cheap, Show me the code!

口說無憑,我們還是來直接看代碼吧!

雲數據庫

雲開發提供了一個文檔型的 NoSQL 數據庫,與傳統的雲上數據庫不同的是,雲開發的數據庫可以在各種客戶端內使用 SDK 直接進行讀寫,比如 Web 應用、小程序內、Flutter 客戶端等等

下面我們以 Web 應用爲例,展示雲數據庫的一系列功能。

基礎讀寫

CURD是數據庫最基礎的功能,雲開發 SDK 提供了一套鏈式調用接口,對數據庫進行讀寫:

// 使用 Web 端 SDK
const cloudbase = require('tcb-js-sdk')
const app = cloudbase.init({ /* 初始化... */ })

const db = app.database()

// 插入文檔
await db.collection('messages').add({
    author: 'stark',
    message: 'Hello World!'
})

// 查詢文檔
const data = await db.collection('messages').where({
    author: 'stark'
}).get()

// 更新文檔
await db.collection('messages').where({
    author: 'stark'
}).update({
    message: 'Hi, Cloudbase!'
})

// 刪除文檔
await db.collection('messages').where({
    author: 'stark'
}).remove()

聚合搜索

普通的查詢可能無法滿足一些複雜的需求,比如聯表、group等等。

雲開發針對這些複雜的查詢場景,推出了聚合搜索的功能,把一系列操作抽象爲一個管道(pipeline),單次執行即可,避免了多次讀的性能問題,我們以 group 操作爲例:

const $ = db.command.aggregate
const result = await db
    .collection('message')
    .aggregate()
    .group({
        // 以 author 字段作爲 key,統計相同 author 的數量
        _id: '$author'
        messagesCount: $.sum(1)
    })
    .end()
    //=> { "_id": "stark", messagesCount: 12 }

更多的聚合搜索功能,可以參考:Aggregate | 雲開發 Cloudbase

事務

在訂票、預約、轉賬等等場景下,開發者可能會要求數據庫能夠保證一連串讀寫的原子性,避免出現競爭條件,這就是數據庫事務的使用場景。

雲開發數據庫當然也提供了事務功能:

// 啓動事務
const transaction = await db.startTransaction()

// 在事務內讀
const data = await transaction.collection('messages')
    .where({ /* <query> */})
    .get()

// 在事務內寫
await transaction.collection('messages')
    .where({ /* <query> */})
    .update({ /* <data> */})

// 提交事務
await transaction.commit()

實時推送

在實時聊天室、實時數據看板等等場景下,我們經常會需要訂閱數據庫的更新,從而實現實時數據推送

雲開發的數據庫提供的 watch() 方法就是爲此設計的,每當數據庫符合條件的文檔發生變化時,都會觸發 onChange 回調,示例代碼如下:

await db.collection('messages')
    .where({/* <query> */})
    .watch({
        onChange: snapshot => {
            console.log("收到snapshot!", snapshot)
        },
        onError: error => {
            console.log("收到error!", error)
        }
    })

更多信息可以參考:數據庫實時推送 | 雲開發 Cloudbase

雲函數

所謂的雲函數,便是在雲端運行的、事件驅動的一段代碼,它可以被 SDK 調用,也可以直接通過 HTTP 調用,還可以設置定時器讓它定期運行:

// sum.js
module.exports = async function(events) {
    return events.a + events.b
}

這一小段代碼很簡單,但是隱藏在它之下的卻是一整套龐大的 FaaS(函數即服務)基礎設施,提供了諸如彈性伸縮、日誌、監控告警等多方面的能力。

使用調用雲函數

使用雲開發的客戶端 SDK,可以輕而易舉地在各個端上調用雲函數,我們以 Web 應用爲例:

const cloudbase = require("tcb-js-sdk");
const app = cloudbase.init({/* 初始化 */});

app.callFunction({
    // 雲函數名稱
    name: "sum",
    // 傳給雲函數的參數
    data: {
      a: 1,
      b: 2
    }
  })
  .then(res => {
    console.log(res); // 輸出 "3"
  })
  .catch(error);

HTTP 訪問

你也許會覺得 SDK 體積龐大,太沉重了,那麼你也可以選擇使用 HTTP 來調用雲函數,響應 HTTP 請求。

// hello.js
module.exports = function() {
    return 'Hello, World!'
}

然後我們直接通過命令行發佈這個函數,併爲它創建一條路由:

$ cloudbase functions:deploy hello
$ cloudbase service:create -f hello -p /hello

隨後便可以通過 url 直接訪問這個雲函數:

$ curl https://xxx.service.tcloudbase.com/hello
Hello, World!

具體可以參考:https://docs.cloudbase.net/service/quick-start.html

在雲函數內部使用服務端 SDK

在 Cloudbase 的雲函數內,你可以直接使用 Node.js SDK,無需在初始化的時候額外注入祕鑰:

const cloudbase = require('@cloudbase/node-sdk')

// 無需使用服務端祕鑰
const app = cloudbase.init()

const data = await app.database().where().get()

更詳細的內容可以參考:https://docs.cloudbase.net/api-reference/server/node/initialization.html

雲存儲

我們在開發應用的過程中,經常會遇到圖片、文件上傳的需求,並且可能需要爲這些文件設置 CDN 訪問。傳統的流程是下面這樣的:

  1. 前端應用調用上傳接口;
  2. 後臺接收文件,把文件放置到文件存儲服務內(例如 騰訊雲 COS);
  3. 如果需要 CDN 加速文件訪問,需要爲 CDN 設置回源路徑到後端 COS 上。

但如果使用雲開發,只需要在客戶端調用 uploadFile,就可以一步完成上面的三件事情:

const tcb = require("tcb-js-sdk");
const app = tcb.init({
    env: 'your-env-id'
})
const { fileID } = await app.uploadFile({
    // 雲端路徑
    cloudPath: "/a/b/c/filename",
    // 需要上傳的文件,File 類型
    filePath: document.getElementById('file').files[0]
})

uploadFile 會返回一個 fileID,是雲開發內文件的唯一標識符,我們可以使用 getTempFileURL 來獲取文件 URL 訪問鏈接:

const tcb = require("tcb-js-sdk");
const app = tcb.init({
    env: 'your-env-id'
})
const { fileList } = app.getTempFileURL({
    fileList: [
        'cloud://a/b/c'
    ]
})
// fileList 是一個有如下結構的對象數組
// [{
//    fileID: 'cloud://a/b/c', // 文件 ID
//    tempFileURL: 'http://xxx/xxx/xxx', // 臨時文件網絡鏈接
//    maxAge: 120 * 60 * 1000, // 有效期
// }]

更詳細的內容,可以參考:https://docs.cloudbase.net/storage/introduce.html

擴展能力

雲開發除了上述的基礎功能之外,還提供了一系列的擴展能力,包括但不僅限於:

更詳細的內容,可以參考:https://docs.cloudbase.net/extension/introduce.html

總結

上面的能力是不是有些讓你看花眼了,完全不知道要怎麼搭配起來使用?

其實一張圖就可以解決:

圖中的客戶端SDK包括:

服務端 SDK 包括:

使用雲開發快速搭建實時聊天室

光看示例代碼當然沒有什麼意思,我們接下來就拿雲開發的一些能力,來快速開發一個實時在線聊天室吧。

項目代碼:https://github.com/TencentCloudBase/cloudbase-realtime-demo

這是一個由 create-react-app 快速生成的腳手架項目,所以大部分構建和工程化的細節這裏就略過不談了,我們直接來看代碼實現,大致上實現了三個功能,括號中是使用的雲開發能力:

首先是我們的初始化流程,先使用匿名登錄,然後建立實時數據推送的連接:

async function init() {
  // 使用匿名登錄
  await auth.anonymousAuthProvider().signIn();

  // 使用 refreshToken 的前 6 位作爲 uid
  setUid(auth.hasLoginState().credential.refreshToken.slice(0, 6));

  // 建立實時數據推送連接
  await db
    .collection("messages")
    .where({})
    .watch({
      onChange(snapshot) {
        setList(snapshot.docs);
        setLoading(false);
      },
      onError(err) {
        console.log(err);
      },
    });
}

建立實時連接之後,集合中的任何變化,都會觸發 onChange() 回調,然後我們使用 setList() 來更新界面上的消息數據。

當然只讀消息是不夠的,我們還需要發送消息,具體實現非常簡單,可以看 sendMessage() 方法,直接使用 add() 方法向數據庫寫入數據就可以了:

// 發送消息
async function sendMessage() {
  const message = {
    timestamp: new Date().getTime(),
    text,
    uid,
  };
  await db.collection("test").add(message);
  // 清空輸入欄
  setText("");
}

當然以上只是局部的代碼片段,整體代碼可以參考:

https://github.com/TencentCloudBase/cloudbase-realtime-demo/blob/master/src/App.js

開發完畢之後,我們便可以使用 雲開發靜態網站 來託管我們的這個聊天室 Web 應用。

首先我們構建我們的應用:

$ npm run build

構建產物會生成到 build 目錄下。

然後我們發佈到靜態託管即可(託管前,請先開通靜態網站):

$ cloudbase hosting:deploy ./build -e your-env-id

發佈完成後,你便可以通過 https://xxxx.tcb.qcloud.la/xxxx 的來訪問你的應用了。進一步,你還可以爲它綁定自定義域名

PS:實際上,雲開發的主頁官方文檔,就是這樣託管的(畢竟做雲服務的,最重要的就是 Eating your own dog food 嘛)。

當然,除了這個實戰 Demo 以外,還可以看看一些真正業務的實踐:

展望

給 Web 開發者帶來效率和質量上的提升,幫助他們開發更多更優質的應用,免去運維、後臺開發的煩惱,是雲開發不變的願景。雖然我們目前已經有了很多強大又方便使用的能力,但這遠不是我們的終點(實際上我們纔剛剛起步),在未來我們將會持續完善下面的能力,進一步完善雲開發的體系:

  • 更多的支持平臺
  • 更完整的用戶登錄鑑權體系
  • 對 Vue、React 等主流框架的深度集成
  • 對 Vue、React SSR 的一站式支持
  • 更好的開發者工具、CLI、腳手架
  • 更加強大的日誌、監控告警能力

PS:如果你覺得這篇文章對你有幫助,不妨花幾分鐘時間,來試一試快速開始吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章