在分布式系统中,分布式锁是一种常用的机制,用于保证同一时间只能有一个进程或线程对某一资源进行操作。在Spring Boot应用中,我们可以利用MongoDB的findAndModify操作来实现分布式锁。MongoDB的findAndModify是一个原子性操作,可以在同一时间内更新一个文档并返回其原始值,这使得它成为实现分布式锁的一个很好的选择。

分布式锁的实现原理

分布式锁的核心思想是通过唯一的标识(例如锁的名称)来控制资源的访问。在MongoDB中,我们可以定义一个lock集合,存储锁的信息。每次需要获取锁时,我们尝试使用findAndModify方法更新锁的状态。成功获取锁的进程或线程可以执行对应的操作,而未能获取锁的则需要等待或重试。

锁的结构

我们可以设计一个简单的锁结构,如下所示:

{
  "_id": "myLock", // 锁的名称
  "isLocked": false, // 锁的状态
  "lockedBy": null, // 锁的持有者
  "expiresAt": null // 锁的过期时间
}

实现步骤

  1. 初始化锁:在应用启动时,如果 lock 集合中没有该锁,则插入一条记录。
  2. 获取锁:使用findAndModify尝试将锁状态更新为已锁定。
  3. 释放锁:当任务完成后,释放锁。
  4. 自动过期:设置锁的过期时间,避免因异常而造成的死锁。

代码示例

下面是一个简单的Spring Boot应用中实现分布式锁的例子:

1. 定义锁的实体类

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "locks")
public class Lock {
    @Id
    private String id;
    private boolean isLocked;
    private String lockedBy;
    private long expiresAt;

    // getters and setters
}

2. 服务类实现分布式锁逻辑

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class LockService {
    @Autowired
    private MongoTemplate mongoTemplate;

    private static final String LOCK_ID = "myLock";
    private static final long LOCK_EXPIRE_TIME = TimeUnit.MINUTES.toMillis(5);

    public boolean acquireLock(String requestId) {
        long currentTime = System.currentTimeMillis();
        Query query = new Query(Criteria.where("_id").is(LOCK_ID)
                .and("isLocked").is(false)
                .orOperator(Criteria.where("expiresAt").lt(currentTime))); // 当前时间大于过期时间

        Update update = new Update()
                .set("isLocked", true)
                .set("lockedBy", requestId)
                .set("expiresAt", currentTime + LOCK_EXPIRE_TIME);

        Lock lock = mongoTemplate.findAndModify(query, update, Lock.class);
        return lock != null; // 返回是否获取锁成功
    }

    public void releaseLock(String requestId) {
        Query query = new Query(Criteria.where("_id").is(LOCK_ID).and("lockedBy").is(requestId));
        Update update = new Update().set("isLocked", false).unset("lockedBy").unset("expiresAt");
        mongoTemplate.updateFirst(query, update, Lock.class);
    }
}

3. 使用锁的示例

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LockController {
    @Autowired
    private LockService lockService;

    @PostMapping("/process")
    public String process(@RequestParam String requestId) {
        if (lockService.acquireLock(requestId)) {
            try {
                // 执行需要互斥的操作
                return "Processing done by " + requestId;
            } finally {
                lockService.releaseLock(requestId); // 确保释放锁
            }
        } else {
            return "Unable to acquire lock, please try again later.";
        }
    }
}

总结

通过上述实现,我们利用MongoDB的原子操作findAndModify成功实现了分布式锁的功能。在实际应用中,可以根据需要调整锁的过期时间、加锁的策略等,以满足特定的业务需求。此种方式简洁高效,适合多种分布式场景。

点赞(0) 打赏

微信小程序

微信扫一扫体验

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部