阅读 808

Serverless从懵逼到实战 | 🏆 技术专题第七期征文

定义

Serverless 是一种执行模型(execution model)。在这种模型中,云服务商负责通过动态地分配资源来执行一段代码。云服务商仅仅收取执行这段代码所需要资源的费用。代码通常会被运行在一个无状态的容器内,并且可被多种事件触发(http 请求、数据库事件、监控报警、文件上传、定时任务……)。代码常常会以函数(function)的形式被上传到云服务商以供执行,因此Serverless也会被称作Functions as a Service 或者 FaaS

  • BaaS: Backend as a Service,这里的Backend可以指代任何第三方提供的应用和服务,比如提供云数据库服务的FirebaseParse,提供统一用户身份验证服务的Auth0Amazon Cognito等。
  • FaaS: Functions as a Service,应用以函数的形式存在,并由第三方云平台托管运行,比如之前提到的AWS Lambda,Google Cloud Functions等。

背景

那么我们为什么需要Serverless呢?对于这个问题来说,我们可以从我们日常个人的开发中找到原因。无论是个人博客,亦或者是小程序,还是个人网站,我们都要面临一个问题就是,怎么解决服务器部署相关的问题。

对于常规的项目来说,一般的流程莫过于:购买服务器 => 域名备案(这一步可能可以不用,看个人需求) => 环境搭建 => 部署

那么我们如果仅仅是想要开发一个几乎不太需要多强的后台支持时,却要去做这么多操作,这对于整个项目的开发来说是十分不友好的,很容易就会让人放弃上线的念头。

那么Serverless就是用于解决这个问题的存在,他具备托管服务端各项功能的能力,让你无需进行上述繁琐的步骤就能轻松让你的网站或服务正常运行且可访问,同时能够极大降低个人的维护成本,这对于广大开发者而言无疑是十分实用的。

Serverless特点

低成本

众所周知,我们购买一个云服务器,抛去人力成本不计,单从收费方式上来分析,各厂商都是采用按月计费的方式来收取,也就是即使没有人访问你的网站或服务,每月需要支付的租金都是不变的。

然后再来看Serverless应用,它是按你实际使用的资源量来进行计价的,可以理解为你用多少付多少,可以类比于我们手机流量的方式来收取。同时,根据福布斯2015年发布的一份研究报告,从全年来看,一个典型的数据中心里的服务器平均资源使用率只有可怜的5%到15%,也就是说如果全部使用Serverless,理论上至少可以节省80%的运行成本。

对比分析之后,Serverless在成本方面无疑是较低的。

自动扩缩容

正如上文所述,函数即应用,每一个函数只会服务于特定的功能,它可以随意的进行动态拓展或者收缩容量,同时不会影响到其他函数,并且粒度更小,速度更快。反观我们的单体应用和微服务来说,借助于各种容器编排技术,虽然也能实现自动扩缩容,但由于粒度关系,相比函数,始终会存在一定的资源浪费。

事件驱动

函数本质上实现的是一种IPO(Input-Process-Output)模型,它是短暂的,是即用即走的。这点是函数区别于单体应用和微服务的另一个特征。不管是单体应用,还是微服务,都是系统中的常驻进程,即便你不使用,它依旧会一直运行。而对于函数来说,没有请求就不需要消耗任何资源,只有在收到请求时才会调动资源进行响应,完成之后就会立刻释放资源,这在节省资源方面无疑是巨大的优点。

无状态

从事件驱动能力中我们提到了它只有在收到请求时才会工作起来,工作完成之后就立刻被释放,也就是各种运行时的内存缓存都是没有太大效益的,不仅如此,同一个请求方式,第二次访问服务时很有可能被调度到其他新的机器上,所以本地缓存方式依旧是失效的,函数只能使用外存(比如Redis,数据库)进行缓存,而操作外存都需要通过网络,性能跟内存、本地硬盘相比差了一到两个数量级。

冷启动

一般来说,如果你的函数很长时间没有被调用,那么它的容器资源就会被回收。等到下一次请求来临时,再重新进行启动和资源分配,同时这个过程也会伴随着时延,造成的用户体验也是不友好的。

不仅如此,一旦你的项目部署到特定的平台上时,必然存在着与目标平台捆绑的情况,也就是如果某一天你打算迁移你的项目,那无疑会带来巨大的阻力。

从开发者角度来看,一旦你的环境依赖于云平台的各项服务,那么必然在本地开发过程中会遇到种种挫折,你可能需要将编写好的代码上传到云端进行调试,这在日常开发中来说简直是糟糕的开发体验,即便你只修改了一行代码。

第一个云函数

这里将利用腾讯云作为演示平台,首先你需要先登录到腾讯云

接着打开云函数控制台

腾讯云函数控制台

我们选择新建一个云函数

新建云函数

在这里我们可以自由选择开发的语言以及是否需要模板,为了演示方便还是选择勾选模板,然后点击完成,就能进入我们正式的代码编写环节了。

代码工作台

这里我们可以随意的修改函数的返回值,接着就能对它进行测试啦。

测试云函数

当你看到运行后的返回结果和你编写的代码返回值一致时就说明你的云函数已经在工作中了。

当然,仅仅只是这么测试一下自然很不过瘾,我还想能通过http请求的方式来访问这个云函数,又该怎么做呢?

这个其实也很简单,我们选择左侧的触发管理,新建一个触发策略,并选择触发方式为API网关,然后直接点击提交。 新建触发策略

具体操作步骤

这个时候我们其实已经可以通过url进行访问了,我们复制访问路径,然后在浏览器访问,就能看到我们云函数给我们返回的内容了。

复制链接

访问步骤

本地开发

介绍完在云平台上创建函数的方式,接下来就该介绍如何进行本地开发并部署的方式了,毕竟总不能一直在云端开发吧,那简直太不友好了。

安装

这里将以Node作为开发语言进行演示,更多语言安装方式请参考官方文档

npm i serverless -g
复制代码

先安装对应的包,这里可以安装到全局便于以后使用。

接着使用相关命令生成demo模板项目:

serverless init sls-demo
复制代码

这里有个小插曲,笔者用官方给的简写sls init sls-demo的时候,发现跳出来一个奇怪的输入,笔者暂时就没去深究了,所以还是改用全写就行了。

创建完成之后,我们进入生成目录,映入眼帘的就是一个src目录和一个serverless.yml配置文件。我们一般将源代码都放置在src目录下,当然,这个也看个人喜好,如果你不喜欢,可以选择放在任意文件夹下,不过就需要修改一下配置文件中的src字段对应的值了,它默认是指向了当前目录下的src目录。

组件信息:

字段名是否必选说明
componentcomponent 的名称,可使用 sls registry 命令查询可引入的组件。
name创建的实例名称,每个组件在部署时将创建一个实例。

参数信息(inputs下对应的字段):

字段名说明
name云函数名称,同时也作为资源 ID。
src代码路径。
handler函数处理方法名称。
runtime云函数运行环境,目前支持: Python2.7、Python3.6、Nodejs6.10、Nodejs8.9、Nodejs10.15、Nodejs12.16、PHP5、PHP7、Go1、Java8 和 CustomRuntime。
region云函数所在的区域。
events触发器。支持的触发器为:timer、apigw、cos、cmq、ckafka 。

想查询更多详细信息可以参考官方文档

开发

我们可以将业务的各项代码都放置在src目录下,这里以当前demo项目举例。

我们打开src/index.js,在暴露的这个函数中我们可以尽情的做一些骚操作,然后返回一个结果,并进行测试。你可以连接数据库操作,或者是发起请求等,根据你自己选取的语言来进行代码编写,笔者这里是选择的Nodejs环境进行调试。

不仅如此,我们可以配合其他Node框架

部署

部署就比较简单了,只要一句小小的命令即可轻松部署到云端:

serverless deploy
复制代码

如果你属于第一次部署的话,将会发现在控制台打印出了一个二维码,你需要做的就是用微信进行扫码授权,然后就静待部署完成即可。

部署效果预览

这里你部署完成之后会发现你的目录下生成了一个.env文件,里面保存了你部署需要用到的私密数据,然后第二次部署时就可以不用扫码就能成功部署了(这个授权信息是有时效的,如果失效则需要重新授权)。

调试

执行触发函数命令,function=后面跟着的是你的云函数名称,这样就能判断是否部署成功了。

serverless invoke  --inputs function=scfdemo-dev-scf-demo
复制代码

效果预览:

效果预览

全栈项目实战

如果仅仅只学会了云函数的玩法会不会就有点显示不出serverless的方便性了,这里将用express + Vue3带来一个实战项目部署的过程。

结构搭建

首先创建一个目录来放置我们的前后端项目,然后进入目录,新建一个api目录用于放置我们服务端项目代码,接着在api同级目录下创建一个vue项目(这里是没有限制的,你可以选择reactvue),笔者为了方便,就采用vite创建了一个vue3项目。

同时在该目录下创建一个serverless.yml作为整体项目的配置文件。

项目结构:

  • api
    • app.js
    • package.json
    • ...
  • front
  • ...
  • serverless.yml

好吧还是放个图方便理解吧,.serverless这个目录不用管,后面部署的时候自动生成的。

目录结构预览

这样我们基本的目录结构就搭好了,下面开始配置一下吧:

服务端代码编写

首先我们打开api目录,创建一个app.js作为入口文件,并采用npm init初始化一下项目,并安装expresscors,该演示项目只用这两个包:

npm init

npm i express cors -S
复制代码

然后在app.js中编写如下代码,并将express实例进行导出。

记住这里不要写app.listen(...),只要默认导出就行了

// app.js文件中代码

const express = require('express');
const cors = require('cors');

const app = express();

app.use(cors);

app.use('/*', (req, res) => {
    res.send({
        msg: 'hello world'
    })
})

module.exports = app;
复制代码

前端代码编写

这里为了演示方便就写了一个简单的例子,看个人爱好来编写自己喜欢的项目。

首先打开front目录下的App.vue,并改写代码:

<template>
<div>{{message}}</div>
</template>

<script>
import '../env'; // 部署的时候自动生成
import {
    ref
} from 'vue'

export default {
    name: 'App',
    setup(props) {
        const message = ref();
        fetch(window.env.apiUrl).then(res => res.json()).then(({
            msg
        }) => {
            message.value = msg;
        });

        return {
            message
        }
    }
}
</script>

复制代码

这里笔者是采用vue3编写的,读者自己看个人喜欢选择框架吧,这里主要是想演示一下发起请求然后渲染页面的这个过程,这个import '../env'是必要的,我们不必在意它在不在我们项目的目录下,待会我们部署代码的时候会自动生成的,也就是我们这里直接导入就行了,它主要的做的事情就是将我们配置好的环境变量挂在到window上。

笔者这里在serverless.yml文件中配置了项目部署之后的服务端url的环境变量(window.env.apiUrl),具体配置方式后面会提到,这里先用就行了

我们看到script中的代码,这里其实笔者只做了一件事,请求我们服务器api,然后获取返回值将他渲染到页面上,功能比较简单这里就不进行详细解释了。

serverless配置文件

最后一步,我们配置一下项目的部署配置,参数的用途看代码的注释即可,比较简单。

# 项目名字
name: tencent-fullstack-vue-app

# 前端相关配置
dashboard:
  # 采用的serverless组件
  component: '@serverless/tencent-website'
  # 设置输入参数
  inputs:
    # 我们项目源代码配置
    code:
      # 部署的文件目录
      src: dist
      # 项目的根目录
      root: front
      # 部署之前执行的命令,这里用来先打包出dist目录,然后再将dist目录部署上去,也就是上面配置的src对应的目录
      hook: npm run build
    # 环境变量
    env:
      # 部署的路径
      apiUrl: ${api.url}
# 服务端相关配置
api:
  # 采用的serverless组件
  component: '@serverless/tencent-express'
  inputs:
    # 部署上去的服务端目录
    code: './api'
    # 云函数名字
    functionName: tencent-fullstack-vue-api
    apigatewayConf:
      # 协议
      protocols:
       - https
复制代码

从这里我们可以看到我们配置的环境变量apiUrl,这也就是我们上文中在前端项目中用到的属性,它会在项目部署的时候自动帮我们挂载到window上,我们直接用window.env.apiUrl访问就行了(前提你需要导入前端项目中根目录的env文件,这个是自动生成的,上文也有提到)

调试

上面的工作完成之后就可以开始部署了:

serverless --debug
复制代码

执行上述命令,它会首先自动部署项目到云端,然后将项目的地址打印在控制台上,我们复制地址直接访问就能看到我们部署好的项目了,这里应该也会要求你进行登录授权,控制台会打印出二维码,微信扫码登录一下就好了。

部署完成

部署成功后我们控制台就能看到以上画面,我们复制url到浏览器打开,我们熟悉的hello world就已经完美的展现在页面上了。

如果说你的页面并没有正常显示,那么就需要检查一下自己有没有地方代码编写错误,或者是语法问题以及配置不准确之类的了。

总结

当然这只是一个入门级的教程,这里仅仅只是介绍了Serverless的一小部分功能,而Serverless的强大远非如此,这就需要广大开发者逐步的开启这个新世界的大门了。

🏆 技术专题第七期 |万物皆可 Serverless