scf10cent

基于 Serverless Component 全栈解决方案

  •  
  •   scf10cent · Feb 18, 2020 · 7386 views
    This topic created in 2274 days ago, the information mentioned may be changed or developed.

    什么是 Serverless Component

    Serverless ComponentServerless Framework 的,支持多个云资源编排和组织的场景化解决方案。

    Serverless Component 的目标是磨平不同云服务平台之间差异,你可以将它看作是可以更轻松地构建应用程序的依赖模块。目前 Serverless Component 已经形成一个由社区贡献驱动的生态系统,你可以浏览和使用社区的所有组件,快速开发一款自己想要的应用。

    Serverless Component 工作原理

    基于 Serverless Component 架构,你可以将任何云服务打包成一个组件。这个组件将含有一份 serverless.yml 配置文件,并且通过简单地进行配置就可以使用。本文以 @serverless/tencent-express 来举例。

    如果我们要使用它,只需要新建一个项目 express-demo,然后修改 serverless.yml 配置如下:

    express:
      component: '@serverless/tencent-express'
      inputs:
        region: ap-shanghai
    

    因为 serverless 框架部署到云的鉴权都是基于 dotenv 注入全局的变量来实现的,所以还得在根目录下新增 .env 文件,并配置对应的鉴权参数。

    之后我们就可以在 app.js 中轻松的编写基于 express 的接口服务了:

    const express = require('express')
    const app = express()
    app.get('/', function(req, res) {
      res.send('Hello Express')
    })
    // 不要忘了导出,因为该组件会对它进行包装,输出成云函数
    module.exports = app
    

    这背后所有的流程逻辑都是组件内部实现的,包括:云函数的部署,API 网关的生成等。

    下面是一张简单的组件依赖图:

    Component Dependency Structure

    通过此图可以清晰地查看组件带来的收益,借助社区现有的 @serverless/tencent-express@serverless/tencent-website 组件,我们就可以很快构建想要的全栈应用。

    全栈应用实战

    接下来将介绍如何借助 Serverless Component 快速开发全栈 Web 应用。

    full-stack

    在开始所有步骤前,需执行 npm install -g serverless 命令,全局安装 serverless cli

    准备

    新建项目目录 fullstack-application-vue,在该项目目录下新增 apidashboard 目录。然后新增 serverless.yml.env 配置文件,项目目录结构如下:

    ├── README.md 		// 项目说明文档
    ├── api					  // Restful api 后端服务
    ├── dashboard			// 前端页面
    ├── .env					// 腾讯云相关鉴权参数:TENCENT_APP_ID,TENCENT_SECRET_ID,TENCENT_SECRET_KEY
    └── serverless.yml	// serverless 文件
    

    后端服务开发

    进入目录 api,新增 app.js 文件,编写 express 服务代码,这里先新增一个路由 /,并返回当前服务器时间:

    const express = require('express')
    const cors = require('cors')
    const app = express()
    
    app.use(cors())
    app.get('/', (req, res) => {
      res.send(JSON.stringfy({ message: `Server time: ${new Date().toString()}` }))
    })
    module.exports = app
    

    前端页面开发

    本案例使用的是 Vue.js + Parcel 的前端模板,当然你可以使用任何前端项目脚手架,比如 Vue.js 官方推荐的 Vue CLI 生成的项目。进入 dashboard 目录,静态资源你可以直接复制我准备好的 项目模板,编写入口文件 src/index.js:

    // 这里初始是没有 env.js 模块的,第一次部署后会自动生成
    require('../env')
    
    const Vue = require('vue')
    
    module.exports = new Vue({
      el: '#root',
      data: {
        message: 'Click me!',
        isVisible: true,
      },
      methods: {
        async queryServer() {
          const response = await fetch(window.env.apiUrl)
          const result = await response.json()
          this.message = result.message
        },
      },
    })
    

    配置

    前后端代码都准备好了,现在我们还需要简单配置下 serverless.yml 文件了:

    name: fullstack-application-vue
    
    frontend:
      component: '@serverless/tencent-website'
      # inputs 为 @serverless/tencent-website 组件的输入
      # 具体配置说明参考: https://github.com/serverless-components/tencent-website/blob/master/docs/configure.md
      inputs:
        code:
          src: dist
          root: frontend
          hook: npm run build
        env:
          # 下面的 API 服务部署后,获取对应的 api 请求路径
          apiUrl: ${api.url}
    
    api:
      component: '@serverless/tencent-express'
      # inputs 为 @serverless/tencent-express 组件的输入
      # 具体配置说明参考: https://github.com/serverless-components/tencent-express/blob/master/docs/configure.md
      inputs:
        code: ./api
        functionName: fullstack-vue-api
        apigatewayConf:
          protocol: https
    

    简单的介绍下配置:首先,该文件定义了 frontendapi 两个模块,分别通过 component 属性指定依赖的 Serverless Component。对于一个标准的 Serverless Component,都会接受一个 inputs 属性参数,然后组件会根据 inputs 的配置进行处理和部署,具体有关配置的参数说明,请参考相关组件的官方配置说明。

    部署

    以上所有的步骤都完成后,接下来就是第一次部署了。

    为什么不是直接联调开发呢?因为后端服务是云函数,但是到目前为止,所有代码都是在本地编写,前端页面接口请求链接还不存在。所以需要先将云函数部署到云端,才能进行前后端调试。这个也是本人目前遇到的痛点,因为每次修改后端服务后,都需要重新部署,然后进行前端开发调试。如果你有更好的建议,欢迎评论指教~

    部署时,只需要运行 serverless 命令就行,当然如果你需要查看部署中的 DEBUG 信息,还需要加上 --debug 参数,如下:

    $ serverless
    # or
    $ serverless --debug
    

    然后终端会 balabalabala~, 输出一大堆 DEBUG 信息,最后只需要看到绿色的 done 就行了:

    Deploy Success Result

    这样一个基于 Serverless Component 的全栈应用就开发好了。赶紧点击你部署好的链接体验一下吧~

    在线 Demo

    数据库连接

    既然是全栈,怎么少得了数据库的读写呢?接下来介绍如何添加数据库的读写操作。

    准备

    想要操作数据库,必须先拥有一台数据库实例,腾讯云 MySQL 云数据库 现在也很便宜,可以购买一个最基本按量计费 1 核 1G 内存 的 1 小时收费不到 4 毛钱,是不是非常划算。购买好之后初始化配置,然后新增一个 serverless 数据库,同时新增一张 users 表:

    CREATE TABLE if not exists `test` ( `name` varchar (32) NOT NULL ,`email` varchar (64) NOT NULL ,`site` varchar (128) NOT NULL ) ENGINE = innodb DEFAULT CHARACTER SET = "utf8mb4" COLLATE = "utf8mb4_general_ci"
    

    前端修改

    首先修改前端入口文件 frontend/src/index.js 新增相关函数操作:

    require('../env')
    
    const Vue = require('vue')
    const axios = require('axios')
    module.exports = new Vue({
      el: '#root',
      data: {
        // ...
        form: {
          name: '',
          email: '',
          site: '',
        },
        userList: [],
      },
      methods: {
        // ...
        // 获取用户列表
        async getUsers() {
          const res = await axios.get(window.env.apiUrl + 'users')
          this.userList = (res.data && res.data.data) || []
        },
        // 新增一个用户
        async addUser() {
          const data = this.form
          const res = await axios.post(window.env.apiUrl + 'users', data)
          console.log(res)
          if (res.data) {
            this.getUsers()
          }
        },
      },
      mounted() {
        // 视图挂在后,获取用户列表
        this.getUsers()
      },
    })
    

    当然你还需要修改视图模板文件 frontend/index.html,在页面模板中新增用户列表和用户表单:

    <!-- user form -->
    <section class="user-form" action="#">
      <div class="form-item">
        <label for="name">
          Name:
        </label>
        <input name="name" v-model="form.name" type="text" /><br />
      </div>
      <div class="form-item">
        <label for="email">
          Email:
        </label>
        <input name="email" v-model="form.email" type="email" /><br />
      </div>
      <div class="form-item">
        <label for="site">
          Site:
        </label>
        <input name="site" v-model="form.site" type="text" /><br />
      </div>
      <button @click="addUser">Submit</button>
    </section>
    
    <!-- user list -->
    <section class="user-list">
      <ul v-if="userList.length > 0">
        <li v-for="item in userList" :key="item.id">
          <p>
            <b>Name: {{ item.name }}</b>
            <b>Email: {{ item.email }}</b>
            <b>Site: {{ item.site }}</b>
          </p>
        </li>
      </ul>
      <span v-else>No Data</span>
    </section>
    

    注意:如果还不熟悉 Vue.js 语法,请移至 官方文档,当然如果你想快速上手 Vue.js 开发,也可以阅读这份 Vue 从入门到精通 教程。

    后端修改

    这里使用 .env 来进行数据库连接参数配置,在 api 目录下新增 .env 文件,将之前的数据库配置填入文件中,参考 api/.env.example 文件。然后添加并安装 dotenv 依赖,同时添加 mysql2 模块进行数据库操作,body-parser 模块进行 POST 请求时的 body 解析。

    之后新增后端 api,进行数据库读写,修改后的 api/app.js 代码如下:

    'use strict'
    require('dotenv').config()
    const express = require('express')
    const cors = require('cors')
    const mysql = require('mysql2')
    const bodyParser = require('body-parser')
    
    // init mysql connection
    function initMysqlPool() {
      const { DB_HOST, DB_PORT, DB_DATABASE, DB_USER, DB_PASSWORD } = process.env
    
      const promisePool = mysql
        .createPool({
          host: DB_HOST,
          user: DB_USER,
          port: DB_PORT,
          password: DB_PASSWORD,
          database: DB_DATABASE,
          connectionLimit: 1,
        })
        .promise()
    
      return promisePool
    }
    
    const app = express()
    app.use(bodyParser.json())
    app.use(cors())
    
    if (!app.promisePool) {
      app.promisePool = initMysqlPool()
    }
    
    app.get('/', (req, res) => {
      res.send(JSON.stringify({ message: `Server time: ${new Date().toString()}` }))
    })
    
    // get user list
    app.get('/users', async (req, res) => {
      const [data] = await app.promisePool.query('select * from users')
      res.send(
        JSON.stringify({
          data: data,
        })
      )
    })
    
    // add new user
    app.post('/users', async (req, res) => {
      let result = ''
      try {
        const { name, email, site } = req.body
        const [res] = await app.promisePool.query('INSERT into users SET ?', {
          name: name,
          email: email,
          site: site,
        })
        result = {
          data: res && res.insertId,
          message: 'Insert Success',
        }
      } catch (e) {
        result = {
          data: e,
          message: 'Insert Fail',
        }
      }
    
      res.send(JSON.stringify(result))
    })
    
    module.exports = app
    

    配置修改

    这里数据库访问需要通过腾讯云私有网络,所以还需要为云函数配置私有网络( VPC ),同时还需要配置能够操作数据库的角色(关于角色配置,可以直接到 角色管理页面),这里我新建了一个 QCS_SCFFull 的角色,可以用来访问数据库。然后修改 serverless.yml 中的配置:

    # ...
    api:
      component: '@serverless/tencent-express'
      # more configuration for @serverless/tencent-website,
      # refer to: https://github.com/serverless-components/tencent-express/blob/master/docs/configure.md
      inputs:
        code: ./api
        functionName: fullstack-vue-api
        role: QCS_SCFFull # 此角色必须具备访问数据库权限
        functionConf:
          # 这个是用来访问新创建数据库的私有网络,可以在你的数据库实例管理页面查看
          vpcConfig:
            vpcId: vpc-6n5x55kb
            subnetId: subnet-4cvr91js
        apigatewayConf:
          protocol: https
    

    最后重新部署一下就行了。

    以上基于腾讯云 Serverless Framework 来实现:

    小结

    当然全栈方案,并没有这么简单,这里只是简单介绍,如何使用 Serverless Component,快速实现一个全栈应用。如果要应用到实际的业务场景,我们还需考虑更多的问题,期待大家去探索和发现!

    传送门:

    欢迎访问:Serverless 中文网,您可以在 最佳实践 里体验更多关于 Serverless 应用的开发!

    No Comments Yet
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1006 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 39ms · UTC 22:16 · PVG 06:16 · LAX 15:16 · JFK 18:16
    ♥ Do have faith in what you're doing.