index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. <template>
  2. <div class="page">
  3. <el-row :gutter="15">
  4. <el-col :span="12" class="marg-b-15">
  5. <el-card shadow="nover" class="box-card-height" style="height:auto">
  6. <template #header>
  7. <div class="card-header">
  8. <span>客户端信息</span>
  9. </div>
  10. </template>
  11. <table cellspacing="0" style=";width: 100%">
  12. <tbody v-if="sysInfo.clients">
  13. <tr>
  14. <td>
  15. <div class="cell-card">当前客户端连接数: </div>
  16. </td>
  17. <td>
  18. <div class="cell-card">{{ sysInfo.clients.connected_clients }}</div>
  19. </td>
  20. </tr>
  21. <tr>
  22. <td>
  23. <div class="cell-card">输出缓冲区中队列对象个数的最大值: </div>
  24. </td>
  25. <td>
  26. <div class="cell-card">{{ sysInfo.clients.client_recent_max_output_buffer }}</div>
  27. </td>
  28. </tr>
  29. <tr>
  30. <td>
  31. <div class="cell-card">输入缓冲区中占用的最大容量:</div>
  32. </td>
  33. <td>
  34. <div class="cell-card">{{ memorySizeFormat(sysInfo.clients.client_recent_max_input_buffer) }}</div>
  35. </td>
  36. </tr>
  37. <tr>
  38. <td>
  39. <div class="cell-card">等待阻塞命令的客户端数量:</div>
  40. </td>
  41. <td>
  42. <div class="cell-card">{{ sysInfo.clients.blocked_clients }}</div>
  43. </td>
  44. </tr>
  45. </tbody>
  46. </table>
  47. </el-card>
  48. </el-col>
  49. <el-col :span="12" class="marg-b-15">
  50. <el-card shadow="nover" class="box-card-height" style="height:auto">
  51. <template #header>
  52. <div class="card-header">
  53. <span>CPU 信息</span>
  54. </div>
  55. </template>
  56. <table cellspacing="0" style="width: 100%">
  57. <tbody v-if="sysInfo.cpu">
  58. <tr>
  59. <td>
  60. <div class="cell-card" title="Redis主进程在内核态所占用的CPU时钟总和">主进程内核态占用CPU时钟: </div>
  61. </td>
  62. <td>
  63. <div class="cell-card">{{ lengthToFixed2(sysInfo.cpu.used_cpu_sys) }}</div>
  64. </td>
  65. </tr>
  66. <tr>
  67. <td>
  68. <div class="cell-card" title="Redis主进程在用户态所占用的CPU时钟总和">主进程用户态占用CPU时钟: </div>
  69. </td>
  70. <td>
  71. <div class="cell-card">{{ lengthToFixed2(sysInfo.cpu.used_cpu_user) }}</div>
  72. </td>
  73. </tr>
  74. <tr>
  75. <td>
  76. <div class="cell-card" title="Redis子进程在内核态所占用的CPU时钟总和">子进程内核态占用CPU时钟:</div>
  77. </td>
  78. <td>
  79. <div class="cell-card">{{ lengthToFixed2(sysInfo.cpu.used_cpu_sys_children) }}</div>
  80. </td>
  81. </tr>
  82. <tr>
  83. <td>
  84. <div class="cell-card" title="Redis子进程在用户态所占用的CPU时钟总和">子进程用户态占用CPU时钟:</div>
  85. </td>
  86. <td>
  87. <div class="cell-card">{{ lengthToFixed2(sysInfo.cpu.used_cpu_user_children) }}</div>
  88. </td>
  89. </tr>
  90. </tbody>
  91. </table>
  92. </el-card>
  93. </el-col>
  94. </el-row>
  95. <el-row :gutter="15">
  96. <el-col :span="12" class="marg-b-15">
  97. <el-card shadow="nover" class="box-card-height" style="height:auto">
  98. <template #header>
  99. <div class="card-header">
  100. <span>服务信息</span>
  101. </div>
  102. </template>
  103. <table cellspacing="0" style="width: 100%">
  104. <tbody v-if="sysInfo.clients">
  105. <tr>
  106. <td>
  107. <div class="cell-card">Redis服务版本: </div>
  108. </td>
  109. <td>
  110. <div class="cell-card">{{ sysInfo.server.redis_version }}</div>
  111. </td>
  112. </tr>
  113. <tr>
  114. <td>
  115. <div class="cell-card">运行模式: </div>
  116. </td>
  117. <td>
  118. <div class="cell-card">{{ sysInfo.server.redis_mode }}</div>
  119. </td>
  120. </tr>
  121. <tr>
  122. <td>
  123. <div class="cell-card">Redis所在机器的操作系统:</div>
  124. </td>
  125. <td>
  126. <div class="cell-card">{{ sysInfo.server.os }}</div>
  127. </td>
  128. </tr>
  129. <tr>
  130. <td>
  131. <div class="cell-card">架构:</div>
  132. </td>
  133. <td>
  134. <div class="cell-card">{{ sysInfo.server.arch_bits }}</div>
  135. </td>
  136. </tr>
  137. <tr>
  138. <td>
  139. <div class="cell-card">Redis所使用的事件处理机制:</div>
  140. </td>
  141. <td>
  142. <div class="cell-card">{{ sysInfo.server.multiplexing_api }}</div>
  143. </td>
  144. </tr>
  145. <tr>
  146. <td>
  147. <div class="cell-card">自Redis服务启动以来,运行的秒数:</div>
  148. </td>
  149. <td>
  150. <div class="cell-card">{{ sysInfo.server.uptime_in_seconds }}</div>
  151. </td>
  152. </tr>
  153. <tr>
  154. <td>
  155. <div class="cell-card">自Redis服务启动以来,运行的天数:</div>
  156. </td>
  157. <td>
  158. <div class="cell-card">{{ sysInfo.server.uptime_in_days }}</div>
  159. </td>
  160. </tr>
  161. <tr>
  162. <td>
  163. <div class="cell-card">serverCron每秒运行次数:</div>
  164. </td>
  165. <td>
  166. <div class="cell-card">{{ sysInfo.server.hz }}</div>
  167. </td>
  168. </tr>
  169. </tbody>
  170. </table>
  171. </el-card>
  172. </el-col>
  173. <el-col :span="12" class="marg-b-15">
  174. <el-card shadow="nover" class="box-card-height" style="height:auto">
  175. <template #header>
  176. <div class="card-header">
  177. <span>内存信息</span>
  178. </div>
  179. </template>
  180. <table v-if='sysInfo.memory' cellspacing="0" style="width: 100%">
  181. <tbody>
  182. <tr>
  183. <td>
  184. <div class="cell-card">Redis分配器分配的内存总量: </div>
  185. </td>
  186. <td>
  187. <div class="cell-card">
  188. {{ memorySizeFormat(sysInfo.memory.used_memory) }} -
  189. {{ lengthToFixed2(sysInfo.memory.used_memory / sysInfo.memory.maxmemory) }}%
  190. </div>
  191. </td>
  192. </tr>
  193. <tr>
  194. <td>
  195. <div class="cell-card">以可读的格式返回used_memory: </div>
  196. </td>
  197. <td>
  198. <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_human) }}</div>
  199. </td>
  200. </tr>
  201. <tr>
  202. <td>
  203. <div class="cell-card">从操作系统的角度,Redis进程占用的物理内存总量:</div>
  204. </td>
  205. <td>
  206. <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_rss) }}</div>
  207. </td>
  208. </tr>
  209. <tr>
  210. <td>
  211. <div class="cell-card">内存使用的最大值:</div>
  212. </td>
  213. <td>
  214. <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_peak) }}</div>
  215. </td>
  216. </tr>
  217. <tr>
  218. <td>
  219. <div class="cell-card">以可读的格式返回used_memory_peak:</div>
  220. </td>
  221. <td>
  222. <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_peak_human) }}</div>
  223. </td>
  224. </tr>
  225. <tr>
  226. <td>
  227. <div class="cell-card">Lua引擎所消耗的内存大小:</div>
  228. </td>
  229. <td>
  230. <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_lua) }}</div>
  231. </td>
  232. </tr>
  233. <tr>
  234. <td>
  235. <div class="cell-card">内存碎片率:</div>
  236. </td>
  237. <td>
  238. <div class="cell-card">{{ sysInfo.memory.mem_fragmentation_ratio }}</div>
  239. </td>
  240. </tr>
  241. <tr>
  242. <td>
  243. <div class="cell-card">Redis所使用的内存分配器:</div>
  244. </td>
  245. <td>
  246. <div class="cell-card">{{ sysInfo.memory.mem_allocator }}</div>
  247. </td>
  248. </tr>
  249. </tbody>
  250. </table>
  251. </el-card>
  252. </el-col>
  253. </el-row>
  254. <div class="flex-row gap-3">
  255. <el-col :xs="24" :sm="24" :md="24" class="marg-b-15">
  256. <el-card shadow="nover" class="box-card-height" style="height:auto">
  257. <template #header>
  258. <div class="card-header">
  259. <span>基础统计信息</span>
  260. </div>
  261. </template>
  262. <div class="flex-row">
  263. <el-form v-if="sysInfo.stats" label-position="right" label-width="160px" class="flex1" style="max-width: 460px">
  264. <el-form-item label="连接过的客户端总数">{{ sysInfo.stats.total_connections_received }}</el-form-item>
  265. <el-form-item label="执行过的命令总数">{{ sysInfo.stats.total_commands_processed }}</el-form-item>
  266. <el-form-item label="每秒处理命令条数">{{ sysInfo.stats.instantaneous_ops_per_sec }}</el-form-item>
  267. <el-form-item label="拒绝的连接个数">{{ sysInfo.stats.rejected_connections }}</el-form-item>
  268. <el-form-item label="key数量">{{ sysInfo.stats.evicted_keys }}</el-form-item>
  269. </el-form>
  270. <el-form v-if="sysInfo.stats" label-position="right" label-width="160px" class="flex1" style="max-width: 460px">
  271. <el-form-item label="网络总入流量">{{ memorySizeFormat(sysInfo.stats.total_net_input_bytes) }}</el-form-item>
  272. <el-form-item label="网络总出流量">{{ memorySizeFormat(sysInfo.stats.total_net_output_bytes) }}</el-form-item>
  273. <el-form-item label="每秒输入量">{{ sysInfo.stats.instantaneous_input_kbps }} kb/s</el-form-item>
  274. <el-form-item label="每秒输出量">{{ sysInfo.stats.instantaneous_output_kbps }} kb/s</el-form-item>
  275. <el-form-item label="最近fork消耗时间">{{ sysInfo.stats.evicted_keys }} μs</el-form-item>
  276. </el-form>
  277. <el-form v-if="sysInfo.stats" label-position="right" label-width="160px" class="flex1" style="max-width: 460px">
  278. <el-form-item label="主从完全同步成功次数">{{ sysInfo.stats.sync_full }}</el-form-item>
  279. <el-form-item label="主从部分同步成功次数">{{ sysInfo.stats.sync_partial_ok }}</el-form-item>
  280. <el-form-item label="主从部分同步失败次数">{{ sysInfo.stats.sync_partial_err }}</el-form-item>
  281. <el-form-item label="正migrate的Redis个数">{{ sysInfo.stats.migrate_cached_sockets }} μs</el-form-item>
  282. <el-form-item label="过期的key数量">{{ sysInfo.stats.expired_keys }}</el-form-item>
  283. </el-form>
  284. <el-form v-if="sysInfo.stats" label-position="right" label-width="160px" class="flex1" style="max-width: 460px">
  285. <el-form-item label="当前使用中的频道数量">{{ sysInfo.stats.pubsub_channels }}</el-form-item>
  286. <el-form-item label="当前使用中的模式数量">{{ sysInfo.stats.pubsub_patterns }}</el-form-item>
  287. <el-form-item label="不命中次数">{{ sysInfo.stats.keyspace_misses }}</el-form-item>
  288. <el-form-item label="命中次数">{{ sysInfo.stats.keyspace_hits }}</el-form-item>
  289. </el-form>
  290. </div>
  291. </el-card>
  292. </el-col>
  293. </div>
  294. <div class="flex-row gap-3">
  295. <el-col :xs="24" :sm="24" :md="24">
  296. <el-card shadow="nover" class="box-card-height" style="height:auto">
  297. <template #header>
  298. <div class="card-header">
  299. <span>info Keyspace统计信息</span>
  300. </div>
  301. </template>
  302. <div style="" class="flex-column" v-if="sysInfo.keyspaceList && sysInfo.keyspace">
  303. <el-form style="display: flex;width: 100%;" v-for="(item, index) in sysInfo.keyspaceList" :key="index" label-position="right" label-width="160px">
  304. <el-form-item class="flex1" label="key名称">{{ item }}</el-form-item>
  305. <el-form-item class="flex1" label="当前数据库key总数">{{ sysInfo.keyspace[item].keys }}</el-form-item>
  306. <el-form-item class="flex1" label="带有过期时间的key总数">{{ sysInfo.keyspace[item].expires }}</el-form-item>
  307. <el-form-item class="flex1" label="平均存活时间">{{ timeFormat(sysInfo.keyspace[item].avg_ttl / 1000) }}</el-form-item>
  308. </el-form>
  309. </div>
  310. </el-card>
  311. </el-col>
  312. </div>
  313. </div>
  314. </template>
  315. <script lang="ts">
  316. import { toRefs, reactive, defineComponent } from 'vue';
  317. import 'echarts-wordcloud';
  318. import getOrigin from '/@/utils/origin'
  319. let interval: any = null;
  320. export default defineComponent({
  321. name: 'monitor',
  322. components: {},
  323. setup() {
  324. const state: any = reactive({
  325. myCharts: [],
  326. sysInfo: {},
  327. });
  328. function startWs() {
  329. const es = new EventSource(getOrigin(import.meta.env.VITE_SERVER_URL + "/subscribe/redisinfo"));
  330. es.addEventListener("stats", statsInfoMsg);
  331. es.addEventListener("clients", clientsInfoMsg);
  332. es.addEventListener("cpu", cpuInfoMsg);
  333. es.addEventListener("server", serverInfoMsg);
  334. es.addEventListener("memory", memoryInfoMsg);
  335. es.addEventListener("keyspace", keyspaceInfoMsg);
  336. }
  337. startWs();
  338. function keyspaceInfoMsg(event: { data: any; }) {
  339. const data = JSON.parse(event.data);
  340. state.sysInfo.keyspaceList = Object.keys(data);
  341. state.sysInfo.keyspace = data
  342. }
  343. function memoryInfoMsg(event: { data: any; }) {
  344. const data = JSON.parse(event.data);
  345. state.sysInfo.memory = data
  346. }
  347. function serverInfoMsg(event: { data: any; }) {
  348. const data = JSON.parse(event.data);
  349. state.sysInfo.server = data
  350. }
  351. function cpuInfoMsg(event: { data: any; }) {
  352. const data = JSON.parse(event.data);
  353. state.sysInfo.cpu = data
  354. }
  355. function statsInfoMsg(event: { data: any; }) {
  356. const data = JSON.parse(event.data);
  357. state.sysInfo.stats = data
  358. }
  359. function clientsInfoMsg(event: { data: any; }) {
  360. const data = JSON.parse(event.data);
  361. state.sysInfo.clients = data
  362. }
  363. return {
  364. ...toRefs(state),
  365. };
  366. },
  367. unmounted() {
  368. if (interval) {
  369. clearInterval(interval);
  370. interval = null;
  371. }
  372. },
  373. data() {
  374. return {};
  375. },
  376. methods: {
  377. memorySizeFormat(size: any) {
  378. size = parseFloat(size);
  379. let rank = 0;
  380. let rankchar = 'Bytes';
  381. while (size > 1024 && rankchar != 'TB') {
  382. size = size / 1024;
  383. rank++;
  384. if (rank == 1) {
  385. rankchar = 'KB';
  386. } else if (rank == 2) {
  387. rankchar = 'MB';
  388. } else if (rank == 3) {
  389. rankchar = 'GB';
  390. } else if (rank == 4) {
  391. rankchar = 'TB';
  392. }
  393. }
  394. return size.toFixed(2) + ' ' + rankchar;
  395. },
  396. lengthToFixed2(size: any) {
  397. size = parseFloat(size);
  398. return size.toFixed(2);
  399. },
  400. timeFormat(second: any) {
  401. if (!second) return '-'
  402. second = parseFloat(second);
  403. let rank = 0;
  404. let rankchar = '秒';
  405. while ((second > 60 && rankchar != '小时' && rankchar != '天') || (second > 24 && rankchar == '小时')) {
  406. if (rankchar == '小时') {
  407. second = second / 24;
  408. } else {
  409. second = second / 60;
  410. }
  411. rank++;
  412. if (rank == 1) {
  413. rankchar = '分';
  414. } else if (rank == 2) {
  415. rankchar = '小时';
  416. } else if (rank == 3) {
  417. rankchar = '天';
  418. }
  419. }
  420. return second.toFixed(2) + ' ' + rankchar;
  421. },
  422. },
  423. });
  424. </script>
  425. <style scoped lang="scss">
  426. .el-card {
  427. height: 300px;
  428. overflow-y: auto;
  429. }
  430. .marg-b-15 {
  431. margin-bottom: 15px;
  432. }
  433. .cell {
  434. box-sizing: border-box;
  435. overflow: hidden;
  436. text-overflow: ellipsis;
  437. white-space: normal;
  438. word-break: break-all;
  439. line-height: 36px;
  440. padding-left: 10px;
  441. padding-right: 10px;
  442. }
  443. .cell-card {
  444. box-sizing: border-box;
  445. overflow: hidden;
  446. text-overflow: ellipsis;
  447. white-space: normal;
  448. word-break: break-all;
  449. line-height: 1.2;
  450. margin: 10px 0;
  451. }
  452. .box-card {
  453. min-height: 380px;
  454. }
  455. .box-card-meter {
  456. height: 230px;
  457. min-height: 180px;
  458. }
  459. .el-form-item{
  460. margin-bottom: 5px;
  461. }
  462. </style>