nlocks/lock.namedpipe.js

349 lines
9.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// named-pipe-server.js
const net = require('net');
const fs = require('fs');
const path = require('path');
const { EventEmitter } = require('events');
const async_hooks = require('async_hooks');
const { v4: uuidv4 } = require('uuid');
const DEFAULT_PIPE_PATH = process.platform === 'win32'? '\\\\.\\pipe\\rwlock' : '/tmp/rwlock.sock';
/**
* 基于命名管道的读写锁服务器
* 提供跨进程的读写锁功能,支持多个客户端同时请求对同一资源的读锁或写锁
* 实现读写锁的基本语义:
* 1. 多个读操作可以同时进行
* 2. 写操作是独占的,不允许同时进行读或其他写操作
* 3. 锁请求按先进先出(FIFO)方式处理
*/
class NamedPipeLockServer extends EventEmitter {
/**
* 创建一个NamedPipeLockServer实例
* @param {string} pipePath - 命名管道路径,默认值根据操作系统确定
*/
constructor(pipePath = DEFAULT_PIPE_PATH) {
super();
this.pipePath = pipePath;
this.server = null;
this.locks = new Map(); // resource -> { readers: Set, writer: null, queue: [] }
this.clients = new Map(); // clientId -> socket
}
/**
* 启动服务器
* 创建并监听命名管道,处理客户端连接和消息
*/
start() {
// 确保管道文件不存在Windows
if (process.platform === 'win32') {
try {
if (fs.existsSync(this.pipePath)) {
fs.unlinkSync(this.pipePath);
}
} catch (error) {
// 忽略错误,可能管道正在使用
}
}
this.server = net.createServer(socket => {
const clientId = uuidv4();
socket.clientId = clientId;
this.clients.set(clientId, socket);
console.log(`Client connected: ${clientId}`);
socket.on('data', data => {
try {
const messageStrs = data.toString().split('\n');
messageStrs.forEach(messageStr => {
if (messageStr) {
const message = JSON.parse(messageStr);
this.handleMessage(clientId, message);
}
});
// const message = JSON.parse();
// this.handleMessage(clientId, message);
} catch (error) {
console.error('Error parsing message:', error);
this.sendError(clientId, 'Invalid message format');
}
});
socket.on('close', () => {
this.handleClientDisconnect(clientId);
this.clients.delete(clientId);
console.log(`Client disconnected: ${clientId}`);
});
socket.on('error', error => {
console.error(`Client error (${clientId}):`, error);
this.handleClientDisconnect(clientId);
this.clients.delete(clientId);
});
});
// 启动命名管道服务器
this.server.listen(this.pipePath, () => {
console.log(`Lock server listening on named pipe: ${this.pipePath}`);
});
this.server.on('error', error => {
console.error('Server error:', error);
});
}
/**
* 处理来自客户端的消息
* @param {string} clientId - 客户端标识符
* @param {Object} message - 消息对象
*/
handleMessage(clientId, message) {
const { type, resource, requestId } = message;
if (!this.locks.has(resource)) {
this.locks.set(resource, {
readers: new Set(),
writer: null,
queue: []
});
}
const lock = this.locks.get(resource);
switch (type) {
case 'readLock':
this.handleReadLock(clientId, resource, requestId, lock);
break;
case 'writeLock':
this.handleWriteLock(clientId, resource, requestId, lock);
break;
case 'unlock':
this.handleUnlock(clientId, resource, lock);
break;
default:
this.sendError(clientId, `Unknown message type: ${type}`);
}
}
/**
* 处理读锁请求
* @param {string} clientId - 客户端标识符
* @param {string} resource - 资源名称
* @param {string} requestId - 请求ID
* @param {Object} lock - 锁对象
*/
handleReadLock(clientId, resource, requestId, lock) {
if (!lock.writer) {
// 可以立即获取读锁
lock.readers.add(clientId);
this.sendToClient(clientId, {
type: 'lockGranted',
requestId,
resource,
lockType: 'read'
});
console.log(`Read lock granted to ${clientId} for ${resource}`);
} else {
// 加入等待队列
lock.queue.push({ clientId, type: 'read', requestId });
console.log(`Read lock queued for ${clientId} for ${resource}`);
}
}
/**
* 处理写锁请求
* @param {string} clientId - 客户端标识符
* @param {string} resource - 资源名称
* @param {string} requestId - 请求ID
* @param {Object} lock - 锁对象
*/
handleWriteLock(clientId, resource, requestId, lock) {
if (lock.readers.size === 0 && !lock.writer) {
// 可以立即获取写锁
lock.writer = clientId;
this.sendToClient(clientId, {
type: 'lockGranted',
requestId,
resource,
lockType: 'write'
});
console.log(`Write lock granted to ${clientId} for ${resource}`);
} else {
// 加入等待队列
lock.queue.push({ clientId, type: 'write', requestId });
console.log(`Write lock queued for ${clientId} for ${resource}`);
}
}
/**
* 处理解锁请求
* @param {string} clientId - 客户端标识符
* @param {string} resource - 资源名称
* @param {Object} lock - 锁对象
*/
handleUnlock(clientId, resource, lock) {
let released = false;
// 移除读锁
if (lock.readers.has(clientId)) {
lock.readers.delete(clientId);
released = true;
console.log(`Read lock released by ${clientId} for ${resource}`);
}
// 移除写锁
if (lock.writer === clientId) {
lock.writer = null;
released = true;
console.log(`Write lock released by ${clientId} for ${resource}`);
}
if (released) {
// 处理等待队列
this.processQueue(resource, lock);
}
}
/**
* 处理等待队列中的锁请求
* 根据读写锁规则,尽可能多地授权锁请求
* @param {string} resource - 资源名称
* @param {Object} lock - 锁对象
*/
processQueue(resource, lock) {
const granted = [];
for (let i = 0; i < lock.queue.length; i++) {
const request = lock.queue[i];
if (request.type === 'read') {
if (!lock.writer) {
// 可以授予读锁
lock.readers.add(request.clientId);
this.sendToClient(request.clientId, {
type: 'lockGranted',
requestId: request.requestId,
resource,
lockType: 'read'
});
granted.push(i);
console.log(`Queued read lock granted to ${request.clientId} for ${resource}`);
} else {
// 有写锁等待,读锁必须等待
break;
}
} else { // write
if (lock.readers.size === 0 && !lock.writer) {
// 可以授予写锁
lock.writer = request.clientId;
this.sendToClient(request.clientId, {
type: 'lockGranted',
requestId: request.requestId,
resource,
lockType: 'write'
});
granted.push(i);
console.log(`Queued write lock granted to ${request.clientId} for ${resource}`);
} else {
// 写锁必须等待前面的锁释放
break;
}
}
}
// 移除已处理的请求
if (granted.length > 0) {
lock.queue = lock.queue.filter((_, index) => !granted.includes(index));
}
}
/**
* 处理客户端断开连接
* 释放该客户端持有的所有锁,并处理相关队列
* @param {string} clientId - 客户端标识符
*/
handleClientDisconnect(clientId) {
console.log(`Processing disconnect for client: ${clientId}`);
// 释放该客户端持有的所有锁
for (const [resource, lock] of this.locks) {
let released = false;
if (lock.readers.has(clientId)) {
lock.readers.delete(clientId);
released = true;
}
if (lock.writer === clientId) {
lock.writer = null;
released = true;
}
// 如果释放了锁,重新处理队列
if (released) {
this.processQueue(resource, lock);
}
}
}
/**
* 向指定客户端发送消息
* @param {string} clientId - 客户端标识符
* @param {Object} message - 要发送的消息对象
*/
sendToClient(clientId, message) {
const socket = this.clients.get(clientId);
if (socket && !socket.destroyed) {
try {
socket.write(JSON.stringify(message));
} catch (error) {
console.error(`Error sending to client ${clientId}:`, error);
}
}
}
/**
* 向客户端发送错误消息
* @param {string} clientId - 客户端标识符
* @param {string} errorMessage - 错误信息
*/
sendError(clientId, errorMessage) {
this.sendToClient(clientId, {
type: 'error',
message: errorMessage
});
}
/**
* 停止服务器
*/
stop() {
if (this.server) {
this.server.close();
console.log('Lock server stopped');
}
}
}
// // 启动服务器
// const pipePath = process.platform === 'win32'
// ? '\\\\.\\pipe\\rwlock-server'
// : '/tmp/rwlock.sock';
// const server = new NamedPipeLockServer(pipePath);
// server.start();
// // 优雅关闭
// process.on('SIGINT', () => {
// console.log('Shutting down lock server...');
// server.stop();
// process.exit(0);
// });
// process.on('SIGTERM', () => {
// console.log('Shutting down lock server...');
// server.stop();
// process.exit(0);
// });
module.exports = NamedPipeLockServer;