rust语言实现redis分布式锁

rust语言实现redis分布式锁

1.Cargo.toml中引入rustredis连接库

mobc连接池

mobc = "0.8.1"
mobc-redis = { version = "0.8.0", default-features = false, features = ["async-std-comp"] }
2.连接池实例的创建

MyErr为自定义错误类型
RedisConfig为自定义配置结构体
在连接时进行一系列的配置,包括库号密码连接数等

use super::cfg::RedisConfig;
use crate::errs::MyErr;
use mobc::Pool;
use mobc_redis::redis;
use mobc_redis::RedisConnectionManager;

pub async fn new_redis(cfg: RedisConfig) -> Result<Pool<RedisConnectionManager>, MyErr> {
    let client = redis::Client::open(redis::ConnectionInfo {
        addr: redis::ConnectionAddr::Tcp(cfg.host.to_string(), cfg.port),
        redis: redis::RedisConnectionInfo {
            db: cfg.db,
            username: None,
            password: Some(cfg.pass.to_string()),
        },
    })?;
    let manager = RedisConnectionManager::new(client);
    let pool = Pool::builder().max_open(2).build(manager);
    Ok(pool)
}

3.定义redis锁所使用的lua脚本
a.检测锁是否存在 用于锁自旋等待

noKey 没有这个锁
myKey 当前检测者为这个锁的持有者,
otherKey 锁存在但不是检测者的

-- NOTE: 根据key是否存在且key中id是不是调用者的来进行返回
-- 结果只有可能是 noKey myKey otherKey
local keyVal = redis.call("GET", KEYS[1])
if not keyVal then -- 这里是结果false 文档里是nil 需要注意
    return "noKey"    -- key 不存在
else
    if keyVal == ARGV[1] then -- 是我的锁
        return "myKey" -- key存在且是我的
    else
        return "otherKey" -- key存在但不是我的
    end
end
b.解锁操作 删除锁

noKey 锁不存在
OK 解锁成功
otherKey 锁不是当前操作者的,不能解锁

-- 如果锁是我的则删除这个锁
local keyVal = redis.call("GET", KEYS[1])
if not keyVal then
    return "noKey" -- key 不存在
else
    if keyVal == ARGV[1] then
        redis.call("DEL", KEYS[1]) -- key存在且是我的 可以删除了 肯定成功返回1
        return "OK" -- 一定返回OK
    else
        return "otherKey" -- key 存在 不是我的
    end
end
c.锁定操作 获得锁操作

OKCreate 锁成功创建并锁定
OKExtend 锁是我的并成功延续-锁续命成功
otherKey 锁不是我的,无法加锁

-- 锁结构=> key:锁Key value:当前请求者id
-- 锁定与锁重入 返回otherKey或OK 只有这两个值
local keyVal = redis.call("GET", KEYS[1])
if not keyVal then -- 这里是 false 文档里是nil 一定返回'OK'
    redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2]) -- 没有这个key 写入这个key
    return "OKCreate"
else
    if keyVal == ARGV[1] then
        redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2]) -- 是我的key 刷新有效时间 一定返回'OK'
        return "OKExtend"
    else
        return "otherKey" -- 是别人的key
    end
end
d.锁重入续命 用于锁守护

noKey 没有这个锁,锁重入失败
OK 锁重入成功-锁被成功续命
otherKey 锁不是我的 无权进行此操作

-- 锁重入 返回otherKey或OK 只有这两个值
local keyVal = redis.call("GET", KEYS[1])
if not keyVal then
    return "noKey"
else
    if keyVal == ARGV[1] then
        redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2]) -- 是我的key 刷新有效时间 一定返回'OK'
        return "OK"
    else
        return "otherKey" -- 是别人的key
    end
end
4.rust调用实现
a.结构定义
// lua脚本
const CHECK: &str = include_str!("lockLua/checkCommand.lua");
const UNLOCK: &str = include_str!("lockLua/delCommand.lua");
const LOCK: &str = include_str!("lockLua/lockCommand.lua");
const RELOCK: &str = include_str!("lockLua/reLockCommand.lua");
// 锁信息结构
pub struct RedisLock {
    lock_msec: i32, // 锁持续时长 毫秒
    key: String, // 锁key
    id: String, // 锁id
    redis_pool: Pool<RedisConnectionManager>, // redis连接池
    watch_handle: Option<(JoinHandle<()>, oneshot::Sender<()>)>, // 锁守护
}
b.加锁操作
// 获得锁的操作
pub async fn lock(&mut self) -> Result<bool, MyErr> {
    let mut conn = self.redis_pool.get().await?; // 从连接池里获得一个redis连接
    let x: String = redis::cmd("eval") // 执行eval命令
        .arg(LOCK) // 锁定的lua脚本
        .arg(1) // 有一个key
        .arg(self.key.to_owned()) // 有三个args ..key
        .arg(self.id.to_owned()) // ..id
        .arg(self.lock_msec) // ..锁保持时长
        .query_async(&mut conn as &mut aio::Connection) // 异步执行
        .await?;
    println!("锁取得 res:{}", x);
    match &x as &str {
        "OKCreate" => {
            // 如果是首次加锁成功 则打开锁守护 进行一秒一次的锁续命操作
            println!("打开锁守护");
            let mut new_lock = self.copy(); // 锁参数copy一份
            let (sx, mut rx) = oneshot::channel(); // 消息chan用于关闭守护工作者
            let dog = tokio::spawn(async move {
                println!("打开锁守护 on");
                // 一秒一次的定时器
                let mut intv = time::interval_at(Instant::now(), Duration::from_secs(1));
                intv.set_missed_tick_behavior(MissedTickBehavior::Delay);
                // 开始守护循环
                loop {
                    println!("打开锁守护 loop");
                    // 等待事件发生
                    select! {
                        _ = intv.tick() => {
                            let res = new_lock.re_lock().await; // 锁重入
                            println!("打开锁守护 reLock pass");
                            match res {
                                Ok(_) => continue,
                                Err(_) => break,
                            }
                        }
                        _ = &mut rx => {
                            println!("锁守护 收到退出信号"); // 守护退出
                            break
                        }
                    }
                }
                println!("打开锁守护 end");
            });
            self.watch_handle = Some((dog, sx));
            Ok(true)
        }
        "OKExtend" => Ok(true), // 锁续命成功
        "otherKey" => Ok(false), // 锁已经存在
        _ => Err(MyErr::new(
            ErrKind::Internal(format!("redis err {}", x)),
            None,
        )),
    }
}
c.解锁操作
pub async fn un_lock(&mut self) -> Result<(), MyErr> {
    // 释放守护工作者
    if self.watch_handle.is_some() {
        let (h, r) = self.watch_handle.take().unwrap();
        r.send(()).unwrap();
        h.await?;
    }
    // 删除key
    let mut conn = self.redis_pool.get().await?;
    let x: String = redis::cmd("eval")
        .arg(UNLOCK)
        .arg(1)
        .arg(self.key.to_owned())
        .arg(self.id.to_owned())
        .query_async(&mut conn as &mut aio::Connection)
        .await?;
    println!("锁释放 res:{}", x);
    match &x as &str {
        "OK" => Ok(()),
        "noKey" => Err(MyErr::new(
            ErrKind::Internal(format!("unlock no key")),
            None,
        )),
        "otherKey" => Err(MyErr::new(
            ErrKind::Internal(format!("unlock otherKey")),
            None,
        )),
        _ => Err(MyErr::new(
            ErrKind::Internal(format!("redis err {}", x)),
            None,
        )),
    }
}
d.锁等待
// 锁等待直到释放
pub async fn block_until_release(&mut self) -> Result<(), MyErr> {
    let mut conn = self.redis_pool.get().await?;
    loop {
        let x: String = redis::cmd("eval")
            .arg(CHECK)
            .arg(1)
            .arg(self.key.to_owned())
            .arg(self.id.to_owned())
            .query_async(&mut conn as &mut aio::Connection)
            .await?;
        println!("锁等待 res:{}", x);
        match &x as &str {
            "noKey" => return Ok(()), // 锁不存在-锁等待成功
            "myKey" => {
                return Err(MyErr::new(
                    ErrKind::Internal(format!("wait on my self lock")),
                    None,
                ))
            }
            "otherKey" => {
                sleep(Duration::from_millis(100)).await; // 休眠100ms然后继续检测锁状态
                continue;
            }
            _ => {
                return Err(MyErr::new(
                    ErrKind::Internal(format!("redis err {}", x)),
                    None,
                ))
            }
        }
    }
}
e.锁重入操作
// 锁重入
pub async fn re_lock(&mut self) -> Result<(), MyErr> {
    let mut conn = self.redis_pool.get().await?;
    let x: String = redis::cmd("eval")
        .arg(RELOCK)
        .arg(1)
        .arg(self.key.to_owned())
        .arg(self.id.to_owned())
        .arg(self.lock_msec)
        .query_async(&mut conn as &mut aio::Connection)
        .await?;
    println!("锁重入 res:{}", x);
    match &x as &str {
        "noKey" => Err(MyErr::new(
            ErrKind::Business("reLock noKey".to_string()),
            None,
        )),
        "OK" => Ok(()), // 反回成功锁重入成功
        "otherKey" => Err(MyErr::new(
            // 不是我的锁
            ErrKind::Business("reLock otherKey".to_string()),
            None,
        )),
        _ => Err(MyErr::new(
            ErrKind::Internal(format!("redis err {}", x)),
            None,
        )),
    }
}
5.对锁操作进行包装

不带锁等待

pub async fn lock_action<'a, F, R>(
    conn: Pool<RedisConnectionManager>,
    key: &str,
    fn_on_lock: F,
) -> Result<R, MyErr>
where
    F: FnOnce() -> BoxFuture<'a, Result<R, MyErr>> + Send + Sync,
{
    let mut lock = RedisLock::new(conn.clone(), key);
    match lock.lock().await? { // 加锁
        true => {
            let res = fn_on_lock().await?;
            lock.un_lock().await?; // 解锁
            Ok(res)
        }
        false => Err(MyErr::new(
            ErrKind::Business("请求锁失败".to_string()),
            None,
        )),
    }
}

带锁等待


pub async fn lock_action_with_spin_out<'a, F, F2, R>(
    conn: Pool<RedisConnectionManager>,
    key: &str,
    fn_on_lock: F,
    fn_on_lock_spin_out: F2,
) -> Result<R, MyErr>
    where
        F: FnOnce() -> BoxFuture<'a, Result<R, MyErr>> + Send + Sync,
        F2: FnOnce() -> BoxFuture<'a, Result<R, MyErr>> + Send + Sync,
{
    let mut lock = RedisLock::new(conn.clone(), key);
    match lock.lock().await? { // 加锁
        true => match fn_on_lock().await {
            Ok(res) => {
                lock.un_lock().await?; // 解锁 
                Ok(res)
            }
            Err(err) => {
                lock.un_lock().await?; // 解锁
                Err(err)
            }
        },
        false => {
            lock.block_until_release().await?; // 等待直到锁释放
            let res = fn_on_lock_spin_out().await?;
            Ok(res)
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独杆小蓬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值