在以往的项目中,遇到高并发大流量需求做并发控制的时候一般都使用redis分布式锁或者mysql加锁处理高并发情况。最近遇到一个php项目,没有安装redis,由于某种原因也不考虑使用mysql加锁控制并发,所以采用文件锁的方式控制并发,整理了下代码

php版本

class FileLock
{
    /** @var string 锁名称 唯一性 */
    private string $key;

    /** @var string 锁文件 */
    private string $file = "";
    /** @var 文件资源 */
    private $fp = null;

    public function __construct(string $key)
    {
        if (empty($key)) {
            throw new \InvalidArgumentException("key 不能为空");
        }
        $this->key = $key;
    }

    /**
     * 加锁
     */
    public function lock(): bool
    {
        // 文件路径
        $file = "lock_{$this->key}.txt";
        $this->file = $file;
        $fp = fopen($file, "w+");
        if (is_resource($fp)) {
            $this->fp = $fp;
        } else {
            return false;
        }
        return flock($fp, LOCK_EX);
    }


    public function unlock(): bool
    {
        if (is_resource($this->fp)) {
            return flock($this->fp, LOCK_UN); // 释放锁
        }
        return false;
    }

    public function __destruct()
    {
        if (is_resource($this->fp)) {
            @flock($this->fp, LOCK_UN);
        }
        if (is_file($this->file)) {
            @unlink($this->file);
        }
    }

}

php代码

go版本

同时整理go版本实现

package main

import (
    "fmt"
    "os"
    "syscall"
    "time"
)

// FileLock 实现文件锁定
type FileLock struct {
    file string
    f    *os.File
    how  int
}

func (l *FileLock) How(how int) {
    l.how = how
}

func New(file string) *FileLock {
    return &FileLock{
        file: file,
        how:  syscall.LOCK_EX | syscall.LOCK_NB, // 默认非阻塞
    }
}

// Lock 加锁
func (l *FileLock) Lock() error {
    f, err := os.Create(l.file)
    if err != nil {
        return err
    }
    l.f = f
    // syscall.LOCK_NB 非阻塞
    if err := syscall.Flock(int(f.Fd()), l.how); err != nil {
        return err
    }
    return nil
}

// Unlock 释放锁
func (l *FileLock) Unlock() error {
    defer l.f.Close()
    if err := syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN); err != nil {
        // 可以增加报警 或者 直接删除文件
        return err
    }
    return nil
}

func main() {
    work()
}

func work() {
    // 给该方法加锁
    lockedFile := "/tmp/my_lock.lock"
    flock := New(lockedFile)
    //flock.How(syscall.LOCK_EX)    阻塞
    err := flock.Lock()
    if err != nil {
        // 获取锁失败
        fmt.Println(err.Error())
        return
    }
    defer flock.Unlock()

    fmt.Println("执行业务代码,模拟长时间")
    time.Sleep(time.Second * 50)
}

go代码


tim_xiao
144 声望2 粉丝

后端程序员