Authored by 姜枫

add api memcache clean

import Memcached from 'memcached';
import ws from '../../lib/ws';
import _ from 'lodash';
class ApiCache {
constructor(host) {
this.host = host;
this.memcached = new Memcached(host);
}
delKey(key) {
this._log(`deleting ${key}`)
this.memcached.del(key, (err) => {
if (err) {
this._log(`deleted ${key} fail`)
} else {
this._log(`deleted ${key} success`)
}
});
}
clean(key) {
let begin = new Date();
let key1 = `apiCache:${key}`;
let key2 = `apiCache2:${key}`;
let count = 0;
this.memcached.items( ( err, result ) => {
if( err ) console.error( err );
// for each server...
result.forEach(itemSet => {
var keys = Object.keys( itemSet );
keys.pop(); // we don't need the "server" key, but the other indicate the slab id's
var len = keys.length;
keys.forEach(stats => {
// get a cachedump for each slabid and slab.number
this.memcached.cachedump( itemSet.server, parseInt(stats, 10), itemSet[stats].number, ( err, response ) => {
// dump the shizzle
if (response) {
if (_.isArray(response)) {
_.each(response, keyObj => {
count ++ ;
if (keyObj.key && (keyObj.key.indexOf(key1) >= 0 || keyObj.key.indexOf(key2) >= 0 )) {
this.delKey(keyObj.key);
} else {
this._log(`skip ${keyObj.key}`)
}
});
} else {
count ++;
if (response.key && (response.key.indexOf(key1) >= 0 || response.key.indexOf(key2) >= 0 )) {
this.delKey(response.key);
} else {
this._log(`skip ${response.key}`)
}
}
}
len --;
if (len === 0) {
this.memcached.end();
let end = new Date();
this._log(`clean success in ${end.getTime() - begin.getTime()} , all keys: ${count}`)
}
})
})
})
});
}
_log(message) {
ws.broadcast(`/api_cache/log`, {
host: this.host,
msg: message
});
}
}
export default ApiCache;
... ...
... ... @@ -9,6 +9,7 @@ import UserModel from './user';
import HotfixModel from './hotfix';
import OperationLoggerModel from './operation_logger';
import PageCacheModel from './page_cache';
import MemcachedHostModel from './memcached_host';
shelljs.mkdir('-p', config.dbDir);
... ... @@ -20,6 +21,7 @@ const User = new UserModel();
const Hotfix = new HotfixModel();
const OperationLogger = new OperationLoggerModel();
const PageCache = new PageCacheModel();
const MemcachedHost = new MemcachedHostModel();
User.init();
PageCache.init();
... ... @@ -32,5 +34,6 @@ export {
User,
Hotfix,
OperationLogger,
PageCache
PageCache,
MemcachedHost
};
\ No newline at end of file
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/8/22
*/
'use strict';
import Model from './model';
class MemcachedHost extends Model {
constructor() {
super('memecached_host');
}
}
export default MemcachedHost;
... ...
'use strict';
import Router from 'koa-router';
import _ from 'lodash';
import {
MemcachedHost
} from '../../models';
import ApiCache from '../../ci/api_cache';
let r = new Router();
const api_cache = {
async index(ctx) {
let hosts = await MemcachedHost.findAll();
await ctx.render('action/api_cache', {hosts: hosts});
},
async addHost(ctx) {
let q = ctx.request.body;
await MemcachedHost.insert({host: q.host});
return ctx.body = {
code: 200
};
},
async delHost(ctx) {
let q = ctx.request.body;
await MemcachedHost.removeById(q.id);
return ctx.body = {
code: 200
};
},
async cleanKey(ctx) {
let q = ctx.request.body;
let key = q.key;
let hosts = await MemcachedHost.findAll();
_.each(hosts, (h) => {
(new ApiCache(h.host)).clean(key);
});
return ctx.body = {
code: 200
};
}
};
r.get('/', api_cache.index);
r.post('/host/add', api_cache.addHost);
r.post('/host/del', api_cache.delHost);
r.post('/clean', api_cache.cleanKey);
export default r;
... ...
... ... @@ -10,6 +10,7 @@ import users from './actions/users';
import hotfix from './actions/hotfix';
import operationLog from './actions/operation_log';
import pageCahe from './actions/page_cache';
import apiCache from './actions/api_cache';
const noAuth = new Router();
const base = new Router();
... ... @@ -36,6 +37,7 @@ export default function (app) {
base.use('/hotfix', hotfix.routes(), hotfix.allowedMethods());
base.use('/operation', operationLog.routes(), operationLog.allowedMethods());
base.use('/page_cache', pageCahe.routes(), pageCahe.allowedMethods());
base.use('/api_cache', apiCache.routes(), apiCache.allowedMethods());
base.use('', index.routes(), index.allowedMethods());
... ...
<div class="pageheader">
<div class="media">
<div class="pageicon pull-left">
<i class="fa fa-th-list"></i>
</div>
<div class="media-body">
<ul class="breadcrumb">
<li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
<li>缓存管理</li>
</ul>
<h4>Api缓存清理</h4>
</div>
</div>
<!-- media -->
</div>
<!-- pageheader -->
<div class="contentpanel">
<div class="row">
<div class="col-sm-4 col-md-3">
<h4 class="md-title mb5">memcached列表</h4>
<div class="host-list">
{{#each hosts}}
<p><span>{{host}}</span><span class="fa fa-times pull-right host-remove" style="color: red; cursor: pointer;" data-id="{{_id}}" data-host="{{host}}"></span></p>
{{/each}}
</div>
<div class="mb20"></div>
<button class="btn btn-success" id="add-btn">添加</button>
<br>
</div><!-- col-sm-4 -->
<div class="col-sm-8 col-md-9">
<div class="panel">
<div class="panel-heading">
<div class="pull-right">
<a class="btn btn-success btn-rounded mr20 api-clean-btn">删除缓存</a>
</div>
<div class="panel-title">
缓存key前缀:
<input type="text" style="width: 33%; height: 40px;" id="api-key">
</div>
</div><!-- panel-heading -->
<div class="panel-body yoho-log-dark">
<div class="results-list" id="api-cache-log">
</div><!-- results-list -->
</div><!-- panel-body -->
</div><!-- panel -->
</div><!-- col-sm-8 -->
</div><!-- row -->
</div>
<script>
$(document).on('ready pjax:success', function() {
$('#add-btn').click(function(){
var i = layer.prompt({
title: '请输入host,例如 127.0.0.1:11211'
}, function(host) {
$.post('/api_cache/host/add', {host: host}, function(ret) {
if (ret.code == 200) {
$('.host-list').append('<p>' + host + '</p>');
layer.close(i);
}
});
});
});
$('.api-clean-btn').click(function() {
var key = $('#api-key').val();
if (key) {
$.post('/api_cache/clean', {key: key}, function(ret) {
if (ret.code == 200) {
layer.msg('正在清理中');
}
});
} else {
layer.msg('请输入key');
}
});
$('.host-remove').click(function(){
var id = $(this).data('id');
var host = $(this).data('host');
var parent = $(this).parent();
var i = layer.confirm('确定删除host:<code>' + host + '</code>吗?', {
btn: ['确定', '取消']
}, function() {
$.post('/api_cache/host/del', {id: id}, function(ret) {
if (ret.code === 200) {
$(parent).remove();
layer.close(i);
}
});
});
});
function layoutResize() {
$('.yoho-log-dark').height($('body').height() - 340);
}
$(window).resize(function() {
layoutResize();
});
layoutResize();
var ws = io();
ws.on('connect', function() {
ws.on('/api_cache/log', function(data) {
console.log(data);
$('#api-cache-log').append('<p><span class="host">'+data.host+'</span><span class="message">'+data.msg+'</span></p>')
});
});
ws.on('error', function() {
console.log('connect fail');
});
});
</script>
\ No newline at end of file
... ...
... ... @@ -28,6 +28,7 @@
<li class="parent"><a href=""><i class="fa fa-history"></i> <span>缓存管理</span></a>
<ul class="children">
<li><a href="/page_cache/query">PageCahe清理</a></li>
<li><a href="/api_cache">ApiCahe清理</a></li>
{{!-- <li><a href="/cdn_cache/query">CDN清理</a></li> --}}
</ul>
</li>
... ...
... ... @@ -51,6 +51,7 @@
"lodash": "^4.13.1",
"md5": "^2.1.0",
"md5-file": "^3.1.1",
"memcached": "^2.2.2",
"moment": "^2.13.0",
"nedb": "^1.8.0",
"nedb-promise": "^2.0.0",
... ...