1. 搭个框架 | 重构ActiveMQ Web Console

1. 搭个框架 | 重构ActiveMQ Web Console_第1张图片
框架

GITHUB:https://github.com/CadMeanM/spring-boot-learnning/tree/spring-demo

最近在自学Springboot,由于其轻量级的web开发特性,让我萌生了重新开始构建ActiveMQ Web Console的想法。记得去年尝试用nodejs+vue构建一个获取ActiveMQ监控数据的页面,但是中途搁置了,原因有很多,一方面是水平能力的不足,另一方面是nodejs用js让我一直觉得不舒服,毕竟我的主要语言还是java。

springboot用起来顺手多了,随着java水平的提升,这次也摆脱了上次用jolokia接口的约束,还是通过JMX接口来获取数据,大致架构就是通过一个webConsole来连接各个AMQ,获取监控数据,并支持集群能力计算。

1. 搭个框架 | 重构ActiveMQ Web Console_第2张图片
大致架构是这样的

这样做的好处主要有几点:

  1. 摆脱需要登录多个ActivmeMQ页面的繁琐操作,一般如果需要通过页面操作集群所有的服务器,需要在web中同时开启N个标签,而如果要清理一个队列,还需要一个个页面进行操作。
  2. ActiveMQ不能支持集群的计算,在完全图的架构下,同一个队列的数据需要将所有ActiveMQ的数据进行一次相加统计并去除集群间转发的部分,无法从单个ActiveMQ上获取这个数据。
  3. 最主要的,我想找个东西练练手……

架构及技术要点

由于自己也是新手,所以有不少问题需要解决,对于一个网站来说,无非是前后端的设计和互动,总结起来就是以下的问题

  1. 前端要展示什么

前端分成两个模块,一个模块展示单个broker状态,另一个计算展示集群状态。

  1. 后端如何快速获取数据

后端与要展示状态的AMQ保持JMX端口的长连接,快速获取数据。连接设置超时机制,超时主动关闭。同时只能与单个集群的所有Broker建立连接,如果跳转到另一个集群则主动断开与上一个集群的所有连接。

  1. 如何保存数据

数据保存在内存中,每次前端调用接口时重新获取数据。所以这个网站只是对当前状态的展示,不涉及对历史数据的趋势视图展示。

  1. 前后端如何交互

前后端通过get,post等方式向后端请求数据,数据为json格式。

技术栈选择

后端毋庸置疑是springboot,前端原来想使用bootstrap,但是前几日看到了layui感觉弹窗很方便,所以这次就选择用layui来做吧。前端使用thymeleaf,主要是用来做router,js使用vue,主要是用vue-resource来替代jquery的ajax调用,通过vue-resource可以不用不停刷页面,只需要更新数据即可。数据的计算通过java写算法实现,图表使用echarts。

搭个框架

少说废话上代码吧。

Controller

package com.springdemo.controller;
import com.springdemo.entity.RespCode;
import com.springdemo.entity.RespEntity;
import com.springdemo.mqinfo.Broker;
import com.springdemo.mqinfo.BrokerInfo;
import org.springframework.boot.SpringApplication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.management.*;
import java.io.IOException;
import java.util.Date;

/**
 * @author Cadmean
 */
@Controller
public class AppController {
    Broker broker=null;

    @RequestMapping("/")
    public String indexMain(){
        return "index";
    }

    @GetMapping("/connectBroker.action")
    public String connectBroker(@RequestParam(value = "addr", required = false) String addr){
        try {
            if (this.broker!=null) {
                if (!this.broker.getAddr().equalsIgnoreCase(addr)) {
                    this.broker.closeConnection();
                    this.broker = new Broker(addr);
                }
            } else {
                this.broker=new Broker(addr);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "broker";
    }

    @RequestMapping(value = "/brokerInfo.json",method=RequestMethod.GET)
    @ResponseBody
    public RespEntity getBrokerInfo() throws MalformedObjectNameException, InstanceNotFoundException, IOException, ReflectionException, AttributeNotFoundException, MBeanException {
        return new RespEntity(RespCode.SUCCESS,this.broker.getBrokerInfo());
    }

}

设计点相关:

  • 有一个index.html主页,用来输入一个IP,跳转到broker.html页面,展示单个broker状态
  • connectBroker.action用于创建一个broker的JMX连接
  • brokerInfo.json用于获取broker的基本信息,json用一个RespEntity来封装,主要是封装上请求是否成功的信息。

数据

package com.springdemo.mqinfo;

/**
 * @author Cadmean
 */
public class BrokerInfo {
    private String brokerName;
    private String addr;
    private String brokerId;
    private String uptime;
    private int connection;
    private long memoryLimit;
    private long storeLimit;

    @Override
    public String toString() {
        return "BrokerInfo{" +
                "brokerName='" + brokerName + '\'' +
                ", addr='" + addr + '\'' +
                ", brokerId='" + brokerId + '\'' +
                ", uptime='" + uptime + '\'' +
                ", connection=" + connection +
                ", memoryLimit=" + memoryLimit +
                ", storeLimit=" + storeLimit +
                '}';
    }

    public String getBrokerName() {
        return brokerName;
    }

    public void setBrokerName(String brokerName) {
        this.brokerName = brokerName;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    public String getBrokerId() {
        return brokerId;
    }

    public void setBrokerId(String brokerId) {
        this.brokerId = brokerId;
    }

    public int getConnection() {
        return connection;
    }

    public void setConnection(int connection) {
        this.connection = connection;
    }

    public long getMemoryLimit() {
        return memoryLimit;
    }

    public void setMemoryLimit(long memoryLimit) {
        this.memoryLimit = memoryLimit;
    }

    public String getUptime() {
        return uptime;
    }

    public void setUptime(String uptime) {
        this.uptime = uptime;
    }

    public BrokerInfo(String addr) {
        this.addr=addr;
    }

    public long getStoreLimit() {
        return storeLimit;
    }

    public void setStoreLimit(long storeLimit) {
        this.storeLimit = storeLimit;
    }
}

package com.springdemo.mqinfo;

import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;

/**
 * @author Cadmean
 */
public class Broker {
    private String url;
    private JMXConnector connector;
    private MBeanServerConnection mbsc;
    private BrokerInfo brokerInfo;
    private String addr;

    public Broker(String addr) throws IOException {
        this.addr=addr;
        this.url="service:jmx:rmi:///jndi/rmi://"+addr+":1099/jmxrmi";
        this.brokerInfo=new BrokerInfo(this.addr);
        createConnector();
    }
    
    public String getAddr() {
        return addr;
    }

    private void createConnector() throws IOException {
        JMXServiceURL url = new JMXServiceURL(this.url);
        connector = JMXConnectorFactory.connect(url, null);
        connector.connect();
        mbsc = connector.getMBeanServerConnection();
    }

    public void closeConnection() throws IOException {
        this.connector.close();
    }
    public BrokerInfo getBrokerInfo() throws MalformedObjectNameException, AttributeNotFoundException, MBeanException, ReflectionException, InstanceNotFoundException, IOException {
        String objectName="org.apache.activemq:brokerName="+this.addr+",type=Broker";
        ObjectName name = new ObjectName(objectName);
        this.brokerInfo.setBrokerName((String) mbsc.getAttribute(name,"BrokerName"));
        this.brokerInfo.setBrokerId((String) mbsc.getAttribute(name,"BrokerId"));
        this.brokerInfo.setConnection((Integer) mbsc.getAttribute(name,"CurrentConnectionsCount"));
        this.brokerInfo.setMemoryLimit((Long) mbsc.getAttribute(name,"MemoryLimit"));
        this.brokerInfo.setStoreLimit((Long) mbsc.getAttribute(name,"StoreLimit"));
        this.brokerInfo.setUptime((String) mbsc.getAttribute(name,"Uptime"));
        return brokerInfo;
    }
}

Broker类用于存放Info信息和一些数据获取的操作,BrokerInfo用于保存Broker基本数据,相对应的后续还可以扩展出Queue、Topic、Connection等等数据信息类。

封装请求

package com.springdemo.entity;

/**
 * @author Cadmean
 */

public enum RespCode {

    SUCCESS(0, "ok"),
    WARN(-1, "fail");

    private int code;
    private String msg;

    RespCode(int code, String msg) {
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }
    public String getMsg() {
        return msg;
    }
}
package com.springdemo.entity;

/**
 * @author Cadmean
 */
public class RespEntity {
    private int code;
    private String msg;
    private R data;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public R getData() {
        return data;
    }

    public void setData(R data) {
        this.data = data;
    }

    public RespEntity(RespCode respCode) {
        this.code = respCode.getCode();
        this.msg = respCode.getMsg();
    }

    public RespEntity(RespCode respCode, R data) {
        this(respCode);
        this.data = data;
    }

    @Override
    public String toString() {
        return "RespEntity{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}

使用RespEntity,并且加了个泛型来作为数据往外传输的通道,RespCode是用来存放返回码和具体返回说明的。在RespEntity中封装上这个就可以在前端得到返回的数据是否正常了。

前端




    
    
    
    
    


UpTime {{ msg.uptime }}
BrokerName {{ msg.brokerName }}
BrokerId {{ msg.brokerId }}
Connections {{ msg.connection }}
MemoryLimit {{ Math.floor(msg.memoryLimit/1024/1024) }} MB
StoreLimit {{ Math.floor(msg.storeLimit/1024/1024/1024) }} GB

用到了layui的table,vue和vue-resource来进行数据渲染。

公司内部网络限制,主要还是在公司内部开发,初步成果差不多是这样。


1. 搭个框架 | 重构ActiveMQ Web Console_第3张图片

你可能感兴趣的:(1. 搭个框架 | 重构ActiveMQ Web Console)