/**
|
* Created by yang on 2015/8/25.
|
*/
|
'use strict';
|
|
var Buffer = require('buffer').Buffer;
|
var Promise = require('bluebird');
|
var logger = require('./logger.js');
|
var protocol = require('./protocol.js');
|
var Storage = require('./storage.js');
|
var helpers = require('./helpers.js');
|
|
module.exports = Tracker;
|
|
function Tracker(config, socket) {
|
this.config = config;
|
this.socket = socket;
|
this._name = config.host + ':' + config.port;
|
}
|
|
// public methods
|
|
/**
|
* query storage server to upload file
|
* 获取指定group的storage实例,如果不指定则tracker server会随机返回1个
|
* @param group
|
*/
|
Tracker.prototype.getStoreStorage = function(group) {
|
var _self = this;
|
// 验证group是否过长
|
if (group && group.length > protocol.FDFS_GROUP_NAME_MAX_LEN) {
|
reject(new Error('group name [' + group + '] is too long'));
|
return;
|
}
|
logger.debug('get a upload storage server from tracker server: [%s]', _self._name);
|
|
var socket = _self.socket;
|
// 获取1个可用storage server 信息
|
// 封装header并发送
|
var command;
|
var bodyLength;
|
if (!group) {
|
command = protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE;
|
bodyLength = 0;
|
|
} else {
|
command = protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE;
|
bodyLength = protocol.FDFS_GROUP_NAME_MAX_LEN;
|
}
|
var header = protocol.packHeader(command, bodyLength, 0);
|
logger.debug('send header to tracker server [%s]', _self._name);
|
socket.write(header);
|
|
// 发送body
|
if (group) {
|
var body = new Buffer(bodyLength);
|
// 默认都填充上0
|
body.fill(0);
|
var groupBL = Buffer.byteLength(group, _self.config.charset);
|
body.write(group, 0, groupBL, _self.config.charset);
|
logger.debug('send body to tracker server [%s]', _self._name);
|
socket.write(body);
|
}
|
|
return new Promise(function(resolve, reject) {
|
protocol.recvPacket(
|
socket,
|
protocol.TRACKER_PROTO_CMD_RESP,
|
protocol.TRACKER_QUERY_STORAGE_STORE_BODY_LEN,
|
function(err, body) {
|
if (err) {
|
reject(err);
|
return;
|
}
|
var storageConfig = _parseStorage(body, _self.config.charset, true);
|
storageConfig.timeout = _self.config.timeout;
|
storageConfig.charset = _self.config.charset;
|
var storage = new Storage(storageConfig);
|
logger.debug('get store storage server info: %j ', storage.config);
|
resolve(storage);
|
}
|
);
|
});
|
};
|
|
/**
|
* query which storage server to download the file
|
* 若只传1个参数则认为是fileId
|
*
|
* * TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE
|
# function: query which storage server to download the file
|
# request body:
|
@ FDFS_GROUP_NAME_MAX_LEN bytes: group name
|
@ filename bytes: filename
|
|
# response body:
|
@ FDFS_GROUP_NAME_MAX_LEN bytes: group name
|
@ FDFS_IPADDR_SIZE - 1 bytes: storage server ip address
|
@ FDFS_PROTO_PKG_LEN_SIZE bytes: storage server port
|
*
|
* @param fileId
|
*/
|
Tracker.prototype.getFetchStorage = function(fileId) {
|
logger.debug('get a fetch storage server from tracker server: [%s]', this._name);
|
return this.getFetchOrUpdateStorage(protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE, fileId);
|
};
|
|
Tracker.prototype.getUpdateStorage = function(fileId) {
|
logger.debug('get a update storage server from tracker server: [%s]', this._name);
|
return this.getFetchOrUpdateStorage(protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE, fileId);
|
};
|
|
/**
|
* # request body:
|
@ FDFS_GROUP_NAME_MAX_LEN bytes: group name
|
@ filename bytes: filename
|
|
# response body:
|
@ FDFS_GROUP_NAME_MAX_LEN bytes: group name
|
@ FDFS_IPADDR_SIZE - 1 bytes: storage server ip address
|
@ FDFS_PROTO_PKG_LEN_SIZE bytes: storage server port
|
* @param command TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE or TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE
|
* @param fileId
|
* @private
|
*/
|
Tracker.prototype.getFetchOrUpdateStorage = function(command, fileId) {
|
var _self = this;
|
var gf = helpers.id2gf(fileId);
|
var group = gf.group;
|
var filename = gf.filename;
|
logger.debug('group: %s, filename: %s', group, filename);
|
|
var socket = _self.socket;
|
var packet = protocol.packFileId(command, group, filename, _self.config.charset);
|
socket.write(packet);
|
|
return new Promise(function(resolve, reject) {
|
protocol.recvPacket(
|
socket,
|
protocol.TRACKER_PROTO_CMD_RESP,
|
protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE,
|
function(err, body) {
|
if (err) {
|
reject(err);
|
return;
|
}
|
var storageConfig = _parseStorage(body, _self.config.charset);
|
storageConfig.timeout = _self.config.timeout;
|
storageConfig.charset = _self.config.charset;
|
var storage = new Storage(storageConfig);
|
logger.info('get storage server info: %j ', storage.config);
|
resolve(storage);
|
}
|
);
|
});
|
};
|
|
// helper methods
|
/**
|
* @ FDFS_GROUP_NAME_MAX_LEN bytes: group name
|
* @ FDFS_IPADDR_SIZE - 1 bytes: storage server ip address
|
* @ FDFS_PROTO_PKG_LEN_SIZE bytes: storage server port
|
* @1 byte: store path index on the storage server {可以没有这个字节}
|
*
|
* @param {Buffer} body
|
* @param {String} charset
|
* @param {Boolean} hasPathIndex
|
* @private {Object}
|
*/
|
function _parseStorage(body, charset, hasPathIndex) {
|
var result = {};
|
|
var group = helpers.trim(body.toString(charset, 0, protocol.FDFS_GROUP_NAME_MAX_LEN));
|
var ip = helpers.trim(body.toString(charset, protocol.FDFS_GROUP_NAME_MAX_LEN, protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1));
|
var port = Number('0x' + body.toString('hex',
|
protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1,
|
protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE));
|
|
result.group = group;
|
result.host = ip;
|
result.port = port;
|
|
if (hasPathIndex &&
|
body.length > protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE) {
|
var storePathIndex = Number('0x' + body.toString('hex',
|
protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE,
|
protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE + 1));
|
|
result.storePathIndex = storePathIndex;
|
}
|
|
return result;
|
}
|
|
Tracker.prototype.listGroups = function() {
|
var _self = this;
|
var socket = _self.socket;
|
|
// 封装header并发送
|
var command = protocol.TRACKER_PROTO_CMD_SERVER_LIST_GROUP;
|
var header = protocol.packHeader(command);
|
logger.debug('send header to tracker server [%s]', _self._name);
|
|
socket.write(header);
|
|
return new Promise(function(resolve, reject) {
|
protocol.recvPacket(
|
socket,
|
protocol.TRACKER_PROTO_CMD_RESP,
|
null,
|
function(err, body) {
|
if (err) {
|
reject(err);
|
return;
|
}
|
if (body.length % 105 != 0) {
|
reject('byte array length:' + body.length + ' is invalid!');
|
return;
|
}
|
var res = {
|
count: body.length / 105,
|
groups: []
|
};
|
var charset = _self.config.charset;
|
var offset = 0;
|
for (var i = 0; i < res.count; i++) {
|
offset = 105*i;
|
res.groups.push({
|
groupName: helpers.trim(body.toString(charset, offset, offset + protocol.FDFS_GROUP_NAME_MAX_LEN)),
|
totalMB: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1),
|
freeMB: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*1),
|
trunkFreeMB: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*2),
|
storageCount: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*3),
|
storagePort: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*4),
|
storageHttpPort: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*5),
|
activeCount: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*6),
|
currentWriteServer: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*7),
|
storePathCount: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*8),
|
subdirCountPerPath: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*9),
|
currentTrunkFileId: helpers.buffer2Number(body, protocol.FDFS_GROUP_NAME_MAX_LEN + 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE*10),
|
});
|
}
|
resolve(res);
|
}
|
);
|
});
|
};
|
|
Tracker.prototype.listStorages = function(group) {
|
var _self = this;
|
var socket = _self.socket;
|
|
// 封装header并发送
|
var command = protocol.TRACKER_PROTO_CMD_SERVER_LIST_STORAGE;
|
var bodyLength = protocol.FDFS_GROUP_NAME_MAX_LEN;
|
var header = protocol.packHeader(command, bodyLength);
|
|
logger.debug('send header to tracker server [%s]', _self._name);
|
socket.write(header);
|
|
var body = new Buffer(bodyLength);
|
// 默认都填充上0
|
body.fill(0);
|
var groupBL = Buffer.byteLength(group, _self.config.charset);
|
body.write(group, 0, groupBL, _self.config.charset);
|
logger.debug('send body to tracker server [%s]', _self._name);
|
socket.write(body);
|
|
return new Promise(function(resolve, reject) {
|
protocol.recvPacket(
|
socket,
|
protocol.TRACKER_PROTO_CMD_RESP,
|
null,
|
function(err, body) {
|
if (err) {
|
reject(err);
|
return;
|
}
|
if (body.length % 600 != 0) {
|
reject('byte array length:' + body.length + ' is invalid!');
|
return;
|
}
|
var res = {
|
count: body.length / 600,
|
storages: []
|
};
|
var charset = _self.config.charset;
|
var offset = 0;
|
for (var i = 0; i < res.count; i++) {
|
offset = 600*i;
|
var s = {};
|
|
s.status = body[offset];
|
offset += 1;
|
s.id = helpers.trim(body.toString(charset, offset, protocol.FDFS_STORAGE_ID_MAX_SIZE));
|
offset += protocol.FDFS_STORAGE_ID_MAX_SIZE;
|
s.ipAddr = helpers.trim(body.toString(charset, offset, offset + protocol.FDFS_IPADDR_SIZE));
|
offset += protocol.FDFS_IPADDR_SIZE;
|
s.domainName = helpers.trim(body.toString(charset, offset, offset + protocol.FDFS_DOMAIN_NAME_MAX_SIZE));
|
offset += protocol.FDFS_DOMAIN_NAME_MAX_SIZE;
|
s.srcIpAddr = helpers.trim(body.toString(charset, offset, offset + protocol.FDFS_IPADDR_SIZE));
|
offset += protocol.FDFS_IPADDR_SIZE;
|
s.version = helpers.trim(body.toString(charset, offset, offset + protocol.FDFS_VERSION_SIZE));
|
offset += protocol.FDFS_VERSION_SIZE;
|
s.joinTime = new Date(helpers.buffer2Number(body, offset)*1000);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.upTime = new Date(helpers.buffer2Number(body, offset)*1000);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalMB = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.freeMB = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.uploadPriority = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.storePathCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.subdirCountPerPath = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.currentWritePath = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.storagePort = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.storageHttpPort = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalUploadCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successUploadCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalAppendCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successAppendCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalModifyCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successModifyCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalTruncateCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successTruncateCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalSetMetaCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successSetMetaCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalDeleteCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successDeleteCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalDownloadCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successDownloadCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalGetMetaCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successGetMetaCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalCreateLinkCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successCreateLinkCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalDeleteLinkCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successDeleteLinkCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalUploadBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successUploadBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalAppendBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successAppendBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalModifyBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successModifyBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalDownloadloadBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successDownloadloadBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalSyncInBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successSyncInBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalSyncOutBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successSyncOutBytes = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalFileOpenCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successFileOpenCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalFileReadCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successFileReadCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.totalFileWriteCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.successFileWriteCount = helpers.buffer2Number(body, offset);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.lastSourceUpdate = new Date(helpers.buffer2Number(body, offset)*1000);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.lastSyncUpdate = new Date(helpers.buffer2Number(body, offset)*1000);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.lastSyncedTimestamp = new Date(helpers.buffer2Number(body, offset)*1000);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.lastHeartBeatTime = new Date(helpers.buffer2Number(body, offset)*1000);
|
offset += protocol.FDFS_PROTO_PKG_LEN_SIZE;
|
s.ifTrunkServer = body[offset] != 0;
|
|
res.storages.push(s);
|
}
|
resolve(res);
|
}
|
);
|
});
|
};
|