nodejs的redis協議解析器 原

      最近用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;
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章