最近用nodejs做项目,大规模的使用了redis,在windows下开发老是遇到各种问题,因为redis的协议解析器有问题,本来redis模块有两种协议解析的方式,一种是使用js实现的,另一种则是使用redis官方提供的hiredis的模块,当使用hiredis模块的时候当然一切正常,但是如果你是windows上的开发者,那么你没得选择,只能使用js版解析器,因为hiredis的模块在windows下安装不了,可是使用js版的解析器的时候就会出现各种问题,曾经改过几次这个js文件,但是还是没能把BUG完全修复完,于是一股脑直接重写了这个解析器,这次改动首先是修复各种协议上的问题,其中最大的区别是在效率的很大程度的提升,为什么这么说?因为原始的解析器方式是这样,如果一条完整的数据包有10K,此时因为网络原因只传输了8K,那么他必须先尝试解析,一旦发现包长度不够,就会等下次2K的数据来了之后再重新解析,意思就是说,之前解析的那8K的时间浪费了。而我改动的则是可以续解析,上次解析到哪里,下次收到数据包后,再接着上次解析的地方继续往下执行,节约了很多时间。简单介绍之后贴上代码吧,希望能对大家有帮助。
var events = require("events"),
util = require('../util');
exports.name = "fast javascript parser";
exports.debug_mode = false;
function Multi(n)
{
this.total = n;
this.offset = 0;
this.data = [];
}
Multi.prototype.push = function(d)
{
this.offset++;
this.data.push(d);
};
Multi.prototype.isFull = function()
{
return (this.total == this.offset);
};
Multi.prototype.incr = function()
{
this.offset++;
};
function ReplyParser(options)
{
this.name = exports.name;
this.options = options || { };
this._buffer = null;
this._start = -1;
this._offset = 0;
this._encoding = "utf-8";
this._debugMode = options.debug_mode;
this._multi = false;//多条数据?
this._multiData = null;//多条数据成员
this._multiCursor = null;//游标
this._data = null;//单条数据内容
this._dataType = 0;//单条数据类型
this._dataTotal = 0;//单条数据总大小
this._dataOffset = 0;//单条已接收的数据大小
this._parserFunc = {
'+' : this._parseStatus,
'-' : this._parseError,
':' : this._parseInteger,
'$' : this._parseBulk,
'*' : this._parseMulti
};
}
util.inherits(ReplyParser, events.EventEmitter);
exports.Parser = ReplyParser;
ReplyParser.prototype.execute = function(buf)
{
this.append(buf);
while(this._offset < this._buffer.length)
{
if(!this._dataType){
if(this._buffer[this._offset] == 0x0d ||
this._buffer[this._offset] == 0x0a)
{//上次可能预留下来的结束标记
this._offset++;
continue;
}
this._dataType = this._buffer[this._offset];
this._offset++;
}
var type = String.fromCharCode(this._dataType);
if(this._parserFunc[type]){
var result = this._parserFunc[type].apply(this);
if(result){
if(this._multi){//多行数据
if(type == '*'){//当前处理的是头信息
if(this._multiCursor.total == -1){//多行数据异常
this._multiCursor.offset = -1;
this._multiCursor.data = null;
}
}else{//多行数据又处理一条
this._multiCursor.push(this._data);
}
//是否需要移动游标?
if(this._multiCursor.isFull() &&
(this._multiCursor != this._multiData))
{
this._multiData.push(this._multiCursor.data);
this._multiCursor = this._multiData;
}
//完整的多行数据处理完毕
if(this._multiData.isFull()){
this._sendReply(this._multiData.data);
this._reset2();
this._reset3();
}
}else if(type == '-'){//错误
this._sendError(this._data);
this._reset3();
}else{//单条数据
this._sendReply(this._data);
this._reset3();
}
//重置数据
this._reset();
}
}else{//非法的定义
break;
}
}
};
ReplyParser.prototype.append = function(buf)
{
//empty
if(!buf){
return ;
}
//first
if(this._buffer === null){
this._buffer = buf;
return ;
}
//append
this._buffer = Buffer.concat([this._buffer.slice(0), buf]);
};
//解析出错
ReplyParser.prototype._parserError = function (message)
{
this.emit("error", message);
};
//发送错误
ReplyParser.prototype._sendError = function (reply)
{
this.emit("reply error", reply);
};
//发送数据
ReplyParser.prototype._sendReply = function (reply)
{
this.emit("reply", reply);
};
//重置数据
ReplyParser.prototype._reset = function()
{
this._start = -1;
this._data = null;
this._dataTotal = 0;
this._dataOffset = 0;
this._dataType = 0;
};
//重置multi数据
ReplyParser.prototype._reset2 = function()
{
this._multi = false;
this._multiData = null;
this._multiCusror = null;
};
//重置buffer
ReplyParser.prototype._reset3 = function()
{
this._buffer = this._buffer.slice(this._offset);
this._offset = 0;
};
//只查找\r,忽略所后面的\n
ReplyParser.prototype._findEnd = function()
{
var offset = this._offset;
while(offset < this._buffer.length)
{
if(this._buffer[offset] == 0x0d){
return offset;
}
offset++;
}
return false;
};
//解析状态
ReplyParser.prototype._parseStatus = function()
{
if(this._start === -1){
this._start = this._offset;
}
//查找结束标记
var end = this._findEnd();
if(end === false){
this._offset = this._buffer.length;
return false;
}
this._offset = end + 1;
this._data = this._buffer.toString(this._encoding, this._start, end);
return true;
};
//解析错误
ReplyParser.prototype._parseError = function()
{
if(this._start === -1){
this._start = this._offset;
}
var end = this._findEnd();
if(end === false){
this._offset = this._buffer.length;
return false;
}
this._offset = end + 1;
this._data = this._buffer.toString(this._encoding, this._start, end);
return true;
};
//解析数字
ReplyParser.prototype._parseInteger = function()
{
if(this._start === -1){
this._start = this._offset;
}
var end = this._findEnd();
if(end === false){
this._offset = this._buffer.length;
return false;
}
this._offset = end + 1;
this._data = +(this._buffer.toString(this._encoding, this._start, end));
return true;
};
//解析单条数据
ReplyParser.prototype._parseBulk = function()
{
//读头信息
if(!this._dataTotal)
{
if(this._start === -1){
this._start = this._offset;
}
var end = this._findEnd();
if(end === false){
this._offset = this._buffer.length;
return false;
}
this._offset = end + 1;
this._dataOffset = 0;
this._dataTotal = +(this._buffer.toString(this._encoding, this._start, end));
//空数据
if(this._dataTotal == -1){
return true;
}
}
//缓冲区已空
if(this._offset >= this._buffer.length){
return false;
}
//准备开始读数据,\n要去掉
if((this._dataOffset == 0) && (this._buffer[this._offset] == 0x0a)){
this._offset++;
}
//分别计算出buffer中剩余的数据和本次需要的数据长度
var need = this._dataTotal - this._dataOffset;
var remain = this._buffer.length - this._offset;
//buffer中有足够的数据
if(remain >= need){
this._offset+= need;
this._data = this._buffer.toString(this._encoding, this._offset - this._dataTotal, this._offset);
return true;
}
//buffer中数据不足
this._offset = this._buffer.length;
this._dataOffset+= remain;
return false;
};
//解析多条数据
ReplyParser.prototype._parseMulti = function()
{
this._multi = true;
if(this._start === -1){
this._start = this._offset;
}
var end = this._findEnd();
if(end === false){
this._offset = this._buffer.length;
return false;
}
var n = +(this._buffer.toString(this._encoding, this._start, end));
this._offset = end + 1;
if(this._multiData === null){
this._multiData = new Multi(n);
this._multiCursor = this._multiData;
}else{
var m = new Multi(n);
this._multiCursor = m;
}
return true;
};
转载于:https://my.oschina.net/yonghan/blog/475601