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;
};

转载于:https://my.oschina.net/scgywx/blog/158009

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值