最近用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;};//重置bufferReplyParser.prototype._reset3 = function(){ this._buffer = this._buffer.slice(this._offset); this._offset = 0;};//只查找\r,忽略所后面的\nReplyParser.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;};