在进行微信小程序开发时,经常会在微信的文档中看到一段话

本接口应在服务器端调用,详细说明参见服务端API。

本接口支持云调用。需开发者工具版本 >= 1.02.1904090(最新稳定版下载),wx-server-sdk >= 0.4.0

这里提到的云调用,就是微信提供的小程序Serverless云开发服务,包括云函数、云数据库、文件存储等一系列后端服务。在小程序中使用云开发服务,可以让开发人员更专注于代码和业务本身,不用关心服务器和底层的运维设施,并且它还有着比服务器端更方便的鉴权步骤,可以让开发人员更快更容易的开发小程序。

Serverless

Serverless 架构是指大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,函数是无服务器架构中抽象语言运行时的最小单位。在这种架构中,我们并不看重运行一个函数需要多少 CPU 或 RAM 或任何其他资源,而是更看重运行函数所需的时间,我们也只为这些函数的运行时间付费。

serverless热度趋势
Serverless是一个从大约2016年被开发者所关注的架构,从他的发展趋势来看,直到现在,它都还保持着上升的势头,这是一个非常有潜力的架构。从字面上看,Serverless架构,即无服务器架构,这并不是说真的没有没有服务器,而是对物理服务器进行了抽象和虚拟化。Serverless架构主要有以下特点。

隔离

这其实是开发人员长久以来一直在做的事,为了解决各种环境问题,想出了一系列的隔离方法。从虚拟机、虚拟环境到docker等容器技术,利用这些技术,将代码和运行代码的硬件以及操作系统隔离开,将应用操作和服务器操作隔离开。这样做之后,就避免了出现bug时还要先排查环境问题的窘境。Serverless架构就可以很好地隔离操作系统,甚至更深层的技术细节。在Serverless架构的应用中,开发者只需要专注于业务逻辑的实现,而完全不需要操心底层的运维等工作。

虚拟化

与隔离同样的,虚拟化也是一个发展已久的技术。但与隔离的目的不同,虚拟化是云计算的重要技术,主要用于物力资源的池化,从而可以弹性地分配给用户。物力资源包括服务器,网络和存储。虚拟化的思想可以追溯到IBM机器的逻辑分区,即把一台机器划分成若干台逻辑的服务器,每台逻辑服务器拥有独占的计算资源(CPU、内存、硬盘、网卡),可以单独安装和运行操作系统。划分为更小的计算单元,大大减少了资源的浪费,提高了生产效率。云计算是通过使计算分布在大量的分布式计算机上,而非本地计算机或远程服务器中,企业数据中心的运行将与互联网更相似。这使得企业能够将资源切换到需要的应用上,根据需求访问计算机和存储系统。对于云计算来说,虚拟化是必不可少的。随着云计算的的流行和发展,虚拟化技术也日渐成熟,我们已经可以自动管理虚拟化的计算资源。在Serverless架构中,应用是真正的按需使用,只有请求到来的时候,应用才开始运行计算。

事件驱动

事件驱动编程(英语:Event-driven programming)是一种电脑程序设计模型。这种模型的程序运行流程是由用户的动作(如鼠标的按键,键盘的按键动作)或者是由其他程序的消息来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由程序员来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在交互程序(Interactive program)的情况下孕育而生的。

Serverless只有在请求到来的时候才开始计算,这就是一种事件驱动编程。也就是说,这和我们在编写GUI程序,比如桌面程序和web应用时一样,通过监听用户的操作,才开始进行相应的处理。Serverless则是在用户使用的时候,才会开始对用户的行为进行响应。

依赖特定平台

Serverless包含的两部分内容Baas和Faas,Baas严重或完全依赖第三方应用程序/服务(比如云平台)管理服务器端逻辑和状态,Faas也需要将服务器端的应用逻辑(微服务甚至粒度更小的服务)以事件驱动的方式运行在无状态的临时的容器中,并且这些容器、计算资源都是由第三方管理。因此,Serverless架构对于云平台或第三方服务的依赖度非常高。国外的主要平台如AWS Lambda,国内也有阿里云和腾讯云等服务。由于Serverless的运行特性,这些第三方服务商也大都是按运行时间和内存来进行收费,实际的开销并不大,用得少甚至不要钱。

Serverless的优势

  1. 部署方便。得益于第三方服务,Serverless的部署甚至比docker还方便。在AWS上,只要运行serverless deploy就能完成部署或更新,在微信云函数上只要点击部署就可以。
  2. 降低成本。得益于云计算,Serverless架构需要的成本大大降低了,这个成本包括硬件成本和运维成本。应用Serverless架构,就不再需要一台一直运行着的服务器,甚至不需要一台一直运行着的云服务器,因为Serverless只有在计算的时候才算钱。同时,也不需要花时间去搭建和维护服务器,第三方平台会做好这一切。
  3. 快速上线。不需要管服务器,不需要写部署脚本,不需要担心服务器安全问题,开发人员只需要关注业务逻辑,甚至连测试服务都由第三方服务准备好了,当然整一个迭代的时间都被大大缩短了。
  4. 适应微服务架构。对比微服务,很容易发现,Serverless有很多微服务的特点。一个Serverless其实就是一个微服务的实例。

Serverless的劣势

  1. 长期来看,虽然维护成本低了,但是长期且大量的请求产生的服务费用会比直接买一台云服务器更贵。这就相当于租车和买车的区别。同时由于强依赖于第三方服务,这就将我们和服务供应商绑定了,很难再进行切换。如果有切换的需求,在进行开发的时候,可能就要有所注意。同时由于第三方服务供应商水平有高有低,提供的调试和开发工具也都各有区别,好不好用是一方面,用习惯了要换又会增加适应成本。
  2. 冷启动时间的问题。传统应用没有冷启动的过程。

由于这些特性,Serverless主要被应用于一些实时性要求不高,且不会长期大量使用的场景。如通知分发,定时任务,一些轻量级api或者初创公司的产品上。

微信云开发

进入正题,微信云开发。微信云开发就是微信为小程序提供的Serverless服务。小程序在很多时候都被用作一个导流工具,其中的通知和消息的功能就很适合用Serverless来进行处理。并且使用云开发,会减少很多请求校验的流程,开发人员可以更专注于业务逻辑。微信云开发中的Serverless主要包括了以下三个功能。

  1. 数据库。云开发提供了一个JSON数据库,顾名思义,数据库中的每条记录都是一个JSON格式的对象。一个数据库可以有多个集合(相当于关系型数据中的表),集合可看做一个JSON数组,数组中的每个对象就是一条记录,记录的格式是JSON对象。
  2. 文件存储。云开发提供了一块存储空间,提供了上传文件到云端、带权限管理的云端下载能力,开发者可以在小程序端和云函数端通过API使用云存储功能。
  3. 云函数。云函数是一段运行在云端的代码,无需管理服务器,在开发工具内编写、一键上传部署即可运行后端代码。也就是关键的业务逻辑。云函数用的是node.js,对于前端开发人员来说再熟悉不过了,大大降低了开发门槛。

同时,小程序还提供了两个环境,区分线上和测试。开通云开发功能后,就可以创建环境和选择配额。如果小程序访问量不大,选择基础的配额,是不需要付费的。开启云开发的功能后,就可以开始愉快的写云函数了。首先要创建云函数的目录,一般来说,存放云函数的文件夹最好和小程序源码平行,比如云函数的文件夹叫functions,小程序的文件夹叫miniprogram,然后在project.config.json中分别定义这两个目录。这样结构更清晰,并且小程序运行时也不会加载无关代码。

这三个功能都可以通过小程序直接调用,也可以通过api由自己的服务器调用,不过当然这需要一定的鉴权。同时微信还提供了云调用功能,可以在云函数中调用数据库和文件存储。接下来主要要讲云函数的功能。

云函数

在新建云函数的时候,系统会默认创建一个package.json文件。这也是云函数的一个特点,每一个函数都有一个package.jaon,它们的依赖项也是相互独立的。你可以选择本地安装npm依赖,也可以选择去云端安装依赖,这取决你是否要在本地调试。

每一个云函数都有一个唯一的main函数作为入口,传入参数有两个,一个是event对象,一个是context对象。event指的是触发云函数的事件,当小程序端调用云函数时,event就是小程序端调用云函数时传入的参数。context对象包含了此处调用的调用信息和运行状态,可以用它来了解服务运行的情况。所以一个简单的云函数就像这样。

// ...
exports.main = async (event, context) => {
  // ...
  return {
    sum: event.a + event.b
  }
}

在小程序中调用云函数,要先将云函数部署到云端,这时就可以选择是否在云端安装依赖。部署完成后,通过wx.cloud.callFunction来调用云函数。

wx.cloud.callFunction({
  // 云函数名称
  name: 'add',
  // 传给云函数的参数
  data: {
    a: 1,
    b: 2,
  },
  success: function(res) {
    console.log(res.result.sum) // 3
  },
  fail: console.error
})

或者用Promise风格的调用。

wx.cloud.callFunction({
  // 云函数名称
  name: 'add',
  // 传给云函数的参数
  data: {
    a: 1,
    b: 2,
  },
})
.then(res => {
  console.log(res.result) // 3
})
.catch(console.error)

云函数所有的调用日志,微信也都存着,排查问题也比较方便。当然这里也有一个问题,部署好的云函数,就没法在服务端进行调试了,比如打断什么的更是不可能的。如果出现错误,要么看日志,要么就只有不停添加console.log然后部署,然后再触发,非常不方便。

例子--自动回复客服消息

首先在开发者工具中配置用云函数处理客服消息。可以选择处理特定的消息类型,这里选择了小程序卡片和进入客服会话事件,那么就只有这两种消息会发送给云函数进行处理,其他消息还是发给客服或者服务器处理。

要调用微信客服消息接口,所以要先在package.json中加上依赖项"wx-server-sdk":"latest",同时在index.js中引入wx-server-sdk,这是一个可以在云函数中操作数据库、存储以及调用其他云函数的微信提供的库。然后我们就可以用微信的sdk来获取用户活动信息,并且调用openapi进行回复。

// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()

// 云函数入口函数
exports.main = async(event, context) => {
    const wxContext = cloud.getWXContext()
    console.log(event);
    await cloud.openapi.customerServiceMessage.send({
      touser: wxContext.OPENID,
      msgtype: 'text',
      text: {
        content: '收到',
      }
    })
    return 'success'
}

在云函数中,也可以发送网络请求,借助httprequestaxios等库,就跟在node.js中发送请求一样。这样,我们就可以根据用户消息中带上的参数来回复特定的内容。例如,在小程序卡片的pagePath或者是进入会话事件中的session-from字段中,带上需要的参数。这里用request-promise来做网络请求,同样的,先在package.json中添加依赖项"request-promise": "^4.2.5"

const cloud = require('wx-server-sdk')
const rp = require('request-promise');
cloud.init()

exports.main = async(event, context) => {
  const wxContext = cloud.getWXContext()
  if (event.MsgType === 'miniprogrampage') {
    await rp({
      uri: 'url',
      method: 'POST',
      body: JSON.parse(event.pagePath.split('=')[1]),
      json: true
    }).then(async res => {
      await cloud.openapi.customerServiceMessage.send({
        touser: wxContext.OPENID,
        msgtype: 'link',
        link: {
          title: 'title',
          description: 'description',
          url: res.result.url,
          thumb_url: res.result.imageUrl
        }
      })
    })
  } else if (event.MsgType === 'event') {
    console.log(JSON.parse(event.SessionFrom));
  }
  return 'success'
}

总结

目前没有试过云开发中的数据库和存储,只是初探了云函数的使用。云函数在处理一些简单的业务逻辑上非常方便,微信也给云函数开了一些绿色通道,比如各种校验,但是也有一个很大的缺陷,就是调试的问题。本地调试没什么问题,远程调试只能一次次加console.log然后部署然后看日志,不知道会不会有更方便的办法。


Jovi
38 声望1 粉丝

想要尽力保住头发的前端程序员