用js写一个zookeeper简易web控制台

使用node.js下的zookeeper客户端访问组件库,写一个 web的控制台,非常简单。比起java的ZooInspector,还有其他一些node下的zk的web控制台,有哪些优势?轻量,部署依赖少,可以单机,可以扔在服务器上多用户访问;依赖少,早起一些node下的zk的控制台,使用了node-zookeeper,这是是一个包装c实现库;这里使用了node-zookeeper-client库,这是一个纯js的zk客户端组件库,更为轻量。

使用express搭建web服务

环境安装,库安装略过;
app.js

const express = require('express')
const bodyParser = require('body-parser')
const api = require('./src/api')

const port = 8123

app = express()
app.use(bodyParser.json({
     limit: '50mb'}))
app.use(express.static('public'))
app.use('/api', api)


app.listen(port, '127.0.0.1', () => console.log(`Server running: http://localhost:${
       port}`))

访问zk的API

const express = require('express')
const zookeeper = require('node-zookeeper-client')
const Long = require('long')
const router = express.Router()

let zkConnections = {
     };

router.post('/zk', async (req, res) => {
     
    switch(req.body.act){
     
        case 'connect': {
     
            let client = await zkClient(req.body.address)
            zkListChildren(client, '/', res)
            break
        }
        case 'list': {
     
            let client = await zkClient(req.body.address)
            zkListChildren(client, req.body.path, res)
            break
        }
        case 'getData': {
     
            let client = await zkClient(req.body.address)
            zkGetData(client, req.body.path, res)
            break
        }
        case 'addNode': {
     
            let client = await zkClient(req.body.address)
            zkAddNode(client, req.body.path, res)
            break
        }
        case 'removeNode': {
     
            let client = await zkClient(req.body.address)
            zkRemoveNode(client, req.body.path, res)
            break
        }
        case 'setData':
            let client = await zkClient(req.body.address)
            zkSetData(client, req.body.path, req.body.data, res)
            break
        default:
            res.send('')
    }
})

async function zkClient(address){
     
    let client = zkConnections[address]
    if(client){
     
        let state = client.getState()
        if(state === zookeeper.State.SYNC_CONNECTED){
     
            return Promise.resolve(client)
         }else{
     
             client.close()
             delete zkConnections[address]
             return await zkClient(address)
         }
    }else{
     
        client = zookeeper.createClient(address)
        zkConnections[address] = client
        console.log('new connetion: %s', address)
        client.connect()
        client.on('state', state => {
     
            console.log(state)
        })
        return new Promise((resolve, reject) => {
     
            client.once('connected', ()=>{
     
                resolve(client)
            })
        })
    }
}

function zkGetData(client, path, res) {
     
    client.getData(path, (err, data, stat) => {
     
        if(err){
     
            console.log('%s', err)
        }
        let s = {
     }
        stat.specification.forEach(i => {
     
            s[i.name] = i.type === 'long' ? Long.fromBytes(stat[i.name]).toString() : stat[i.name] 
        });
        res.send({
     data: (data && data.length > 0) ? data.toString('utf8') : '', stat: s})
    })
}

function zkListChildren(client, path, res) {
     
    client.getChildren(path, (err, children, stat) => {
     
        if(err){
     
            console.log('%s', err)
        }
        res.send(children)
    })
}

function zkRemoveNode(client, path, res) {
     
    client.remove(path, -1, (err)=>{
     
        if(err){
     
            console.log('%s', err)
            res.send('fail')
        }else{
     
            res.send('success')
        }
    })
}

function zkAddNode(client, path, res){
     
    client.create(path, null, null, zookeeper.CreateMode.PERSISTENT, (err) =>{
     
        if(err){
     
            console.log('%s', err)
            res.send('fail')
        }else{
     
            res.send('success')
        }
    })
}

function zkSetData(client, path, data, res){
     
    client.setData(path, Buffer.from(data), -1, (error, stat) => {
     
        if (error) {
     
            console.log(error.stack);
            res.send('fail')
        }else{
     
            res.send('success')
        }
    })
}

module.exports = router

使用Vue.js搭建前端

这里vue使用简单的方式,可以参考不使用Webpack的Vue传统前端使用方式(一)。

说说主要的几个点。

使用CDN引入js库

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js" integrity="sha256-ngFW3UnAN0Tnm76mDuu7uUtYEcG3G5H1+zioJw3t+68=" crossorigin="anonymous">script>

使用了以下前端组件:

  • boostrap前端css/html框架
  • nanoscroller 滚动条
  • vue 前端mvvm框架
  • axios http请求库

使用js加载js

这个方法可以实现使用js加载js到html中,而且是同步的;

async function loadjs(src) {
     
  if(src instanceof Array){
     
    return Promise.all(src.map(i => loadjs(i)))
  }
  let script = document.createElement('script')
  script.setAttribute('type', 'text/javascript')
  script.src = src;
  document.getElementsByTagName('body')[0].appendChild(script)
  return new Promise(resolve => {
     
    script.onload = () => {
     
      return resolve('ok')
    }
  })
}

用法:

await loadjs([
  'js/vue_com.js',
  'js/vue_zk.js'
])

vue实现tree组件

Vue.component('tree', {
     
  template: `
    
  `,
  props: ['nodes', 'show'],
  data(){
     
    return {
     }
  },
  methods:{
     
    loadAndToggleExpand(node){
     
      if(node.show === null){
     
        this.$emit('load', node)
      }else{
     
        node.show = !node.show
      }
    },
    nodeClick(e, node){
     
      this.$emit('nodeClick', node)
    },
    childNodeClick(node){
     
      this.$emit('nodeClick', node)
    },
    childNodeLoad(node){
     
      this.$emit('load', node)
    }
  }
  
});

主界面及操作表单

详细代码不贴了,可以直接访问仓库: https://github.com/ccor/zk-browser

主界面主要是左侧树形目录,右侧的数据显示和操作区;

主要实现了经典的程序员的四门功课:增删改查。一般来说,基本够用了。

你可能感兴趣的:(nodejs,vue,zookeeper)