云开发云函数中使用Redis的最佳实践,包括五种常用数据结构和分布式全局锁
发布于 4 年前 作者 tanggang 4078 次浏览 来自 分享

Redis因其拥有丰富的数据结构、基于单线程模型可以实现简易的分布式锁、单分片5w+ ops的超强性能等等特点,成为了大家处理高并发问题的最常用的缓存中间件。

那么云开发能不能使用Redis呢?答案是肯定的。

下面我介绍下云开发中Redis使用的最佳实践:

第一步、购买Redis,安装Redis扩展

参见官方文档:https://developers.weixin.qq.com/community/develop/article/doc/000a4446518488b6002c9fa3651813

吐槽一下,写这篇文章的原因之一就是上面的官方文档中的示例代码是在不堪入目,希望这篇文章能让小伙伴少踩些坑。

第二步、创建并部署测试云函数,配置云函数的网络环境

第三步、编写代码

cache.js

const Redis = require('ioredis')

const redis = new Redis({
  port: 6379,
  host: '1.1.1.1',
  family: 4,
  password: 'password',
  db: 0
})

exports.redis = redis

/**
 * 加redis全局锁
 * [@param](/user/param) {锁的key} lockKey 
 * [@param](/user/param) {锁的值} lockValue 
 * [@param](/user/param) {持续时间,单位s} duration
 */
exports.lock = async function(lockKey, lockValue, duration) {
  const lockSuccess = await redis.set(lockKey, lockValue, 'EX', duration, 'NX')
  if (lockSuccess) {
    return true
  } else {
    return false
  }
}

/**
 * 解redis全局锁
 * [@param](/user/param) {锁的key} lockKey 
 * [@param](/user/param) {锁的值} lockValue 
 */
exports.unlock = async function (lockKey, lockValue) {
  const existValue = await redis.get(lockKey)
  if (existValue == lockValue) {
    await redis.del(lockKey)
  }
}

上面是操作redis的工具方法,可以打包放到云函数的层管理中,方便其他云函数引用。层管理使用方式参见官方文档:https://cloud.tencent.com/document/product/876/50940

index.js

const cloud = require("wx-server-sdk")
const cache = require('/opt/utils/cache.js') // 使用到了云函数的层管理

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

global.cloud = cloud
global.db = cloud.database()
global._ = db.command
global.$ = _.aggregate

exports.main = async (event, context) => {
  context.callbackWaitsForEmptyEventLoop = false

  const wxContext = cloud.getWXContext()

  let appId = wxContext.APPID
  if (wxContext.FROM_APPID) {
    appId = wxContext.FROM_APPID
  }

  let unionId = wxContext.UNIONID
  if (wxContext.FROM_UNIONID) {
    unionId = wxContext.FROM_UNIONID
  }

  let openId = wxContext.OPENID
  if (wxContext.FROM_OPENID) {
    openId = wxContext.FROM_OPENID
  }

  // redis五种常用数据结构
  // 字符串
  await cache.redis.set('hello', 'world') // 无过期时间
  await cache.redis.set('hello', 'world', 'EX', 60) // 过期时间60s
  let stringValue = await cache.redis.get('hello')
  console.log('string: ', stringValue)

  // hash
  await cache.redis.hset('hash', 'hello', 'world')
  let hashValue = await cache.redis.hget('hash', 'hello')
  console.log('hash: ', hashValue)

  // list
  await cache.redis.lpush('list', 'hello', 'world')
  let listList = await cache.redis.lrange('list', 0, -1) // 读取队列所有元素
  await cache.redis.ltrim('list', 1, 0) // 清空队列
  console.log('listList: ', listList)

  // set
  await cache.redis.sadd('set', 'hello', 'world')
  let setExist = await cache.redis.sismember('set', 'hello') // 检查元素是否在集合中
  console.log('set: ', setExist)

  // zset
  await cache.redis.zadd('zset', 1, 'hello', 2, 'world')
  let zsetList = await cache.redis.zrange('zset', 0, -1, 'WITHSCORES')
  console.log('zsetList: ', zsetList)

  // redis实现分布式全局锁
  // 加全局锁,锁的过期时间应根据实际业务调整
  const createOrderLock = `createOrderLock:${unionId}`
  const ts = Date.now()
  if (!(await cache.lock(createOrderLock, ts, 3))) {
    return {
      code: 4,
      msg: '操作太频繁了'
    }
  }

  // 这边写全局互斥的业务逻辑代码
  // 比如创建订单,一个用户同时只能并发创建一个订单

  // 解全局锁
  await cache.unlock(createOrderLock, ts)

  return {
    code: 0,
    data: {}
  }
}

上面是测试云函数的入口文件,演示了redis五种常用数据结构和redis全局锁的使用方法。

最后还有个小tips,所有引用到cache.js的云函数需要安装ioredis的依赖,进入云函数目录,使用如下命令:

npm install ioredis
3 回复

我命油我不油天?

牛逼!多来点大佬这种教程,哈哈哈哈

回到顶部