上一篇
"小王最近接手了一个电商项目,数据库里存着大量用户购物车数据、秒杀活动缓存和会话信息,全都用Redis处理,每次排查问题都要登录服务器敲命令行,查看键值对简直像大海捞针,团队里非技术同事更是一头雾水,总来问他'这个缓存能不能清一下?''那个数据能不能导出来?'..."
如果你也遇到过类似情况,是时候自己动手搭建一个可视化的Redis管理页面了!今天我们就从零开始,用最接地气的方式实现这个需求。
# 前端项目初始化 npm install -g @vue/cli vue create redis-admin cd redis-admin npm install element-ui axios --save # 后端服务初始化 mkdir server cd server npm init -y npm install express ioredis cors body-parser --save
在server/index.js
中写入:
const express = require('express'); const Redis = require('ioredis'); const cors = require('cors'); const bodyParser = require('body-parser'); const app = express(); app.use(cors()); app.use(bodyParser.json()); // 创建Redis连接(实际项目记得用环境变量配置) const redis = new Redis({ host: '127.0.0.1', port: 6379, password: '你的密码' // 没有密码就去掉这行 }); // 健康检查接口 app.get('/ping', async (req, res) => { try { const pong = await redis.ping(); res.send(pong); } catch (e) { res.status(500).send('Redis连接失败'); } }); app.listen(3001, () => { console.log('Redis管理后端已启动: http://localhost:3001'); });
启动服务试试:
node index.js
继续在index.js
中添加这些实用接口:
// 获取键列表(支持模式匹配) app.post('/keys', async (req, res) => { const { pattern = '*' } = req.body; try { const keys = await redis.keys(pattern); res.json({ success: true, data: keys }); } catch (e) { res.status(500).json({ success: false, message: e.message }); } }); // 获取键值详情 app.post('/key-info', async (req, res) => { const { key } = req.body; try { const type = await redis.type(key); let value, ttl; switch (type) { case 'string': value = await redis.get(key); break; case 'hash': value = await redis.hgetall(key); break; case 'list': value = await redis.lrange(key, 0, -1); break; case 'set': value = await redis.smembers(key); break; case 'zset': value = await redis.zrange(key, 0, -1, 'WITHSCORES'); break; } ttl = await redis.ttl(key); res.json({ success: true, data: { type, value, ttl } }); } catch (e) { res.status(500).json({ success: false, message: e.message }); } }); // 删除键 app.post('/delete-key', async (req, res) => { const { key } = req.body; try { await redis.del(key); res.json({ success: true }); } catch (e) { res.status(500).json({ success: false, message: e.message }); } });
修改src/App.vue
:
<template> <div id="app"> <el-container> <!-- 侧边栏 --> <el-aside width="250px" style="background:#545c64;min-height:100vh"> <div style="color:#fff;padding:15px;font-size:18px"> <i class="el-icon-menu"></i> Redis管理 </div> <el-menu background-color="#545c64" text-color="#fff" active-text-color="#ffd04b"> <el-menu-item index="1"> <i class="el-icon-document"></i> <span>键值管理</span> </el-menu-item> <el-menu-item index="2"> <i class="el-icon-setting"></i> <span>服务器状态</span> </el-menu-item> </el-menu> </el-aside> <!-- 主内容区 --> <el-main> <router-view/> </el-main> </el-container> </div> </template> <script> export default { name: 'App' } </script>
创建src/views/KeyManager.vue
:
<template> <div class="key-manager"> <el-card shadow="hover"> <div slot="header" class="clearfix"> <span>Redis键值管理</span> <el-button-group style="float:right"> <el-button size="mini" @click="refreshKeys">刷新</el-button> <el-button size="mini" type="primary" @click="showAddDialog"> 新增键 </el-button> </el-button-group> </div> <!-- 搜索框 --> <el-input v-model="searchPattern" placeholder="输入匹配模式(如:user:*)" clearable style="margin-bottom:20px"> <el-button slot="append" icon="el-icon-search" @click="fetchKeys"/> </el-input> <!-- 键列表表格 --> <el-table :data="keys" border stripe height="500" v-loading="loading" @row-click="showKeyDetail"> <el-table-column prop="key" label="键名" width="300"/> <el-table-column label="操作" width="120"> <template slot-scope="scope"> <el-button size="mini" type="danger" icon="el-icon-delete" @click.stop="deleteKey(scope.row.key)"/> </template> </el-table-column> </el-table> <!-- 键详情对话框 --> <el-dialog :title="currentKey" :visible.sync="detailVisible" width="70%"> <el-tabs v-model="activeTab"> <el-tab-pane label="基本信息" name="info"> <el-descriptions border column={2}> <el-descriptions-item label="键类型"> {{ keyInfo.type }} </el-descriptions-item> <el-descriptions-item label="剩余TTL"> {{ keyInfo.ttl === -1 ? '永不过期' : `${keyInfo.ttl}秒` }} </el-descriptions-item> </el-descriptions> </el-tab-pane> <el-tab-pane :label="`值内容(${keyInfo.type})`" name="value"> <!-- 字符串类型展示 --> <el-input v-if="keyInfo.type === 'string'" type="textarea" :rows="5" v-model="keyInfo.value" readonly/> <!-- 哈希类型展示 --> <el-table v-else-if="keyInfo.type === 'hash'" :data="hashToArray(keyInfo.value)" border> <el-table-column prop="field" label="字段名"/> <el-table-column prop="value" label="字段值"/> </el-table> <!-- 其他类型展示... --> </el-tab-pane> </el-tabs> </el-dialog> </el-card> </div> </template> <script> export default { data() { return { searchPattern: '*', keys: [], loading: false, currentKey: '', detailVisible: false, activeTab: 'info', keyInfo: { type: '', value: null, ttl: -1 } } }, methods: { async fetchKeys() { this.loading = true; try { const res = await this.$http.post('/keys', { pattern: this.searchPattern }); this.keys = res.data.data.map(key => ({ key })); } catch (e) { this.$message.error('获取键列表失败: ' + e.message); } finally { this.loading = false; } }, async showKeyDetail(row) { this.currentKey = row.key; try { const res = await this.$http.post('/key-info', { key: row.key }); this.keyInfo = res.data.data; this.detailVisible = true; } catch (e) { this.$message.error('获取键详情失败: ' + e.message); } }, async deleteKey(key) { try { await this.$confirm(`确定删除键 "${key}" 吗?`, '警告', { type: 'warning' }); await this.$http.post('/delete-key', { key }); this.$message.success('删除成功'); this.fetchKeys(); } catch (e) { if (e !== 'cancel') { this.$message.error('删除失败: ' + e.message); } } }, hashToArray(hash) { return Object.keys(hash).map(key => ({ field: key, value: hash[key] })); }, refreshKeys() { this.searchPattern = '*'; this.fetchKeys(); } }, mounted() { this.fetchKeys(); } } </script>
在src/main.js
中:
import Vue from 'vue' import App from './App.vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import axios from 'axios' import router from './router' Vue.use(ElementUI) // 配置axios Vue.prototype.$http = axios.create({ baseURL: 'http://localhost:3001' }) new Vue({ router, render: h => h(App) }).$mount('#app')
创建src/router/index.js
:
import Vue from 'vue' import Router from 'vue-router' import KeyManager from '@/views/KeyManager.vue' Vue.use(Router) export default new Router({ routes: [ { path: '/', redirect: '/keys' }, { path: '/keys', component: KeyManager } ] })
在后端index.js
中添加:
// 获取服务器信息 app.get('/server-info', async (req, res) => { try { const info = await redis.info(); // 将Redis的INFO命令返回的多行文本转为对象 const result = {}; info.split('\r\n').forEach(line => { if (line && !line.startsWith('#')) { const [key, value] = line.split(':'); if (key && value) result[key] = value; } }); res.json({ success: true, data: result }); } catch (e) { res.status(500).json({ success: false, message: e.message }); } });
前端添加ServerStatus.vue
组件(代码类似键值管理,主要展示内存使用、连接数等关键指标)
在后端添加:
// 设置键值 app.post('/set-key', async (req, res) => { const { key, value, ttl } = req.body; try { if (ttl > 0) { await redis.set(key, value, 'EX', ttl); } else { await redis.set(key, value); } res.json({ success: true }); } catch (e) { res.status(500).json({ success: false, message: e.message }); } });
// 使用连接池 const redis = new Redis.Cluster([ { host: 'redis-node1', port: 6379 }, { host: 'redis-node2', port: 6379 } ]);
现在你的团队再也不用对着命令行发愁了!这个管理页面虽然简单,但已经覆盖了80%的日常需求,根据实际业务,你还可以继续添加:
希望这篇指南能帮你快速搭建起实用的Redis管理工具,如果有任何问题,欢迎在评论区交流讨论!
本文由 麦冰之 于2025-08-02发表在【云服务器提供商】,文中图片由(麦冰之)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/512852.html
发表评论