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/yonghan/blog/475601

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值