JMX概念及实际开发应用【实现IP黑名单】

发布时间:2023-08-19 15:30

JMX技术介绍及使用

JMX概念及实际开发应用【实现IP黑名单】_第1张图片

1 JMX概念

JMX:Java Management Extensions技术是Java SE平台的标准功能,提供了一种简单的、标准的监控和管理资源的方式。主要用于监控和管理Java应用程序运行状态、设备和资源信息、Java虚拟机运行情况等信息。JMX可以是动态的,所以可以在资源创建、安装、实现时进行动态监控和管理,JDK自带的jconsole就是使用JMX技术实现的监控工具。

使用JMX技术时,需要定义一个被称为MBean或MXBean的Java对象来表示要管理指定的资源,然后可以把资源信息注册到MBean Server对外提供服务。MBean Server充当了对外提供服务和内存管理MBean资源的代理功能。

JMX不仅可以用于本地管理,JMX Remote API还为JMX添加了远程功能,使之可以通过网络远程监视和管理应用程序。

2 JMX作用

  1. 开箱即用。提供了资源管理、服务托管、远程监控等管理基础功能。
  2. 支持远程使用。不仅可以本地使用,还支持远程调用,如Java EE应用等。
  3. 内置JVM监测。可以监控和管理JVM。
  4. JMX架构优秀,其组件化设计可以自由扩展。
  5. JMX技术严格遵守Java现有规范,如JNDI规范

JNDI:JavaNaming And Directory Interface,Java命名和目录结构
命名服务,如:域名映射到ip地址的服务
目录服务,如:电话簿,如果我们要找某个人的电话号码,我们需要从电话簿里找到这个人的名称,然后再看其电话号码

  1. JMX可以自由的与其他管理解决方案集成,可以通过Web服务管理JMX中的资源。

3 JMX技术架构

JMX技术架构主要有资源管理(MBean/MXBean)模块、资源代理模块(MBean Server)、远程管理模块(Remote API)组成。
JMX概念及实际开发应用【实现IP黑名单】_第2张图片

3.1 资源管理MBean

资源管理在架构中标识为资源探测层(Probe Level),在JMX中,使用MBean或 MXBean来表示一个资源(下面简称MBean),访问和管理资源也都是通过MBean,所以MBean往往包含着资源的属性和操作方法。

JMX已经对JVM进行了多维度的资源检测,所以可以轻松启动JMX代理来访问内置的JVM资源检测,从而通过JMX技术远程监控和管理JVM。

以下是JMX对JVM的资源检测类,都可以直接使用。
JMX概念及实际开发应用【实现IP黑名单】_第3张图片

3.1.1 实际操作

package com.example.test.bean;

import java.lang.management.*;
import java.util.List;
import java.util.stream.Collectors;

public class JavaManagementExtensions {
    public static void main(String[] args) {
        //操作系统信息
        OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
        String osName = operatingSystemMXBean.getName();
        String osVersion = operatingSystemMXBean.getVersion();
        int processors = operatingSystemMXBean.getAvailableProcessors();
        System.out.println(String.format("操作系统:%s,版本:%s,处理器:%d个", osName, osVersion, processors));

        //编译系统信息
        CompilationMXBean compilationMXBean = ManagementFactory.getCompilationMXBean();
        String compilationMXBeanName = compilationMXBean.getName();
        System.out.println("编译系统:" + compilationMXBeanName);

        //内存信息
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
        long max = heapMemoryUsage.getMax();
        long used = heapMemoryUsage.getUsed();
        System.out.println(String.format("使用内存:%dMB/%dMB", used / 1024 / 1024, max / 1024 / 1024));

        //垃圾收集器信息
        List<GarbageCollectorMXBean> gcMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
        String gcNames = gcMXBeans.stream()
                .map(MemoryManagerMXBean::getName)
                .collect(Collectors.joining(","));
        System.out.println("垃圾收集器:" + gcNames);
    }
}

3.1.2 结果

操作系统:Windows 10,版本:10.0,处理器:8个
编译系统:HotSpot 64-Bit Tiered Compilers
使用内存:6MB/3604MB
垃圾收集器:PS Scavenge,PS MarkSweep

3.2 资源代理 MBean Server

资源代理MBean Server是MBean资源的代理,通过MBean Server可以让MBean资源用于远程管理,MBean资源和MBean Server通常是在同一个JVM中,但不是必须的。

想要MBean Server管理MBean资源,首先要把资源注册到MBean Server,任何符合JMX的MBean资源都可以进行注册,最后MBean Server会暴露一个远程通信接口对外提供服务。

合规的MBean -> 注册到MBServer -> MBServer会暴露接口提供服务

3.3 JMX远程管理

可以通过网络协议访问JMX API,如HTTP协议,SNMP(网络管理协议)、RMI远程方法调用协议等,JMX技术默认实现了RMI远程调用协议。

因为资源管理MBean的充分解耦,所以我们可以轻松的把资源管理功能扩展到其他协议,如:通过HTTP在网页端进行管理。

4 JMX实际应用

演示自定义资源MBean,模拟一个内存资源MBean,最后对它进行进程管理

4.1 自定义内存资源管理Bean(普通Java工程方式)

4.1.1 编写资源管理MBean

MBean的编写必须遵守JMX的设计规范,MBean类似于一个特殊的Java Bean,它需要一个接口和一个实现类。MBean资源接口总是以MBean或者MXBean结尾,实现类则是要以接口去掉MBean或MXBean之后的名字来命名。

命名规范:

例如:
MBean资源接口:MyMemoryMBean/MyMemoryMXBean
实现类:MyMemory

自定义编写MyMemoryMXBean:

package com.example.test.bean;

//定义MBean资源接口[如果资源接口中含有自定义实体类的引用,则需要以MXBean结尾,MyMemoryMXBean]
public interface MyMemoryMBean {

    long getTotal();

    void setTotal(long total);

    long getUsed();

    void setUsed(long used);

    String doMemoryInfo();
}

4.1.2 编写实现类MyMemory

package com.example.test.bean;

public class MyMemory implements MyMemoryMBean{

    private long total;
    private long used;

    @Override
    public long getTotal() {
        return total;
    }

    @Override
    public void setTotal(long total) {
        this.total = total;
    }

    @Override
    public long getUsed() {
        return used;
    }

    @Override
    public void setUsed(long used) {
        this.used = used;
    }

    @Override
    public String doMemoryInfo() {
        return String.format("使用内存:%dMB/%dMB", used, total);
    }
}

注意:
①这个例子在MyMemory.java中只有两个long基本类型属性,所以接口是以MBean结尾。如果资源实现类中的属性是自定义实体类的引用,那么接口就需要以MXBean结尾。
②MyMemory与MyMemoryMBea需要在同一个包下,否则的话,需要额外实现其他接口

这样就完成了线程数量资源MBean的创建,其中total和used是资源属性,doMemoryInfo是资源操作方法。

4.1.3 注册资源到MBean Server

通过上面的JMX架构图,我们可以知道MBean资源需要注册到MBean Server进行代理才可以暴露给外部进行调用。所以我们想要通过远程管理我们自定义的MyMemory资源,需要先进行资源代理。

package com.example.test.demo;

import com.example.test.bean.MyMemory;

import javax.management.*;
import java.lang.management.ManagementFactory;

public class MyMemoryManagement {

    public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException, InterruptedException {
        //获取MBean Server
        MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
        //创建【实现类】服务
        MyMemory myMemory = new MyMemory();
        myMemory.setTotal(100L);
        myMemory.setUsed(20L);
        //注册服务到MBean Server
        //com.example.test.bean:type=myMemory [包名:type=服务名]
        ObjectName objectName = new ObjectName("com.example.test.bean:type=myMemory");
        platformMBeanServer.registerMBean(myMemory, objectName);

        while(true){
            Thread.sleep(3000);
            System.out.println(myMemory.doMemoryInfo());
        }
    }
}

结果:

使用内存:20MB/100MB
使用内存:20MB/100MB

JMX概念及实际开发应用【实现IP黑名单】_第4张图片

4.1.4 远程管理jconsole

  • jconsole是Java自带的基于JMX技术的监控管理工具,如果已经配置了JDK环境变量,可以直接在cmd窗口输入jconsole命令启动。
    在这里插入图片描述

  • 启动jconsole后会列出当前机器上的Java进程,我们选择自己想要监控的Java进程监控即可,连接后会提示不安全的协议,因为Java程序默认启动是不会配置HTTPS协议。
    JMX概念及实际开发应用【实现IP黑名单】_第5张图片

  • 连接成功之后就可以看到多维度的JVM监控信息,这些信息都是通过读取JVM资源MBean信息得到的。

4.2 通过JMX实现动态IP黑名单【基于SpringBoot】

传统的应用程序使用JMX,只需要两步:
1.编写MBean,提供管理接口和监控数据
2.注册MBean
在Spring应用程序中,使用JMX只需要一步:
编写MBean,提供管理接口和监控数据【第二步的注册由Spring自动完成,加上@EnableMBeanExport,告诉Spring自动注册MBean】

4.2.1 编写MBean【BlacklistBean】

package com.example.test.demo;

import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

@Component
@ManagedResource(objectName = "com.example.test.demo:name=blacklist", description = "Blacklist of IP address")
public class BlacklistMBean {
    private Set<String> ips = new HashSet<>();

    @ManagedAttribute(description = "Get IP address in blacklist")
    public String[] getBlacklist(){
        int size = ips.size();
        String[] ans = new String[size];
        int i = 0;
        for(String str : ips){
            ans[i++] = str;
        }
        return ans;
    }

    @ManagedOperation
    @ManagedOperationParameter(name="ip", description = "Target IP address that will be added to blacklist")
    public void addBlacklist(String ip){
        ips.add(ip);
    }

    @ManagedOperation
    @ManagedOperationParameter(name="ip", description = "Target IP address that will be removed from blacklist")
    public void removeBlacklist(String ip){
        ips.remove(ip);
    }

    public boolean shouldBlock(String ip){
        return ips.contains(ip);
    }

}

4.2.2 自定义拦截器【拦截黑名单中的ip请求】

package com.example.test.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定义拦截器
 */
@Order(1)
@Component
public class BlacklistInterceptor implements HandlerInterceptor {
    final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    BlacklistMBean blacklistMBean;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String ip = request.getRemoteAddr();
        logger.info("check ip address {}...", ip);
        if(blacklistMBean.shouldBlock(ip)){
            logger.warn("will block ip {} for it is in blacklist...", ip);
            response.sendError(403);
            return false;
        }
        return true;
    }
}

4.2.3 注册拦截器到SpringBoot环境中

必须加上@Configuration注解,Spring才能统一管理当前拦截器的实例
addPathPatterns("/api/**")配置拦截路径,
其中/**表示当前目录以及所有子目录(递归),/*表示当前目录,不包括子目录
package com.example.test.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@ComponentScan
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

	//注入自定义拦截器
    @Autowired
    private BlacklistInterceptor blacklistInterceptor;

    //添加自定义拦截器【拦截所有请求】
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(blacklistInterceptor).addPathPatterns("/**");

    }
}

4.2.4 编写controller及SpringBoot启动类配置

SpringBoot启动类配置(加上@EnableMBeanExport):

@SpringBootApplication
@EnableMBeanExport // 自动注册MBean
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

controller编写:

package com.example.test.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class JMXController {

    @RequestMapping("/test")
    public String testController(){
        return "jmx test....";
    }
}

4.3 通过JMX动态改变MXBean的值

4.3.1 启动jconsole

启动cmd窗口,输入jconsole,选择本地进程【黑名单进程TestApplication】,并连接(默认是不安全连接,无视即可)

JMX概念及实际开发应用【实现IP黑名单】_第6张图片

4.3.2 选择MBean,查看MBean,选择自己的应用

JMX概念及实际开发应用【实现IP黑名单】_第7张图片
此时查看ip黑名单列表,发现没有任何数据
JMX概念及实际开发应用【实现IP黑名单】_第8张图片
在浏览器上访问地址:

http://127.0.0.1:8080/test

出现结果:

jmx test....

服务器日志打印信息:

2022-09-02 11:45:03.522  INFO 15084 --- [nio-8080-exec-1] c.e.test.demo.BlacklistInterceptor       : check ip address 127.0.0.1...

4.3.3 操作MBean:将127.0.0.1添加进ip黑名单,再测试

JMX概念及实际开发应用【实现IP黑名单】_第9张图片
JMX概念及实际开发应用【实现IP黑名单】_第10张图片

调用getBlacklist方法,发现127.0.0.1已经添加进去了:
JMX概念及实际开发应用【实现IP黑名单】_第11张图片
这个时候再访问地址:

http://127.0.0.1:8080/test

发现结果:
JMX概念及实际开发应用【实现IP黑名单】_第12张图片

服务器日志信息:

2022-09-02 13:45:30.817  INFO 15084 --- [nio-8080-exec-4] c.e.test.demo.BlacklistInterceptor       : check ip address 127.0.0.1...
2022-09-02 13:45:30.817  WARN 15084 --- [nio-8080-exec-4] c.e.test.demo.BlacklistInterceptor       : will block ip 127.0.0.1 for it is in blacklist...

可以发现,我们可以通过jconsole动态修改MBean的属性,动态调用其方法

使用jconsole直接通过Local Process 连接JVM有个限制,就是jconsole和正在运行的JVM必须在同一台机器。如果要开远程连接,首先需要打开JMX端口。同时,在启动项目时,需要添加以下JVM启动参数:

  • Dcom.sun.management.jmxremote.port=19999
  • Dcom.sun.management.jmxremote.authenticate=false
  • Dcom.sun.management.jmxremote.ssl=false
第一个参数表示在1999端口监听JMX连接
第二个和第三个表示无需验证,不使用SSL连接,在开发测试阶段比较方便,生产环境必须指定验证
方式并启用SSL(安全性)

许多JavaEE服务器,如JBoss的管理后台都是通过JMX提供管理接口,并由Web方式访问,对用户更
加友好

原文链接:
①https://www.liaoxuefeng.com/wiki/1252599548343744/1282385687609378
②https://www.wdbyte.com/java/jmx.html#_3-jmx-%E7%9A%84%E6%8A%80%E6%9C%AF%E6%9E%B6%E6%9E%84

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号