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