sonofs
Version:
sono nodejs distributed file system
192 lines (165 loc) • 6.93 kB
JavaScript
const net = require('net');
const { parseFileName } = require('./util');
const socketUtils = require('./socket-utils');
const { MAX_BUNDLE_ID } = require('./consts');
const MAX_CONNECTIONS = Math.pow(2, 32);
class RegistryServer {
static start(cfg, cb) {
const registryServer = new RegistryServer(cfg);
registryServer.start(cb);
}
constructor({ logger = console, ...cfg }) {
this.logger = logger;
this.cfg = cfg;
this.servers = [];
this.server = net.createServer((socket) => {
let type;
let serverCfg;
socket.on('timeout', () => {
this.logger.info('socket timeout');
socket.end();
});
socketUtils.onReceiveBlock(socket, (actionType, buf) => {
type = actionType;
if (type === 1) {
const groupId = buf.readUInt8(1);
const serverId = buf.readUInt8(2);
const isSlave = buf.readUInt8(3);
const port = buf.readUInt32BE(4);
const bundleId = buf.readUIntBE(8, 3);
const bundleSize = buf.readUInt32BE(11);
serverCfg = {
bundleId,
bundleSize,
groupId,
serverId,
isSlave,
port,
host: socket.remoteAddress
};
// console.log('registerServer:', cfg);
this.registerServer(serverCfg);
socketUtils.sendBlock(socket, 1);
} else if (type === 4) {
// 获取Master
const groupId = buf.readUInt8(0);
const serverId = buf.readUInt8(1);
const master = this.getMaster(groupId, serverId);
socketUtils.sendBlock(socket, 1, Buffer.from(JSON.stringify(master), 'utf8'));
} else {
const callbackId = buf.readUIntBE(0, 3);
let server;
if (type === 2) {
// Client Get File
const fileName = buf.toString('utf8', 3);
server = this.getReadServer(fileName);
} else if (type === 3) {
// Client Upload File
server = this.getUploadServer();
}
const callbackIdBuf = Buffer.alloc(3);
callbackIdBuf.writeUIntBE(callbackId, 0, 3);
if (server == null) {
socketUtils.sendBlock(socket, 0, callbackIdBuf);
} else {
socketUtils.sendBlock(socket, 1, Buffer.concat([callbackIdBuf, Buffer.from(JSON.stringify(server), 'utf8')]));
}
}
});
socket.on('close', () => {
if (type === 1 && serverCfg) {
// 文件服务器断联后需要移除服务器
const { groupId, serverId, host, port } = serverCfg;
for (let i = this.servers.length - 1; i >= 0; i--) {
const srv = this.servers[i];
if (srv.groupId === groupId && srv.serverId === serverId && srv.host === host && srv.port === port) {
// remove server
this.servers.splice(i, 1);
this.logger.info('remove server', serverCfg);
}
}
}
});
});
}
start(cb) {
const { server } = this;
server.listen(this.cfg.port, () => {
this.logger.info('start registry on', server.address());
cb && cb();
});
}
registerServer({ groupId, serverId, isSlave, host, port, bundleId, bundleSize }) {
// Register Server
let server = this.servers.find((srv) => (srv.groupId === groupId && serverId === srv.serverId && srv.host === host && srv.port === port));
if (!server) {
server = {
groupId,
serverId,
host,
port,
connections: 0
};
this.servers.push(server);
}
server.isSlave = isSlave;
server.bundleId = bundleId;
server.bundleSize = bundleSize;
server.r_expireAt = Date.now() + 10000;
}
getMaster(groupId, serverId) {
for (let i = 0; i < this.servers.length; i++) {
const server = this.servers[i];
if (server.serverId === serverId && server.groupId === groupId && server.r_expireAt > Date.now() && !server.isSlave) {
return server;
}
}
return null;
}
getReadServer(fileName) {
const { groupId, serverId, bundleId, fileStart, fileSize } = parseFileName(fileName);
let result;
let minConnections = -1;
for (let i = 0; i < this.servers.length; i++) {
const server = this.servers[i];
if (server.serverId === serverId && server.groupId === groupId && server.r_expireAt > Date.now()) {
// 取连接数最小的服务器
if (minConnections == -1 || server.connections < minConnections) {
// 如果是Slave机,需要判断文件是否已从Master同步
if (!server.isSlave || server.bundleId > bundleId || (server.bundleId == bundleId && server.bundleSize >= (fileStart + fileSize))) {
minConnections = server.connections;
result = server;
}
}
}
}
if (!result) return null;
result.connections += 1;
if (result.connections >= MAX_CONNECTIONS) {
result.connections = 0;
}
return result;
}
getUploadServer() {
let result;
let minConnections = -1;
for (let i = 0; i < this.servers.length; i++) {
const server = this.servers[i];
// Slave机不可上传
if (server.r_expireAt > Date.now() && !server.isSlave) {
// 文件服务器文件超限不可上传
if (server.bundleId < MAX_BUNDLE_ID && (minConnections == -1 || server.connections < minConnections)) {
minConnections = server.connections;
result = server;
}
}
}
if (!result) return null;
result.connections += 1;
if (result.connections >= MAX_CONNECTIONS) {
result.connections = 0;
}
return result;
}
}
module.exports = RegistryServer;