美文网首页
Redlock实践--非阻塞模式

Redlock实践--非阻塞模式

作者: C3_b262 | 来源:发表于2019-01-02 00:44 被阅读0次

    本文主要介绍在琴房预约项目中所用到的资源访问加锁技术Redlock,以及如果实现将redlock转为非阻塞锁。

    1. 背景简介

    1.1 项目简介

    本次实践项目是THU琴房预约小程序与web管理端,后端使用koa框架。主要功能有预约琴房(用户)、更改琴房可用时间(管理端)等。后端使用mysql存储琴房信息(包含琴房可用时间串)。

    1.2 问题介绍

    • 多个用户可能在同一时间对同一琴房的同一时间段进行预约
    • 用户与管理员可能同时进行预约、更改琴房可用时间

    根据上篇文章所描述的,因为存在不同操作之间的资源竞争,所以我们这里使用增加信号量的方式——Redlock。

    2. 加锁实践

    redlock是一个基于redis数据库的分布式锁。通过在redis数据库中设定键值来进行信号量的定义。

    • 我们把“预约“和“更改琴房可用时间”定义为使用同一个键值。
    • 在操作前首先获取此键值,如果获取不到,说明被占用。

    关于这个流程,包括set键值与”占用"和"未占用“状态的更改是由Redlock实现的。

    2.1 给资源加锁

    // 获取权限
    let redis = require("redis");
    let client = redis.createClient("to change: your redis port","to change: your server ip");
    let redlock = new Redlock([client]);
    // 设置时间
    let totalTime = 5000;
    let key = "to change: as you like";
    // 加锁
    redlock.lock(key, totalTime).then(async function(lock){
            // to do
            // your operation block
            // release lock
            lock.unlock().catch(function(err){})
        }
    }).catch(()=>{}) // 如果不加catch会直接报错终止程序
    

    主要流程为:

    • 获取redis的操作权限
    • 设定键值,最长等待时间(防止一直被占用)
    • 执行操作
    • 释放锁

    按照上面流程,我们成功进行了加锁。

    键值的设置非常自由,我们可以通过键值的设置,控制加锁覆盖的范围以及力度。

    2.2 阻塞锁与非阻塞锁

    在完成加锁之后,我们进行压力测试,发现了一个非常坑的情况:Redlock是非阻塞锁

    这就意味着,当一个用户进行预约的时候,别的用户如果有请求就会直接失败,这样是非常用户不友好的。所以我们需要一些操作来将redlock转为非阻塞锁。有两个方案:

    • 方案一:实现一个请求队列以及回调函数,在资源被释放的时候,进行回调。
    • 方案二:设置最长等待时间,在此时间段内进行轮询,如果超过此时间,放弃请求。

    对于方案一,请求队列可能会很长,如果前面的资源不释放,就会一直等待;对于方案二,轮询需要消耗更多的资源。

    我们这里说明方案二的实现(目前最常用的转阻塞锁的方案)

    let redis = require("redis");
    let client = redis.createClient(config.redisPort,config.serverIp);
    let redlock = new Redlock([client]);
    let totalTime = 5000;
    let key = "to change: as you like";
    let intervalTime = 50;  // 轮询时间间隔
    
    let sleep = function(ms) {
        return new Promise(resolve => setTimeout(resolve, ms))
    }
    
    let tag = 0;
    for(let j = 0; j<200; j++){
        redlock.lock(key, totalTime).then(async function(lock){
            if(tag === 1){
                lock.unlock().catch(function(err){})
            }
            else{
                tag = 1
                // to do
                // your operation block
                // release lock
                lock.unlock().catch(function(err){})
            }
        }).catch(()=>{})
        if(tag === 1){
            break
        }
        await sleep(intervalTime) // 设置间隔时间
    }
    if(tag === 0){
        errorMsg = "请求超时"
        return ;
    }
    

    3. 效果检验

    加锁效果

    在预约与检票单项测试的时候,1000个人中只有一个人成功,符合预期。

    加锁效果

    在预约与更改琴房可用时间混合测试的时候,2000个人中只有一个人成功,符合预期。

    以上,说明这样加锁,可以成功解决资源访问竞争情况。

    4. 参考资料

    1. redlock 效果解析

    2. redlock git地址

    相关文章

      网友评论

          本文标题:Redlock实践--非阻塞模式

          本文链接:https://www.haomeiwen.com/subject/grmklqtx.html