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












