nodejs对memcache进行备份与恢复
memcache是个古老的高速缓存工具,前辈们古老的程序还用着。但是市场上没有完美的备份恢复工具,老板让我开发一个。
这需求让我两眼一抹黑,完全不知所措。先模拟一下思路,可以telenet到端口上,然后命令提取。这里坑很多,首先memcache只能返回2MB的数据,需要修改源码再进行编译,改成分页效果。这个我不在行,与运维同事配合修改了源码并编译成功了(过程还是挺曲折的)。
接着就是代码。nodejs编写,多线程。
#! /usr/bin/env node //需要 npm install memcached -g / npm install memcached 2个命令(涉及环境变量问题,尽量这2个都用一下) var $type='stat'; var $str=""; var $bak_file=""; var $num=""; //预留,暂时不用 var $argv=process.argv.splice(2); //console.log($argv); var $mem_ip=""; var $mem_port=""; // 对Date的扩展,将 Date 转化为指定格式的String // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, // 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) // 例子: // (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 // (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 Date.prototype.Format = function (fmt) { //author: meizz var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小时 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; } //调用: //var time1 = new Date().Format("yyyy-MM-dd"); //var time2 = new Date().Format("yyyy-MM-dd HH:mm:ss"); var time=function() { var result=Date.parse(new Date()); result=Math.ceil(result/1000); return result; } function chr(code) { return String.fromCharCode(code); } function strpos(allstr,findstr) { return allstr.indexOf(findstr); } function readtxt(url) { try{ var fs = require('fs'); var result=fs.readFileSync(url, 'utf8'); }catch(e){ var result=''; } return result; } function savetxt(url,content,back) { if(!back) { var back=function(result){ console.log(result); } } var fs = require('fs'); fs.writeFile(url, content,function(err){ back(err); }); } function appendtxt(url,content) { //console.log(content); var fs = require('fs'); //console.log(__dirname+'/'+url); fs.appendFileSync(__dirname+'/'+url, content); } function str_number(str,num) { if(str.length>=num) { return str; } var result=''; for(let i=1;i<=num-str.length;i++) { result=result+' '; } result=result+str; return result; } function addlog(content) { //console.log('写日志'); var fs = require('fs'); var now_time = new Date().Format("yyyy-MM-dd hh:mm:ss"); var logurl = 'log/'+new Date().Format("yyyy-MM-dd")+".txt"; var content="["+now_time+"] "+content+'\n'; fs.exists(logurl,function(exists){ if(exists){ //console.log("文件存在") appendtxt(logurl,content); } if(!exists){ fs.createWriteStream(logurl) //console.log("文件不存在"); setTimeout(function() { appendtxt(logurl,content); },100); } }) } var go_help=function(){ console.log(`操作方法 [状态] node mem.js 127.0.0.1:11555 [备份] node mem.js 127.0.0.1:11555 dump bak_01.txt [恢复] node mem.js 127.0.0.1:11555 set bak_01.txt `); process.exit(); }; //savetxt("mem_keys.txt",'ssssssssssssss'); try{ //兼容几种参数错位 if(strpos($argv[0],":")>0) { $str=$argv[0]; $type=$argv[1]; $bak_file=$argv[2]; }else if(strpos($argv[1],":")>0){ $str=$argv[1]; $type=$argv[2]; $bak_file=$argv[3]; }else if(strpos($argv[2],":")>0){ $str=$argv[2]; $type=$argv[3]; $bak_file=$argv[4]; } if(!$type) $type='stat'; var $str_arr=$str.split(":"); $mem_ip=$str_arr[0]; $mem_port=$str_arr[1]; //console.log($mem_ip); //console.log($mem_port); if(!$mem_ip || !$mem_port ) { console.error("参数异常"); go_help(); } if($type=='bak' && !$bak_file) { $bak_file=new Date().Format("yyyyMMdd_hhmmss")+"_"+$mem_ip+".bak"; } if(($type=='dump' || $type=='bak' || $type=='set' ) && !$bak_file ) { console.error("参数异常"); go_help(); } }catch(e){ console.error("参数异常"); process.exit(); } //process.exit(); var tmp={ client:null //client 外置 ,memcached:null } var cache={ tag:'' //标记当前在做那件事情 get_keys_obj , get_keys , ,send:'' //当前send消息缓存的值 ,send_now:0 //开始send的标记, 循环开始时把数据写进这个值 ,send_end:0 //结束send的标记, 循环开始时把数据写进这个值 ,keys_obj:{} ,keys_obj_list:[]//keys_obj 的list形式,有了排序概念 ,keys:[] ,active_time:time() ,data:'' //存收到的包的数据 ,output:[] //,stats:[] ,stats_slabs_data:'' //对应send的内容 ,stats_items_data:'' ,key_exptime:{} //key的过期时间 ,keys_setvalue_list:[] // ,keys_setvalue_num:{} //set 指令尝试次数,计次 ,keys_setvalue_maxnum:3 //set 的最大次数 ,key_num:{} //得到每个key 列的条数 ,key_back_num:{} //得到每个key 列的条数 (返回计次) }; if($type=='set') { const Memcached = require('memcached'); tmp.memcached = new Memcached($mem_ip+':'+$mem_port); } function go_nodememcache(obj,callback,num) { if(!num) { var num=5; } num=num-1; tmp.memcached.set(obj.key, obj.value, obj.time, function (err) { if(!err && err!==false) { callback(true,obj); }else{ if(num>0) { go_nodememcache(obj,callback,num); }else{ callback(false,obj); } } }); } //百分比处理数据 var tag_dump_pers=function(arr) { if(arr.length<20) { var this_max_num=arr.length; }else{ var this_max_num=20; } let per_number=Math.ceil(arr.length/this_max_num); var per_obj={}; for(let i=0;i<this_max_num;i++ ) { let this_number=i*per_number+1; let this_per=Math.ceil((i*100*100)/this_max_num)/100; per_obj[this_number]=this_per+'%'; } cache.per_obj=per_obj; //console.log(per_obj); } var goerr=function(msg,console_cache){ if(!console_cache) { let console_cache=false; } if(!msg) { let msg='程序执行异常,即将退出'; } console.error(msg); if(console_cache==true) { console.log(cache); } tmp.client.end(); setTimeout(function(){ process.exit(); },500); } var action={ get_stats_send:function() { //console.log('get_stats_send'); cache.tag='get_stats'; cache.send='stats items\n' cache.data=''; tmp.client.write(cache.send); } // STAT items:4:number 7623 ,get_stats_back:function(data) { //console.log('get_stats_back'); //console.log(data); let data_line=data.split("\n"); let end_str=data_line[data_line.length-1].trim(); //console.log(end_str); if(end_str=='END') { // cache.stats_items_data=data; this.get_stats_slabs_send(); }else if(end_str=='ERROR'){ //连接繁忙式异常 重新连接 this.get_stats_send(); }else{ console.error(cache.tag+' 未知异常'); } } ,get_stats_slabs_send:function() { //stats_items _data stats_slabs _data cache.tag='get_stats_slabs'; cache.send='stats slabs\n' cache.data=''; tmp.client.write(cache.send); } ,get_stats_slabs_back:function(data) { //console.log('get_stats_slabs_back'); //console.log(data); /* # Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM 1 80B 280025s 3 38747 no 0 0 0 2 104B 268966s 5 40731 no 0 0 0 3 136B 269010s 5 36764 no 0 0 0 4 176B 18793s 5 27137 no 0 0 0 5 224B 280034s 9 40429 no 0 0 0 6 280B 280037s 1 563 no 0 0 0 */ let data_line=data.split("\n"); let end_str=data_line[data_line.length-1].trim(); //console.log(end_str); if(end_str=='END') { // cache.stats_slabs_data=data; this.get_stats_output(); //返回数据 }else if(end_str=='ERROR'){ //连接繁忙式异常 重新连接 this.get_stats_slabs_send(); }else{ console.error(cache.tag+' 未知异常'); } } ,get_stats_output:function() { //输出状态的结果 let stats_items_data=cache.stats_items_data; let stats_slabs_data=cache.stats_slabs_data; stats_items_data=stats_items_data.replace(/items:/g,''); let all_stat_data=stats_items_data.trim()+'\n'+stats_slabs_data.trim(); let list=all_stat_data.split("\n"); var key_arr={}; var stat_obj={}; for(let i in list) { let this_str=list[i].trim(); //var this_reg = new RegExp("^STAT items:(\d*):number (\d*)"); let this_arr=this_str.match(/STAT ([0-9]{1,5}):([a-zA-Z0-9_]{1,15}) ([0-9]{1,15})/); //console.log(this_str); //console.log(this_arr); try{ if(this_arr.length>3) { let this_num=this_arr[1]; let this_key=this_arr[2]; let this_value=this_arr[3]; if(this_key=='free_chunks_end') { if(this_value==0) { this_value='no'; }else{ this_value='yes'; } } if(this_key=='free_chunks_end') { if(this_value==0) { this_value='yes'; }else{ this_value='no'; } } try{ if(!key_arr[this_num]) { var this_need=1; }else{ var this_need=0; } }catch(e){ var this_need=1; } if(this_need==1) { key_arr[this_num]=1; stat_obj[this_num]={}; } stat_obj[this_num][this_key]=this_value; } }catch(e){} } //console.log(stat_obj); //Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM /* number: '563', age: '333936', evicted: '0', evicted_nonzero: '0', evicted_time: '0', outofmemory: '0', tailrepairs: '0', reclaimed: '0', chunk_size: '280', chunks_per_page: '3744', total_pages: '1', total_chunks: '3744', used_chunks: '563', free_chunks: '0', free_chunks_end: '3181', mem_requested: '127073', get_hits: '563', cmd_set: '563', delete_hits: '0', incr_hits: '0', decr_hits: '0', cas_hits: '0', */ var display_obj={ chunk_size:'Item_Size' ,age:'Max_age' ,total_pages:'Pages' ,number:'Count' ,free_chunks_end:'Full' ,evicted:'Evicted' ,evicted_time:'Evict_Time' ,outofmemory:'OOM' }; var this_out_put=str_number('#',5); for(let i in display_obj) { this_out_put=this_out_put+''+str_number(display_obj[i],15); } for(let i in stat_obj) { let this_one_arr=[]; for(let j in display_obj) { try{ var this_tmp_html=stat_obj[i][j]; }catch(e){ var this_tmp_html=''; } this_one_arr.push(str_number(this_tmp_html,15)); } this_out_put=this_out_put+'\n'+str_number(i,5)+''+this_one_arr.join(''); } //正常输出 console.log(this_out_put); savetxt('stat.txt',this_out_put,function(){ goerr(' 调试end'); }); //goerr(' 调试end'); } //001 ,get_keys_obj_send:function() { console.log("即将遍历所有key"); cache.tag='get_keys_obj'; cache.send='stats items\n' cache.data=''; tmp.client.write(cache.send); } //第一步取到数据(4 做取keys准备) STAT items:4:number 7623 ,get_keys_obj_back:function(data) { //console.log('get_keys_obj_back'); //console.log(data); let data_line=data.split("\n"); let end_str=data_line[data_line.length-1].trim(); //console.log(end_str); if(end_str=='END') { cache.keys_obj={}; for(let i in data_line) { this_line=data_line[i].trim(); if(this_line) { let this_line_arr=this_line.split(':'); let this_line_arr2=this_line.split(' '); if(this_line_arr.length>=3) { cache.keys_obj[this_line_arr[1]]=1; //STAT items:2:number 250000 if(this_line.indexOf("number")>0) { //得到每个key 列的条数 cache.key_num[this_line_arr[1]]=Math.ceil(this_line_arr2[this_line_arr2.length-1]); //得到每个key 列的条数 对应的返回调试为0 cache.key_back_num[this_line_arr[1]]=0; } } } } //已经取出keys 存在 cache.keys_obj, cache.keys_obj_list=[];//存储为list for(let keys_num in cache.keys_obj) { cache.keys_obj_list.push(keys_num); } // if(cache.keys_obj_list.length==0) { goerr('无数据,操作失败'); } //下一步发送数据的准备工作 cache.send_now=0; cache.send_end=cache.keys_obj_list.length-1; this.get_keys_send(); }else if(end_str=='ERROR'){ //连接繁忙式异常 重新连接 this.get_keys_obj_send(); }else{ console.error(cache.tag+' 未知异常'); } } //002 ,get_keys_send:function() { cache.tag='get_keys'; cache.data=''; //console.log('get_keys_send'); let keys_num=cache.keys_obj_list[cache.send_now]; console.log('获取 #'+keys_num+' 所有key中'); //发消息 let statrt_num=cache.key_back_num[keys_num]; let this_send=`stats cachedump ${keys_num} ${statrt_num} 0\r\n`; console.log(this_send); //console.log(cache); // ITEM views.decorators.cache.cache_header..cc7d9 [6 b; 1256056128 s] END //console.log(this_send); //console.log(cache); cache.send=this_send; tmp.client.write(this_send); } ,get_keys_back:function(data) { //console.log('get_keys_back'); ITEM 867458042679102_LockState [1 b; 1610530568 s] //console.log(data); console.log('...校验获取到的key'); addlog(data); //addlog(data); var data_line=data.split("\n"); let end_str=data_line[data_line.length-1].trim(); //console.log(end_str); if(end_str=='END') { //cache.keys_obj={}; //cache.keys=[]; var get_keys_back_num=0; for(let i in data_line) { this_line=data_line[i].trim(); if(this_line) { let this_line_arr=this_line.split(' '); if(this_line_arr.length>=3) { get_keys_back_num=get_keys_back_num+1; let this_key=this_line_arr[1].trim(); //console.log(this_key); cache.keys.push(this_key); //处理key的到期时间 ITEM 867458042679102_LockState [1 b; 1610530568 s] try{ var this_key_exptime=this_line_arr[this_line_arr.length-2]; if(!this_key_exptime) { var this_key_exptime=0; } }catch(e){ var this_key_exptime=0; } var reg_number=/[0-9]{1,20}/; if(!reg_number.test(this_key_exptime)) { var this_key_exptime=0; } this_key_exptime=Math.ceil(this_key_exptime); if(this_key_exptime<time()) { //小于备份的时间,为永久设置为0 this_key_exptime=0; } cache.key_exptime[this_key]=this_key_exptime; //addlog(this_key); } } } //下一步发送数据的准备工作 //常规 //分页处理 // cache.key_back_num[cache.keys_obj_list[cache.send_now]]=cache.key_back_num[cache.keys_obj_list[cache.send_now]]+get_keys_back_num; console.log(cache.key_back_num[cache.keys_obj_list[cache.send_now]]); //取到的数据不足10行认为是 当前key列表结束, 结束后取下个key列表 if(data_line.length<10) { //console.log(cache.send_now); if(cache.send_now!=cache.send_end) { cache.send_now=cache.send_now+1; //console.log(cache.send_now); this.get_keys_send(); }else{ //最后一个 //下一步发送数据的准备工作 cache.send_now=0; cache.send_end=cache.keys.length-1; tag_dump_pers(cache.keys); this.get_keys_value_send(); } }else{ this.get_keys_send(); } }else if(end_str=='ERROR'){ //连接繁忙式异常 重新连接 this.get_keys_obj_send(); }else{ goerr('未知异常'); } } ,get_keys_value_send:function() { //goerr('调试终止'); /*let mem_save_txt=cache.keys.join("\r\n"); //console.log(mem_save_txt); savetxt("mem_keys.txt",mem_save_txt,function(){ goerr('调试终止'); }); console.log('get_keys_value_send');*/ cache.tag='get_keys_value'; cache.data=''; //console.log('get_keys_value_send'); let keys_num=cache.keys[cache.send_now]; //处理百分比 try{ if(cache.per_obj[cache.send_now]) { console.log('已完成 '+cache.per_obj[cache.send_now]); } }catch(e){} //console.log(cache.keys); //console.log(cache.send_now); //console.log(keys_num); //发消息 let this_send=`get ${keys_num}\r\n`; //console.log(this_send); //console.log(cache); //addlog(this_send); cache.send=this_send; tmp.client.write(this_send); } ,get_keys_value_back:function(data) { //console.log('get_keys_value_back'); //console.log(data); //VALUE 356802030658685_gpsstatus 0 99 //VALUE key flags len //add $key $flags $keyexp{$key} $len\r\n$val $keyexp{$key}到期时间 倒数第2个字段 1610530568-》 ITEM 862868040756048_gsmstatus [23 b; 1610530568 s] /* add 123456_allstatus 0 1470385088 342 1,2,3,4,5 */ let data_line=data.split("\n"); let end_str=data_line[data_line.length-1].trim(); let this_key=cache.keys[cache.send_now]; //addlog(this_key+'\n->'+data); //console.log(end_str); if(end_str=='END') { /*cache.keys_obj={}; cache.keys=[]; for(let i in data_line) { this_line=data_line[i].trim(); if(this_line) { let this_line_arr=this_line.split(' '); if(this_line_arr.length>=3) { let this_key=this_line_arr[1].trim(); //console.log(this_key); cache.keys.push(this_key); } } }*/ //3行数据才是正常的 var this_valueback_check_err=1; if(data_line.length==3) { /* VALUE 862032047380167_LockState 0 1 1 END get 863753041517053_today VALUE 863753041517053_today 0 58 1610804920,8699,54254.058594,85.610001,8.496506,1610804915 END */ try{ var this_key_exptime=cache.key_exptime[this_key]; if(!this_key_exptime) { var this_key_exptime=0; } }catch(e){ var this_key_exptime=0; } var this_value=data_line[1].trim(); try{ this_value=this_value.replace(/[\x00-\x1f]+/g, ''); var this_value_end=this_value.substr(this_value.length-1,1); //console.log(this_value_end); if(chr('255')==this_value_end) { //console.log(this_value); var this_value=this_value.substr(0,this_value.length-1); } }catch(e){} var this_value_arr=data_line[0].split(' '); try{ if(!this_value_arr[2]) { var this_flag=0; }else{ var this_flag=this_value_arr[2]; } }catch(e){ var this_flag=0; } let this_back_str=`[****] ${this_key} <----> ${this_value} <----> ${this_key_exptime} <----> ${this_flag}`; var this_valueback_check_err=0; cache.output.push(this_back_str); //addlog(this_back_str); } if(this_valueback_check_err==1) { addlog(this_key+'\n'+data); } //下一步发送数据的准备工作 //常规 if(cache.send_now!=cache.send_end) { cache.send_now=cache.send_now+1; this.get_keys_value_send(); }else{ //最后一个 //下一步发送数据的准备工作 this.save_output(); } }else if(end_str=='ERROR'){ //连接繁忙式异常 重新连接 this.get_keys_obj_send(); }else{ goerr('未知异常'); } } ,save_output:function(){ //console.log("save_output"); let mem_save_txt=cache.output.join("\n"); //console.log(mem_save_txt); savetxt($bak_file,mem_save_txt,function(){ console.log("\n"); console.log(new Date().Format("yyyy-MM-dd hh:mm:ss")); let this_log_num=cache.send_end+1; console.log("总计备份: "+this_log_num); goerr('备份成功'); }); } ,set_keys:function() { //console.log('get_stats_send'); cache.tag='set_keys'; //从备份文件读出数据,进行set var bak_str=readtxt($bak_file); var bak_str_arr=bak_str.split('[****]'); var keys_setvalue_list=[]; for(var i in bak_str_arr) { var this_str_one=bak_str_arr[i].trim(); var this_str_one_arr=this_str_one.split("<---->"); if(this_str_one_arr.length>2) { //console.log(this_str_one_arr); var this_key=this_str_one_arr[0].trim(); var this_value=this_str_one_arr[1].trim()+''; try{ var this_exptime=this_str_one_arr[2].trim(); if(!this_exptime) { var this_exptime=0; } }catch(e){ var this_exptime=0; } try{ var this_flag=this_str_one_arr[3].trim(); if(!this_flag) { var this_flag=0; } }catch(e){ var this_flag=0; } if(this_exptime==0) { var this_time=0; }else{ var this_time=this_exptime-time(); } if(this_time<0) { this_time=0; } var this_length=this_value.length; var this_set_html=`set ${this_key} ${this_flag} ${this_time} ${this_length}\r\n${this_value}\r\n`; var this_set_obj={ key:this_key ,send:this_set_html }; keys_setvalue_list.push(this_set_obj); cache.keys_setvalue_num[this_key]=0;//发送set的次数 } } cache.keys_setvalue_list=keys_setvalue_list; if(keys_setvalue_list.length==0) { goerr('备份文件无合适数据set'); } tag_dump_pers(cache.keys_setvalue_list); //console.log(keys_setvalue_list); //下一步发送数据的准备工作 cache.send_now=0; cache.send_end=cache.keys_setvalue_list.length-1; this.set_keys_value_send() } ,set_keys_value_send:function() { //console.log('set_keys_value_send'); cache.tag='set_keys_value'; cache.data=''; // let this_key=cache.keys_setvalue_list[cache.send_now].key; let this_send=cache.keys_setvalue_list[cache.send_now].send; //console.log(cache.send_now); //处理百分比 try{ if(cache.per_obj[cache.send_now]) { console.log('已完成 '+cache.per_obj[cache.send_now]); } }catch(e){} cache.keys_setvalue_num[this_key]=cache.keys_setvalue_num[this_key]+1; //console.log(this_send); //console.log(cache); //addlog(this_send); cache.send=this_send; tmp.client.write(this_send); } ,set_keys_value_back:function(data) { let data_line=data.split("\n"); let end_str=data_line[data_line.length-1].trim(); let this_key=cache.keys_setvalue_list[cache.send_now].key; //addlog(this_key+'\n->'+data); //console.log(end_str); if(end_str=='STORED') { //console.log(this_key); //下一步发送数据的准备工作 //常规 if(cache.send_now!=cache.send_end && cache.keys_setvalue_num[this_key]<cache.keys_setvalue_maxnum) { cache.send_now=cache.send_now+1; this.set_keys_value_send(); }else{ //最后一个 //下一步发送数据的准备工作 this.set_output(); } }else if(end_str=='ERROR'){ //连接繁忙式异常 重新连接 this.get_keys_obj_send(); }else{ goerr('未知异常'); } } ,set_output:function(){ //console.log("set_output"); console.log("\n"); console.log(new Date().Format("yyyy-MM-dd hh:mm:ss")); let this_log_num=cache.send_end+1; console.log("总计set: "+this_log_num); goerr('SET成功'); } ,set_keys_nodememcache:function() { console.log('set_keys_value_nodememcache'); cache.tag='set_keys_value_nodememcache'; //从备份文件读出数据,进行set var bak_str=readtxt($bak_file); var bak_str_arr=bak_str.split('[****]'); var keys_setvalue_list=[]; for(var i in bak_str_arr) { var this_str_one=bak_str_arr[i].trim(); var this_str_one_arr=this_str_one.split("<---->"); if(this_str_one_arr.length>2) { //console.log(this_str_one_arr); var this_key=this_str_one_arr[0].trim(); var this_value=this_str_one_arr[1].trim()+''; try{ var this_exptime=this_str_one_arr[2].trim(); if(!this_exptime) { var this_exptime=0; } }catch(e){ var this_exptime=0; } try{ var this_flag=this_str_one_arr[3].trim(); if(!this_flag) { var this_flag=0; } }catch(e){ var this_flag=0; } if(this_exptime==0) { var this_time=0; }else{ var this_time=this_exptime-time(); } if(this_time<0) { this_time=0; } var this_length=this_value.length; var this_set_html=`set ${this_key} ${this_flag} ${this_time} ${this_length}\r\n${this_value}\r\n`; var this_set_obj={ key:this_key ,send:this_set_html ,time:this_time ,value:this_value ,flag:this_flag }; keys_setvalue_list.push(this_set_obj); cache.keys_setvalue_num[this_key]=0;//发送set的次数 } } cache.keys_setvalue_list=keys_setvalue_list; if(keys_setvalue_list.length==0) { goerr('备份文件无合适数据set'); } tag_dump_pers(cache.keys_setvalue_list); //console.log(keys_setvalue_list); //下一步发送数据的准备工作 cache.send_now=0; cache.send_end=cache.keys_setvalue_list.length-1; this.set_keys_value_nodememcache(); } ,set_keys_value_nodememcache:function() { //console.log('set_keys_value_nodememcache'); cache.tag='set_keys_value_nodememcache'; cache.data=''; // let this_key=cache.keys_setvalue_list[cache.send_now].key; let this_value=cache.keys_setvalue_list[cache.send_now].value; let this_time=cache.keys_setvalue_list[cache.send_now].time; //console.log(cache.send_now); //处理百分比 try{ if(cache.per_obj[cache.send_now]) { console.log('已完成 '+cache.per_obj[cache.send_now]); } }catch(e){} cache.keys_setvalue_num[this_key]=cache.keys_setvalue_num[this_key]+1; //console.log(this_send); //console.log(cache); //addlog(this_send); go_nodememcache(cache.keys_setvalue_list[cache.send_now],function(result,obj){ //console.log(obj.key); //console.log(result); //下一步发送数据的准备工作 //常规 if(cache.send_now!=cache.send_end && cache.keys_setvalue_num[this_key]<cache.keys_setvalue_maxnum) { cache.send_now=cache.send_now+1; action.set_keys_value_nodememcache(); }else{ //最后一个 //下一步发送数据的准备工作 action.set_output(); } }); } }; console.log(new Date().Format("yyyy-MM-dd hh:mm:ss")); var net = require('net'); var socket=net.Socket(); socket.setEncoding('ascii'); socket.setNoDelay(false); var client = socket.connect({host:$mem_ip,port: $mem_port}, function() { tmp.client=client; console.log('\n'); console.log('连接到MEMCACHE服务器!'); if($type=="bak" || $type=="dump") { //备份 action.get_keys_obj_send(); //client.write('stats items\n'); }else if($type=='set') { action.set_keys_nodememcache(); }else if($type=='stat') { action.get_stats_send(); }else { go_help(); } }); client.on('data', function(data) { cache.active_time=time();//更新收到数据时间 //console.log(data); var end_str=data.substr(data.length-1,1); end_str=end_str.charCodeAt(); //console.log(end_str); var end_str2=data.substr(data.length-2,1); end_str2=end_str2.charCodeAt(); //console.log(end_str2); var data_bak=data.trim(); let check_end=data_bak.substr(data_bak.length-3,3); let check_error=data_bak.substr(data_bak.length-5,5); let check_stored=data_bak.substr(data_bak.length-6,6); //console.log(check_end); //console.log(check_error); cache.data=cache.data+data; if( (check_end=='END' || check_error=='ERROR' || check_stored=='STORED') && end_str==10 && end_str2==13) { //console.log('ok'); var cache_data=cache.data.trim(); action[cache.tag+'_back'](cache_data); try{ }catch(e){} } //client.end(); }); client.on('end', function() { console.log('\n'); console.log('结束与MEMCACHE服务器的连接'); }); client.on('error', function() { console.log('\n'); console.error('断开与MEMCACHE服务器的连接'); });
操作方法 [状态] node mem.js 127.0.0.1:11555 [备份] node mem.js 127.0.0.1:11555 dump bak_01.txt [恢复] node mem.js 127.0.0.1:11555 set bak_01.txt
需要修改memcache源码,支持下面命令多了个 statrt_num,分页的开始值
stats cachedump ${keys_num} ${statrt_num} 0