Authored by 姜枫

add api memcache clean

  1 +
  2 +import Memcached from 'memcached';
  3 +import ws from '../../lib/ws';
  4 +import _ from 'lodash';
  5 +
  6 +class ApiCache {
  7 +
  8 + constructor(host) {
  9 + this.host = host;
  10 + this.memcached = new Memcached(host);
  11 + }
  12 +
  13 + delKey(key) {
  14 + this._log(`deleting ${key}`)
  15 +
  16 + this.memcached.del(key, (err) => {
  17 + if (err) {
  18 + this._log(`deleted ${key} fail`)
  19 + } else {
  20 + this._log(`deleted ${key} success`)
  21 + }
  22 + });
  23 + }
  24 +
  25 + clean(key) {
  26 + let begin = new Date();
  27 +
  28 + let key1 = `apiCache:${key}`;
  29 + let key2 = `apiCache2:${key}`;
  30 +
  31 + let count = 0;
  32 +
  33 + this.memcached.items( ( err, result ) => {
  34 + if( err ) console.error( err );
  35 + // for each server...
  36 + result.forEach(itemSet => {
  37 +
  38 + var keys = Object.keys( itemSet );
  39 + keys.pop(); // we don't need the "server" key, but the other indicate the slab id's
  40 +
  41 + var len = keys.length;
  42 +
  43 + keys.forEach(stats => {
  44 +
  45 + // get a cachedump for each slabid and slab.number
  46 + this.memcached.cachedump( itemSet.server, parseInt(stats, 10), itemSet[stats].number, ( err, response ) => {
  47 + // dump the shizzle
  48 +
  49 + if (response) {
  50 + if (_.isArray(response)) {
  51 + _.each(response, keyObj => {
  52 + count ++ ;
  53 + if (keyObj.key && (keyObj.key.indexOf(key1) >= 0 || keyObj.key.indexOf(key2) >= 0 )) {
  54 + this.delKey(keyObj.key);
  55 + } else {
  56 + this._log(`skip ${keyObj.key}`)
  57 + }
  58 + });
  59 + } else {
  60 + count ++;
  61 + if (response.key && (response.key.indexOf(key1) >= 0 || response.key.indexOf(key2) >= 0 )) {
  62 + this.delKey(response.key);
  63 + } else {
  64 + this._log(`skip ${response.key}`)
  65 + }
  66 + }
  67 + }
  68 +
  69 + len --;
  70 +
  71 + if (len === 0) {
  72 + this.memcached.end();
  73 + let end = new Date();
  74 + this._log(`clean success in ${end.getTime() - begin.getTime()} , all keys: ${count}`)
  75 + }
  76 + })
  77 + })
  78 + })
  79 + });
  80 + }
  81 +
  82 + _log(message) {
  83 + ws.broadcast(`/api_cache/log`, {
  84 + host: this.host,
  85 + msg: message
  86 + });
  87 + }
  88 +}
  89 +
  90 +export default ApiCache;
  91 +
@@ -9,6 +9,7 @@ import UserModel from './user'; @@ -9,6 +9,7 @@ import UserModel from './user';
9 import HotfixModel from './hotfix'; 9 import HotfixModel from './hotfix';
10 import OperationLoggerModel from './operation_logger'; 10 import OperationLoggerModel from './operation_logger';
11 import PageCacheModel from './page_cache'; 11 import PageCacheModel from './page_cache';
  12 +import MemcachedHostModel from './memcached_host';
12 13
13 shelljs.mkdir('-p', config.dbDir); 14 shelljs.mkdir('-p', config.dbDir);
14 15
@@ -20,6 +21,7 @@ const User = new UserModel(); @@ -20,6 +21,7 @@ const User = new UserModel();
20 const Hotfix = new HotfixModel(); 21 const Hotfix = new HotfixModel();
21 const OperationLogger = new OperationLoggerModel(); 22 const OperationLogger = new OperationLoggerModel();
22 const PageCache = new PageCacheModel(); 23 const PageCache = new PageCacheModel();
  24 +const MemcachedHost = new MemcachedHostModel();
23 25
24 User.init(); 26 User.init();
25 PageCache.init(); 27 PageCache.init();
@@ -32,5 +34,6 @@ export { @@ -32,5 +34,6 @@ export {
32 User, 34 User,
33 Hotfix, 35 Hotfix,
34 OperationLogger, 36 OperationLogger,
35 - PageCache 37 + PageCache,
  38 + MemcachedHost
36 }; 39 };
  1 +/**
  2 + *
  3 + * @author: jiangfeng<jeff.jiang@yoho.cn>
  4 + * @date: 16/8/22
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +import Model from './model';
  10 +
  11 +class MemcachedHost extends Model {
  12 +
  13 + constructor() {
  14 + super('memecached_host');
  15 + }
  16 +
  17 +}
  18 +
  19 +export default MemcachedHost;
  1 +
  2 +
  3 +'use strict';
  4 +
  5 +import Router from 'koa-router';
  6 +import _ from 'lodash';
  7 +
  8 +import {
  9 + MemcachedHost
  10 +} from '../../models';
  11 +
  12 +import ApiCache from '../../ci/api_cache';
  13 +
  14 +let r = new Router();
  15 +
  16 +const api_cache = {
  17 + async index(ctx) {
  18 + let hosts = await MemcachedHost.findAll();
  19 +
  20 + await ctx.render('action/api_cache', {hosts: hosts});
  21 + },
  22 +
  23 + async addHost(ctx) {
  24 + let q = ctx.request.body;
  25 +
  26 + await MemcachedHost.insert({host: q.host});
  27 + return ctx.body = {
  28 + code: 200
  29 + };
  30 + },
  31 +
  32 + async delHost(ctx) {
  33 + let q = ctx.request.body;
  34 +
  35 + await MemcachedHost.removeById(q.id);
  36 + return ctx.body = {
  37 + code: 200
  38 + };
  39 + },
  40 +
  41 + async cleanKey(ctx) {
  42 + let q = ctx.request.body;
  43 + let key = q.key;
  44 +
  45 + let hosts = await MemcachedHost.findAll();
  46 +
  47 + _.each(hosts, (h) => {
  48 + (new ApiCache(h.host)).clean(key);
  49 + });
  50 +
  51 + return ctx.body = {
  52 + code: 200
  53 + };
  54 + }
  55 +};
  56 +
  57 +r.get('/', api_cache.index);
  58 +r.post('/host/add', api_cache.addHost);
  59 +r.post('/host/del', api_cache.delHost);
  60 +r.post('/clean', api_cache.cleanKey);
  61 +
  62 +export default r;
@@ -10,6 +10,7 @@ import users from './actions/users'; @@ -10,6 +10,7 @@ import users from './actions/users';
10 import hotfix from './actions/hotfix'; 10 import hotfix from './actions/hotfix';
11 import operationLog from './actions/operation_log'; 11 import operationLog from './actions/operation_log';
12 import pageCahe from './actions/page_cache'; 12 import pageCahe from './actions/page_cache';
  13 +import apiCache from './actions/api_cache';
13 14
14 const noAuth = new Router(); 15 const noAuth = new Router();
15 const base = new Router(); 16 const base = new Router();
@@ -36,6 +37,7 @@ export default function (app) { @@ -36,6 +37,7 @@ export default function (app) {
36 base.use('/hotfix', hotfix.routes(), hotfix.allowedMethods()); 37 base.use('/hotfix', hotfix.routes(), hotfix.allowedMethods());
37 base.use('/operation', operationLog.routes(), operationLog.allowedMethods()); 38 base.use('/operation', operationLog.routes(), operationLog.allowedMethods());
38 base.use('/page_cache', pageCahe.routes(), pageCahe.allowedMethods()); 39 base.use('/page_cache', pageCahe.routes(), pageCahe.allowedMethods());
  40 + base.use('/api_cache', apiCache.routes(), apiCache.allowedMethods());
39 41
40 base.use('', index.routes(), index.allowedMethods()); 42 base.use('', index.routes(), index.allowedMethods());
41 43
  1 +<div class="pageheader">
  2 + <div class="media">
  3 + <div class="pageicon pull-left">
  4 + <i class="fa fa-th-list"></i>
  5 + </div>
  6 + <div class="media-body">
  7 + <ul class="breadcrumb">
  8 + <li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
  9 + <li>缓存管理</li>
  10 + </ul>
  11 + <h4>Api缓存清理</h4>
  12 + </div>
  13 + </div>
  14 + <!-- media -->
  15 +</div>
  16 +<!-- pageheader -->
  17 +
  18 +<div class="contentpanel">
  19 +
  20 + <div class="row">
  21 + <div class="col-sm-4 col-md-3">
  22 +
  23 + <h4 class="md-title mb5">memcached列表</h4>
  24 + <div class="host-list">
  25 + {{#each hosts}}
  26 + <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>
  27 + {{/each}}
  28 + </div>
  29 +
  30 + <div class="mb20"></div>
  31 +
  32 + <button class="btn btn-success" id="add-btn">添加</button>
  33 +
  34 + <br>
  35 +
  36 + </div><!-- col-sm-4 -->
  37 + <div class="col-sm-8 col-md-9">
  38 + <div class="panel">
  39 + <div class="panel-heading">
  40 + <div class="pull-right">
  41 + <a class="btn btn-success btn-rounded mr20 api-clean-btn">删除缓存</a>
  42 + </div>
  43 + <div class="panel-title">
  44 + 缓存key前缀:
  45 + <input type="text" style="width: 33%; height: 40px;" id="api-key">
  46 + </div>
  47 +
  48 +
  49 + </div><!-- panel-heading -->
  50 + <div class="panel-body yoho-log-dark">
  51 + <div class="results-list" id="api-cache-log">
  52 + </div><!-- results-list -->
  53 + </div><!-- panel-body -->
  54 + </div><!-- panel -->
  55 + </div><!-- col-sm-8 -->
  56 + </div><!-- row -->
  57 +
  58 +</div>
  59 +
  60 +<script>
  61 + $(document).on('ready pjax:success', function() {
  62 + $('#add-btn').click(function(){
  63 + var i = layer.prompt({
  64 + title: '请输入host,例如 127.0.0.1:11211'
  65 + }, function(host) {
  66 + $.post('/api_cache/host/add', {host: host}, function(ret) {
  67 + if (ret.code == 200) {
  68 + $('.host-list').append('<p>' + host + '</p>');
  69 + layer.close(i);
  70 + }
  71 + });
  72 + });
  73 + });
  74 +
  75 + $('.api-clean-btn').click(function() {
  76 + var key = $('#api-key').val();
  77 +
  78 + if (key) {
  79 + $.post('/api_cache/clean', {key: key}, function(ret) {
  80 + if (ret.code == 200) {
  81 + layer.msg('正在清理中');
  82 + }
  83 + });
  84 + } else {
  85 + layer.msg('请输入key');
  86 + }
  87 + });
  88 +
  89 + $('.host-remove').click(function(){
  90 + var id = $(this).data('id');
  91 + var host = $(this).data('host');
  92 + var parent = $(this).parent();
  93 + var i = layer.confirm('确定删除host:<code>' + host + '</code>吗?', {
  94 + btn: ['确定', '取消']
  95 + }, function() {
  96 + $.post('/api_cache/host/del', {id: id}, function(ret) {
  97 + if (ret.code === 200) {
  98 + $(parent).remove();
  99 + layer.close(i);
  100 + }
  101 + });
  102 + });
  103 + });
  104 +
  105 + function layoutResize() {
  106 + $('.yoho-log-dark').height($('body').height() - 340);
  107 + }
  108 +
  109 + $(window).resize(function() {
  110 + layoutResize();
  111 + });
  112 +
  113 + layoutResize();
  114 +
  115 +
  116 + var ws = io();
  117 + ws.on('connect', function() {
  118 + ws.on('/api_cache/log', function(data) {
  119 + console.log(data);
  120 +
  121 + $('#api-cache-log').append('<p><span class="host">'+data.host+'</span><span class="message">'+data.msg+'</span></p>')
  122 + });
  123 + });
  124 + ws.on('error', function() {
  125 + console.log('connect fail');
  126 + });
  127 + });
  128 +</script>
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 <li class="parent"><a href=""><i class="fa fa-history"></i> <span>缓存管理</span></a> 28 <li class="parent"><a href=""><i class="fa fa-history"></i> <span>缓存管理</span></a>
29 <ul class="children"> 29 <ul class="children">
30 <li><a href="/page_cache/query">PageCahe清理</a></li> 30 <li><a href="/page_cache/query">PageCahe清理</a></li>
  31 + <li><a href="/api_cache">ApiCahe清理</a></li>
31 {{!-- <li><a href="/cdn_cache/query">CDN清理</a></li> --}} 32 {{!-- <li><a href="/cdn_cache/query">CDN清理</a></li> --}}
32 </ul> 33 </ul>
33 </li> 34 </li>
@@ -51,6 +51,7 @@ @@ -51,6 +51,7 @@
51 "lodash": "^4.13.1", 51 "lodash": "^4.13.1",
52 "md5": "^2.1.0", 52 "md5": "^2.1.0",
53 "md5-file": "^3.1.1", 53 "md5-file": "^3.1.1",
  54 + "memcached": "^2.2.2",
54 "moment": "^2.13.0", 55 "moment": "^2.13.0",
55 "nedb": "^1.8.0", 56 "nedb": "^1.8.0",
56 "nedb-promise": "^2.0.0", 57 "nedb-promise": "^2.0.0",