微信小程序云开发如何实现数据库自动备份实现


Posted in Javascript onAugust 16, 2019

前言

数据是无价的,我们通常会把重要的业务数据存放在数据库中,并需要对数据库做定时的自动备份工作,防止数据异常丢失,造成无法挽回的损失。

小程序云开发提供了方便的云数据库供我们直接使用,云开发使用了腾讯云提供的云数据库,拥有完善的数据保障机制,无需担心数据丢失。但是,我们还是不可避免的会担心数据库中数据的安全,比如不小心删除了数据集合,写入了脏数据等。

还好,云开发控制台提供了数据集合的导出,导入功能,我们可以手动备份数据库。不过,总是手动备份数据库也太麻烦了点,所有重复的事情都应该让代码去解决,下面我们就说说怎么搞定云开发数据库自动备份。

通过查阅微信的文档,可以发现云开发提供了数据导出接口databaseMigrateExport

POST https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=ACCESS_TOKEN

通过这个接口,结合云函数的定时触发功能,我们就可以做数据库定时自动备份了。梳理一下大致的流程:

  • 创建一个定时触发的云函数
  • 云函数调用接口,导出数据库备份文件
  • 将备份文件上传到云存储中以供使用

1. 获取 access_token

调用微信的接口需要 access_token,所以我们首先要获取 access_token。通过文档了解到使用 auth.getAccessToken 接口可以用小程序的 appid 和 secret 获取 access_token。

// 获取 access_token
request.get(
  `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`,
  (err, res, body) => {
    if (err) {
      // 处理错误
      return;
    }
    const data = JSON.parse(body);
    // data.access_token
  }
);

2. 创建数据库导出任务

获取 access_token 后,就可以使用 databaseMigrateExport 接口导出数据进行备份。

databaseMigrateExport 接口会创建一个数据库导出任务,并返回一个 job_id,这个 job_id 怎么用我们下面再说。显然数据库的数据导出并不是同步的,而是需要一定时间的,数据量越大导出所要花费的时间就越多,个人实测,2W 条记录,2M 大小,导出大概需要 3~5 S。

调用 databaseMigrateExport 接口需要传入环境 Id,存储文件路径,导出文件类型(1 为 JSON,2 为 CSV),以及一个 query 查询语句。

因为我们是做数据库备份,所以这里就导出 JSON 类型的数据,兼容性更好。需要备份的数据可以用 query 来约束,这里还是很灵活的,既可以是整个集合的数据,也可以是指定的部分数据,这里我们就使用 db.collection('data').get() 备份 data 集合的全部数据。同时我们使用当前时间作为文件名,方便以后使用时查找。

request.post(
  `https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=${accessToken}`,
  {
    body: JSON.stringify({
      env,
      file_path: `${date}.json`,
      file_type: '1',
      query: 'db.collection("data").get()'
    })
  },
  (err, res, body) => {
    if (err) {
      // 处理错误
      return;
    }
    const data = JSON.parse(body);
    // data.job_id
  }
);

3. 查询任务状态,获取文件地址

在创建号数据库导出任务后,我们会得到一个 job_id,如果导出集合比较大,就会花费较长时间,这时我们可以使用 databaseMigrateQueryInfo 接口查询数据库导出的进度。

当导出完成后,会返回一个 file_url,即可以下载数据库导出文件的临时链接。

request.post(  `https://api.weixin.qq.com/tcb/databasemigratequeryinfo?access_token=${accessToken}`,
  {
    body: JSON.stringify({
      env,
      job_id: jobId
    })
  },
  (err, res, body) => {
    if (err) {
      reject(err);
    }

    const data = JSON.parse(body);
    // data.file_url
  }
);

获取到文件下载链接之后,我们可以将文件下载下来,存入到自己的云存储中,做备份使用。如果不需要长时间的保留备份,就可以不用下载文件,只需要将 job_id 存储起来,当需要恢复备份的时候,通过 job_id 查询到新的链接,下载数据恢复即可。

至于 job_id 存在哪,就看个人想法了,这里就选择存放在数据库里。

await db.collection('db_back_info').add({
  data: {
    date: new Date(),
    jobId: job_id
  }
});

4. 函数定时触发器

云函数支持定时触发器,可以按照设定的时间自动执行。云开发的定时触发器采用的 Cron 表达式语法,最大精度可以做的秒级,详细的使用方法可以参考官方文档:定时触发器 | 微信开放文档

这里我们配置函数每天凌晨 2 点触发,这样就可以每天都对数据库进行备份。在云函数目录下新建 config.json文件,写入如下内容:

{
 "triggers": [
  {
   "name": "dbTrigger",
   "type": "timer",
   "config": "0 0 2 * * * *"
  }
 ]
}

完整代码

最后,贴出可以在云函数中使用的完整代码,只需要创建一个定时触发的云函数,并设置好相关的环境变量即可使用

  • appid
  • secret
  • backupColl:需要备份的集合名称,如 ‘data'
  • backupInfoColl:存储备份信息的集合名称,如 ‘db_back_info'

注意,云函数的默认超时时间是 3 秒,创建备份函数时,建议将超时时间设定到最大值 20S,留有足够的时间查询任务结果。

/* eslint-disable */
const request = require('request');
const cloud = require('wx-server-sdk');

// 环境变量
const env = 'xxxx';

cloud.init({
  env
});

// 换取 access_token
async function getAccessToken(appid, secret) {
  return new Promise((resolve, reject) => {
    request.get(
      `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`,
      (err, res, body) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(JSON.parse(body));
      }
    );
  });
}

// 创建导出任务
async function createExportJob(accessToken, collection) {
  const date = new Date().toISOString();

  return new Promise((resolve, reject) => {
    request.post(
      `https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=${accessToken}`,
      {
        body: JSON.stringify({
          env,
          file_path: `${date}.json`,
          file_type: '1',
          query: `db.collection("${collection}").get()`
        })
      },
      (err, res, body) => {
        if (err) {
          reject(err);
        }

        resolve(JSON.parse(body));
      }
    );
  });
}

// 查询导出任务状态
async function waitJobFinished(accessToken, jobId) {
  return new Promise((resolve, reject) => {
    // 轮训任务状态
    const timer = setInterval(() => {
      request.post(
        `https://api.weixin.qq.com/tcb/databasemigratequeryinfo?access_token=${accessToken}`,
        {
          body: JSON.stringify({
            env,
            job_id: jobId
          })
        },
        (err, res, body) => {
          if (err) {
            reject(err);
          }

          const { status, file_url } = JSON.parse(body);

          console.log('查询');

          if (status === 'success') {
            clearInterval(timer);
            resolve(file_url);
          }
        }
      );
    }, 500);
  });
}
exports.main = async (event, context) => {
  // 从云函数环境变量中读取 appid 和 secret 以及数据集合
  const { appid, secret, backupColl, backupInfoColl } = process.env;

  const db = cloud.database();

  try {
    // 获取 access_token
    const { errmsg, access_token } = await getAccessToken(appid, secret);

    if (errmsg && errcode !== 0) {
      throw new Error(`获取 access_token 失败:${errmsg}` || '获取 access_token 为空');
    }

    // 导出数据库
    const { errmsg: jobErrMsg, errcode: jobErrCode, job_id } = await createExportJob(access_token, backupColl);

    // 打印到日志中
    console.log(job_id);

    if (jobErrCode !== 0) {
      throw new Error(`创建数据库备份任务失败:${jobErrMsg}`);
    }

    // 将任务数据存入数据库
    const res = await db.collection('db_back_info').add({
      data: {
        date: new Date(),
        jobId: job_id
      }
    });

    // 等待任务完成
    const fileUrl = await waitJobFinished(access_token, job_id);

    console.log('导出成功', fileUrl);

    // 存储到数据库
    await db
      .collection(backupInfoColl)
      .doc(res._id)
      .update({
        data: {
          fileUrl
        }
      });
  } catch (e) {
    throw new Error(`导出数据库异常:${e.message}`);
  }
};

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
用javascript实现的图片马赛克后显示并切换加文字功能
Apr 21 Javascript
JavaScript中URL编码函数代码
Jan 11 Javascript
JavaScript高级程序设计 阅读笔记(十四) js继承机制的实现
Aug 14 Javascript
转换字符串为json对象的方法详解
Nov 29 Javascript
node.js中的console.dir方法使用说明
Dec 10 Javascript
jquery UI Datepicker时间控件的使用方法(基础版)
Nov 07 Javascript
node-http-proxy修改响应结果实例代码
Jun 06 Javascript
输入法的回车与消息发送快捷键回车的冲突解决方法
Aug 09 Javascript
javascript如何创建对象
Aug 29 Javascript
echarts学习笔记之箱线图的分析与绘制详解
Nov 22 Javascript
vue webpack重写cookie路径的方法
Jul 10 Javascript
小程序中的箭头函数的具体使用
Jun 19 Javascript
jQuery zTree树插件的使用教程
Aug 16 #jQuery
Vue 权限控制的两种方法(路由验证)
Aug 16 #Javascript
JS中的算法与数据结构之列表(List)实例详解
Aug 16 #Javascript
jQuery zTree插件快速实现目录树
Aug 16 #jQuery
JS中的算法与数据结构之二叉查找树(Binary Sort Tree)实例详解
Aug 16 #Javascript
微信小程序webview组件交互,内联h5页面并网页实现微信支付实现解析
Aug 16 #Javascript
JS中的算法与数据结构之常见排序(Sort)算法详解
Aug 16 #Javascript
You might like
ThinkPHP的cookie和session冲突造成Cookie不能使用的解决方法
2014/07/01 PHP
Yii2下点击验证码的切换实例代码
2017/03/14 PHP
php使用curl下载指定大小的文件实例代码
2017/09/30 PHP
发两个小东西,ASP/PHP 学习工具。 用JavaScript写的
2007/04/12 Javascript
解决Jquery鼠标经过不停滑动的问题
2014/03/03 Javascript
javascript中实现兼容JAVA的hashCode算法代码分享
2020/08/11 Javascript
js时间日期格式化封装函数
2014/12/02 Javascript
jQuery简单实现遍历数组的方法
2015/04/14 Javascript
基于jQuery实现仿51job城市选择功能实例代码
2016/03/02 Javascript
使用jQuery给input标签设置默认值
2016/06/20 Javascript
浅谈$('div a') 与$('div>a')的区别
2016/07/18 Javascript
JavaScript中闭包之浅析解读(必看篇)
2016/08/25 Javascript
easyui datagrid 大数据加载效率慢,优化解决方法(推荐)
2016/11/09 Javascript
详解前端自动化工具gulp自动添加版本号
2016/12/20 Javascript
原生js实现瀑布流布局
2017/03/08 Javascript
JS实现在文本指定位置插入内容的简单示例
2017/12/22 Javascript
mpvue将vue项目转换为小程序
2018/09/30 Javascript
详解基于vue-cli3快速发布一个fullpage组件
2019/03/08 Javascript
微信小程序收货地址API兼容低版本解决方法
2019/05/18 Javascript
Vue使用localStorage存储数据的方法
2019/05/27 Javascript
Vue使用Three.js加载glTF模型的方法详解
2020/06/14 Javascript
django模型中的字段和model名显示为中文小技巧分享
2014/11/18 Python
python 读取excel文件生成sql文件实例详解
2017/05/12 Python
windows下python安装paramiko模块和pycrypto模块(简单三步)
2017/07/06 Python
Python基于OpenCV实现视频的人脸检测
2018/01/23 Python
python下的opencv画矩形和文字注释的实现方法
2019/07/09 Python
IE10 Error.stack 让脚本调试更加方便快捷
2013/04/22 HTML / CSS
eDreams加拿大:廉价航班、酒店和度假
2019/03/29 全球购物
世界上最大的艺术社区:SAA
2020/12/30 全球购物
大一自我鉴定范文
2013/10/04 职场文书
水电站项目建议书
2014/05/12 职场文书
离婚协议书范本2014
2014/10/27 职场文书
2014年残疾人工作总结
2014/12/06 职场文书
2015年班主任个人工作总结
2015/03/31 职场文书
2015年保险公司工作总结
2015/04/24 职场文书
学雷锋感言
2015/08/03 职场文书