You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NFS上实现正确的文件锁?Python多进程场景适配

嘿,刚好我之前在NFS环境下折腾过Python多进程文件锁的问题,给你梳理下正确的实现方式和需要注意的坑~

NFS上fcntl文件锁的核心前提

首先得明确:NFS本身是无状态协议,它的文件锁依赖系统的两个服务——lockd和statd。这俩服务负责跟踪锁的持有者和处理锁的失效(比如进程崩溃后释放锁)。所以第一步要确保你的Linux/macOS系统上这两个服务是正常运行的:

  • Linux(比如Ubuntu/Debian):安装nfs-common包,然后启动rpc-lockd和rpc-statd服务
  • macOS:这俩服务默认是随NFS客户端启动的,你可以用ps aux | grep lockd确认是否在运行
正确的Python实现步骤

接下来是代码层面的细节,这些点直接决定锁在NFS上能不能正常工作:

  • 文件打开方式要正确:必须用二进制模式打开文件,并且使用os.O_RDWR | os.O_CREAT的标志位。锁是和文件描述符绑定的,所以不要用open()函数的文本模式,避免额外的缓冲导致的问题,同时O_CREAT确保文件不存在时会被创建。
  • 用try/finally或上下文管理器管理锁:一定要保证锁能被释放,哪怕中途抛出异常。我个人习惯写一个上下文管理器来封装锁的获取和释放,这样代码更整洁。
  • 遵守建议性锁规则:NFS上的fcntl锁是建议性锁,也就是说所有访问这个文件的进程都必须主动获取锁才能操作,不然锁完全起不到作用。所以你的所有进程(包括可能的外部工具)都要遵循这个逻辑。
完整代码示例

这里给你一个我实际用过的RecordManager类实现,已经适配了NFS环境:

import fcntl
import os
import json

class RecordManager:
    def __init__(self, storage_path):
        self.storage_path = storage_path
        # 确保父目录存在
        os.makedirs(os.path.dirname(storage_path), exist_ok=True)

    def _get_locked_file(self):
        # 用os.open获取文件描述符,再转成文件对象
        fd = os.open(
            self.storage_path,
            os.O_RDWR | os.O_CREAT,
            0o644  # 设置合理的文件权限
        )
        file_obj = os.fdopen(fd, 'r+b')
        try:
            # 获取排他锁,阻塞直到获取到
            fcntl.lockf(file_obj, fcntl.LOCK_EX)
            yield file_obj
        finally:
            # 无论如何都要释放锁并关闭文件
            fcntl.lockf(file_obj, fcntl.LOCK_UN)
            file_obj.close()

    def update_record(self, record_key, record_value):
        """更新或添加一条记录"""
        with self._get_locked_file() as f:
            # 读取现有记录
            f.seek(0)
            try:
                records = json.load(f)
            except json.JSONDecodeError:
                # 文件为空或格式错误时初始化空字典
                records = {}
            
            # 更新记录
            records[record_key] = record_value
            
            # 清空文件并写入新内容
            f.seek(0)
            f.truncate()
            json.dump(records, f, indent=2)

    def get_record(self, record_key):
        """获取一条记录,同样需要加锁保证一致性"""
        with self._get_locked_file() as f:
            f.seek(0)
            try:
                records = json.load(f)
                return records.get(record_key)
            except json.JSONDecodeError:
                return None
额外注意事项
  • NFS版本选择:尽量使用NFSv3或更高版本,NFSv2的锁支持非常有限,容易出现各种奇怪的问题。
  • 锁的延迟问题:NFS上的锁同步可能有轻微延迟,所以不要依赖锁的即时性(比如刚释放锁就立刻让另一个进程获取),不过一般业务场景下这个延迟可以忽略。
  • 异常处理:如果lockd或statd服务未运行,加锁时会抛出OSError,建议在代码中捕获这类异常,提示用户检查系统服务状态。

这样实现的话,你的记录管理器应该就能在NFS上安全地支持多进程访问了,我之前用这个方案在生产环境的NFS存储上跑过,没遇到过锁失效的问题~

内容的提问来源于stack exchange,提问作者Ant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:12:59