Golang编写一个ID锁在PHP中使用(Unix Socket套接字)

使用Unix Socket进行通讯,只能在linux环境使用。若在win运行需要自行更改Unix Socket为其他方式!

GO代码(服务端)

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "net"
    "os"
    "sync"
    "time"
)

var myidLock *IDLock
var wg sync.WaitGroup
var wg2 sync.WaitGroup

type IDLock struct {
    mu        sync.Mutex
    lockedIDs map[string]mytime
    expiry    time.Duration
}
type mytime struct {
    extime time.Time     // 锁定时间
    expiry time.Duration // 过期时间
}

func init() {
    myidLock = NewIDLock(500 * time.Millisecond) // 创建默认锁过期时间
}

func NewIDLock(expiry time.Duration) *IDLock {
    return &IDLock{
       lockedIDs: make(map[string]mytime),
       expiry:    expiry,
    }
}

func (l *IDLock) clean() {
    l.mu.Lock()
    defer l.mu.Unlock()

    now := time.Now()
    for key, lockedTime := range l.lockedIDs {
       if now.Sub(lockedTime.extime) > lockedTime.expiry {
          delete(l.lockedIDs, key)
       }
    }
}

func (l *IDLock) Lock(id string, expiry time.Duration) bool {
    l.mu.Lock()
    defer l.mu.Unlock()

    if lockedTime, ok := l.lockedIDs[id]; ok && time.Since(lockedTime.extime) < lockedTime.expiry {
       return false // ID already locked and not expired
    }
    if expiry != 0 && expiry > 0 {
       l.lockedIDs[id] = mytime{time.Now(), expiry} // 用户传入的
    } else {
       l.lockedIDs[id] = mytime{time.Now(), l.expiry} // 默认的
    }
    return true
}

func (l *IDLock) Unlock(id string) {
    l.mu.Lock()
    defer l.mu.Unlock()

    delete(l.lockedIDs, id)
}

func (l *IDLock) TryLock(id string, expiry time.Duration, retryInterval time.Duration, maxRetries int) bool {
    if retryInterval <= 0 || maxRetries <= 0 {
       return false
    }

    for i := 0; i < maxRetries; i++ {
       if l.Lock(id, expiry) {
          return true
       }
       time.Sleep(retryInterval)
    }
    return false
}

type Request struct {
    Method        string `json:"method"`
    ID            string `json:"id"`
    Expiry        int    `json:"expiry"`
    RetryInterval int    `json:"retry_interval"`
    MaxRetries    int    `json:"max_retries"`
}

type Response struct {
    Status  string `json:"status"`
    Message string `json:"message"`
}

func handleConnection(conn net.Conn, idLock *IDLock) {
    defer conn.Close()

    decoder := json.NewDecoder(conn)
    encoder := json.NewEncoder(conn)
    for {
       var req Request
       if err := decoder.Decode(&req); err != nil {
          //fmt.Println("解码请求失败:", err)
          return
       }
       resp := handleRequest(req, idLock)
       if err := encoder.Encode(resp); err != nil {
          //fmt.Println("编码响应失败:", err)
          return
       }
    }
}

func handleRequest(req Request, idLock *IDLock) Response {
    var resp Response
    switch req.Method {
    case "Lock":
       if idLock.Lock(req.ID, time.Duration(req.Expiry)*time.Millisecond) {
          resp.Status = "success"
          resp.Message = "Lock acquired"
       } else {
          resp.Status = "failure"
          resp.Message = "Lock failed"
       }
    case "Unlock":
       idLock.Unlock(req.ID)
       resp.Status = "success"
       resp.Message = "Lock released"
    case "TryLock":
       if idLock.TryLock(req.ID, time.Duration(req.Expiry)*time.Millisecond, time.Duration(req.RetryInterval)*time.Millisecond, req.MaxRetries) {
          resp.Status = "success"
          resp.Message = "Lock acquired"
       } else {
          resp.Status = "failure"
          resp.Message = "Lock failed"
       }
    default:
       resp.Status = "failure"
       resp.Message = "Unknown method"
    }
    return resp
}

func goclean(ctx context.Context) {
    ticker := time.NewTicker(time.Second * 1800) //定时器半个小时一次
    defer ticker.Stop()
    for {
       select {
       case <-ticker.C:
          myidLock.clean()
       case <-ctx.Done():
          return
       }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    wg2.Add(1)
    go func() {
       defer wg2.Done()
       goclean(ctx)
    }()

    // 获取当前可执行文件的完整路径

    socketPath := "/tmp/idlock.sock"

    // 检查并删除已存在的套接字文件
    if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) {
       fmt.Println("Failed to remove existing socket:", err)
       return
    }

    listener, err := net.Listen("unix", socketPath)
    if err != nil {
       fmt.Println("启动服务器错误:", err)
       return
    }
    defer listener.Close()

    fmt.Println("Unix Socket:", socketPath)

    for {
       conn, err := listener.Accept()
       if err != nil {
          fmt.Println("监听链接错误:", err)
          continue
       }
       wg.Add(1)
       go func(conn net.Conn) {
          defer wg.Done()
          handleConnection(conn, myidLock)
       }(conn)
    }
    wg2.Wait()
}


PHP代码(客户端)

<?php

class Request {
public $method;
public $id;
public $expiry;
public $retry_interval;
public $max_retries;

public function __construct($method, $id, $expiry = 0, $retry_interval = 0, $max_retries = 0) {
$this->method = $method;
$this->id = $id;
$this->expiry = $expiry;
$this->retry_interval = $retry_interval;
$this->max_retries = $max_retries;
}

public function toJson() {
return json_encode($this);
}
}
class lock
{
private $socketPath;
function __construct()
{
$this->socketPath = "/tmp/idlock.sock";//自行修改参数
}

function sendRequest( $request) {
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
if ($socket === false) {
// echo "Socket creation failed: " . socket_strerror(socket_last_error()) . "\n";
return null;
}

if (socket_connect($socket, $this->socketPath) === false) {
// echo "Socket connection failed: " . socket_strerror(socket_last_error($socket)) . "\n";
socket_close($socket);
return null;
}

$requestJson = $request->toJson();
socket_write($socket, $requestJson, strlen($requestJson));

$responseJson = socket_read($socket, 1024);
socket_close($socket);

if ($responseJson === false) {
// echo "Socket read failed: " . socket_strerror(socket_last_error($socket)) . "\n";
return null;
}

return json_decode($responseJson);
}

public function lock($id,$expiry=0,$retry_interval=1,$max_retries=1)
{
if (is_string($id)) {
// 示例请求
$response = $this->sendRequest( new Request("TryLock", $id, $expiry, $retry_interval, $max_retries));

if ($response !== null && $response->status=="success") {
return true;
//echo "响应: " . $response->status . " - " . $response->message . "\n";
}
return false;
}
return false;
}

public function unlock($id)
{
if (is_string($id)) {

$response = $this->sendRequest(new Request("Unlock", $id));

if ($response !== null && $response->status=="success") {
return true;
// echo "响应: " . $response->status . " - " . $response->message . "\n";
}
//message字段返回失败
return false;
}
return false;
}
}
文章地址:漫夜 » Golang编写一个ID锁在PHP中使用(Unix Socket套接字)
分享到:
赞(0) 打赏

网站不错赶快打赏吧!

支付宝扫一扫打赏

微信扫一扫打赏