评论

云开发 For Web:一站式开发下一代 Serverless Web 应用

云开发 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 服务来说,几乎是量身定做的。

举一个最简单的例子,你现在需要上线一个新的 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:如果你觉得这篇文章对你有帮助,不妨花几分钟时间,来试一试快速开始吧。

点赞 1
收藏
评论

1 个评论

  • 维她命系
    维她命系
    星期二 13:15

    看起来不错啊,有空搞个试试。

    星期二 13:15
    赞同
    回复
登录 后发表内容