进程锁设计逻辑:
使用时,在调用加锁函数之后,写defer释放锁函数。
/*数据库脚本
use cloudeventserver;
// ----------------------------
// Collection structure for lock
// ----------------------------
db.getCollection("lock").drop();
db.createCollection("lock");
db.getCollection("lock").createIndex({
"lock_key": NumberInt("-1")
}, {
name: "lock_key_",
unique: true
});
// ----------------------------
// Documents of lock
// ----------------------------
db.getCollection("lock").insert([ {
_id: ObjectId("60f1457b8853e3b27c1ad23a"),
"lock_key": "cloud_event_lock",
"is_lock": NumberInt("0"),
"update_time": NumberLong("1626428220")
} ]);
*/
const (
UNLOCK_STATUS = 0 //未锁定状态
LOCK_STATUS = 1 //锁定状态
MongoDocumentNotFound = "no documents in result"
)
//解锁
func unLock(ctx context.Context, key string) error {
var (
collection *mongo.Collection
filter *bson.M
upsert = false
err error
)
filter = &bson.M{"lock_key": key}
collection = config.Pool.Database("cloudeventserver").Collection("lock")
update := &bson.M{"$set": &bson.M{"is_lock": UNLOCK_STATUS, "update_time": time.Now().Unix()}}
err = collection.FindOneAndUpdate(ctx, filter, update, &options.FindOneAndUpdateOptions{
Upsert: &upsert}).Err()
if err != nil && !strings.Contains(err.Error(), MongoDocumentNotFound) {
uclog.Error("unLock failed error:%s", err)
return err
}
return nil
}
//加锁
func lock(ctx context.Context, key string) error {
var (
collection *mongo.Collection
filter *bson.M
upsert = false
err error
)
now := time.Now().Unix()
//对已经释放的锁执行加锁操作
filter = &bson.M{"lock_key": key, "is_lock": UNLOCK_STATUS}
collection = config.Pool.Database("cloudeventserver").Collection("lock")
update := &bson.M{"$set": &bson.M{"is_lock": LOCK_STATUS, "update_time": now}}
opts := &options.FindOneAndUpdateOptions{
Upsert: &upsert}
err = collection.FindOneAndUpdate(ctx, filter, update, opts).Err()
if err != nil {
//只要加锁失败,则返回错误,结束本次任务
//如果执行加锁失败,有可能是因为锁没有释放成功,则尝试着对已经过期未释放的锁进行解锁
expireTime := now - beego.AppConfig.DefaultInt64("LockExpireTime", 5)*60
filter = &bson.M{"lock_key": key, "is_lock": LOCK_STATUS, "update_time": &bson.M{"$lt": expireTime}}
update := &bson.M{"$set": &bson.M{"is_lock": UNLOCK_STATUS, "update_time": now}}
collection.FindOneAndUpdate(ctx, filter, update, opts).Err()
uclog.Error("lock failed:%+s", err.Error())
return err
}
return nil
}