rust
语言实现redis
分布式锁
1.Cargo.toml
中引入rust
的redis
连接库
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)
}
}
}