add api memcache clean
Showing
8 changed files
with
308 additions
and
1 deletions
apps/ci/api_cache.js
0 → 100644
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 | }; |
apps/models/memcached_host.js
0 → 100644
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; |
apps/web/actions/api_cache.js
0 → 100644
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 |
apps/web/views/action/api_cache.hbs
0 → 100644
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", |
-
Please register or login to post a comment