使用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; } }