中国邮政Java面试被问:Actuator端点的安全过滤和JMX导出原理

一、Actuator端点安全过滤深度解析

1.1 端点安全架构概览

java

复制

下载

复制代码
// Actuator安全架构核心组件
┌─────────────────────────────────────────────────────────────┐
│                  应用层安全配置                               │
├─────────────────────────────────────────────────────────────┤
│  @Configuration                                             │
│  public class ActuatorSecurityConfig                        │
│      extends WebSecurityConfigurerAdapter {                 │
│      // 1. 端点角色控制                                     │
│      // 2. IP白名单过滤                                     │
│      // 3. 请求头验证                                       │
│  }                                                          │
├─────────────────────────────────────────────────────────────┤
│                过滤器链 (Filter Chain)                       │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │    CORS     │  │    认证     │  │    授权     │         │
│  │   Filter    │  │   Filter    │  │   Filter    │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
├─────────────────────────────────────────────────────────────┤
│                端点访问控制矩阵                               │
│  ┌─────────────────┬─────────────┬─────────────┐            │
│  │     端点        │  敏感度      │  所需角色   │            │
│  ├─────────────────┼─────────────┼─────────────┤            │
│  │  /actuator/health│  低        │  PUBLIC     │            │
│  │  /actuator/info  │  低        │  PUBLIC     │            │
│  │  /actuator/metrics│ 中        │  MONITOR    │            │
│  │  /actuator/env   │  高        │  ADMIN      │            │
│  │  /actuator/loggers│ 高        │  ADMIN      │            │
│  └─────────────────┴─────────────┴─────────────┘            │
└─────────────────────────────────────────────────────────────┘

1.2 端点安全配置实现

java

复制

下载

复制代码
// 完整端点安全配置类
@Configuration
@Order(1) // 高优先级,先于应用安全配置
public class ActuatorSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    @Value("${management.endpoints.web.exposure.include:health,info}")
    private String[] exposedEndpoints;
    
    @Value("${management.endpoints.web.base-path:/actuator}")
    private String actuatorBasePath;
    
    @Value("${security.actuator.ips:}")
    private String[] allowedIps;
    
    @Autowired
    private ActuatorEndpointProperties endpointProperties;
    
    // 自定义请求头验证器
    @Bean
    public ActuatorHeaderValidator headerValidator() {
        return new ActuatorHeaderValidator();
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 1. 构建端点访问规则
        List<EndpointAccessRule> rules = buildAccessRules();
        
        // 2. 配置安全规则
        http
            // 匹配Actuator端点路径
            .antMatcher(actuatorBasePath + "/**")
            .authorizeRequests()
                // 健康检查端点 - 完全公开
                .antMatchers(HttpMethod.GET, 
                    actuatorBasePath + "/health",
                    actuatorBasePath + "/health/**")
                .permitAll()
                
                // 信息端点 - 基础认证
                .antMatchers(HttpMethod.GET, actuatorBasePath + "/info")
                .hasAnyRole("USER", "MONITOR", "ADMIN")
                
                // 指标端点 - 监控角色
                .antMatchers(HttpMethod.GET, actuatorBasePath + "/metrics")
                .hasRole("MONITOR")
                .antMatchers(HttpMethod.GET, actuatorBasePath + "/metrics/**")
                .hasRole("MONITOR")
                
                // 环境变量端点 - 管理员角色
                .antMatchers(actuatorBasePath + "/env")
                .hasRole("ADMIN")
                .antMatchers(actuatorBasePath + "/env/**")
                .hasRole("ADMIN")
                
                // 日志级别端点 - 管理员角色
                .antMatchers(actuatorBasePath + "/loggers")
                .hasRole("ADMIN")
                .antMatchers(actuatorBasePath + "/loggers/**")
                .hasRole("ADMIN")
                
                // 配置属性端点 - 管理员角色
                .antMatchers(actuatorBasePath + "/configprops")
                .hasRole("ADMIN")
                
                // Bean信息端点 - 管理员角色
                .antMatchers(actuatorBasePath + "/beans")
                .hasRole("ADMIN")
                
                // 线程转储端点 - 管理员角色
                .antMatchers(actuatorBasePath + "/threaddump")
                .hasRole("ADMIN")
                
                // 堆转储端点 - 管理员角色
                .antMatchers(actuatorBasePath + "/heapdump")
                .hasRole("ADMIN")
                
                // 所有其他端点默认拒绝
                .anyRequest().denyAll()
            .and()
            
            // 3. 添加自定义过滤器
            .addFilterBefore(new ActuatorIPFilter(allowedIps), 
                BasicAuthenticationFilter.class)
            .addFilterBefore(new ActuatorHeaderFilter(headerValidator()), 
                BasicAuthenticationFilter.class)
            
            // 4. 配置HTTP Basic认证
            .httpBasic()
                .realmName("Actuator")
                .authenticationEntryPoint(new ActuatorAuthenticationEntryPoint())
            .and()
            
            // 5. 禁用CSRF(API端点通常不需要)
            .csrf().disable()
            
            // 6. 配置会话管理
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            
            // 7. 添加安全头
            .headers()
                .contentSecurityPolicy("default-src 'self'")
                .frameOptions().deny()
                .xssProtection().block(true);
        
        // 8. 动态配置基于属性的端点暴露
        configureExposedEndpoints(http);
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 配置Actuator专用的内存用户存储
        auth.inMemoryAuthentication()
            .withUser("actuator-monitor")
                .password(passwordEncoder().encode("monitor-secret"))
                .roles("MONITOR")
                .and()
            .withUser("actuator-admin")
                .password(passwordEncoder().encode("admin-secret"))
                .roles("ADMIN", "MONITOR")
                .and()
            .withUser("app-user")
                .password(passwordEncoder().encode("user-secret"))
                .roles("USER");
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    /**
     * 构建端点访问规则
     */
    private List<EndpointAccessRule> buildAccessRules() {
        List<EndpointAccessRule> rules = new ArrayList<>();
        
        // 从配置文件中读取端点敏感度配置
        Map<String, EndpointSensitivity> sensitivityMap = 
            endpointProperties.getSensitivity();
        
        for (Map.Entry<String, EndpointSensitivity> entry : sensitivityMap.entrySet()) {
            String endpointId = entry.getKey();
            EndpointSensitivity sensitivity = entry.getValue();
            
            EndpointAccessRule rule = new EndpointAccessRule(
                endpointId,
                sensitivity.getLevel(),
                sensitivity.getRequiredRoles(),
                sensitivity.getAllowedMethods(),
                sensitivity.isRequireSsl()
            );
            rules.add(rule);
        }
        
        return rules;
    }
    
    /**
     * 动态配置暴露的端点
     */
    private void configureExposedEndpoints(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = 
            http.authorizeRequests();
        
        // 根据配置动态添加端点访问规则
        for (String endpoint : exposedEndpoints) {
            String pattern = actuatorBasePath + "/" + endpoint;
            String patternWildcard = pattern + "/**";
            
            // 设置端点访问权限
            registry
                .antMatchers(HttpMethod.GET, pattern)
                    .hasRole(getRequiredRole(endpoint))
                .antMatchers(HttpMethod.GET, patternWildcard)
                    .hasRole(getRequiredRole(endpoint));
        }
    }
    
    /**
     * 获取端点所需角色
     */
    private String getRequiredRole(String endpointId) {
        switch (endpointId.toLowerCase()) {
            case "health":
            case "info":
                return "USER";
            case "metrics":
            case "prometheus":
                return "MONITOR";
            case "env":
            case "loggers":
            case "configprops":
            case "beans":
            case "threaddump":
            case "heapdump":
                return "ADMIN";
            default:
                return "ADMIN"; // 默认需要管理员权限
        }
    }
    
    // 自定义IP过滤器
    public class ActuatorIPFilter extends OncePerRequestFilter {
        private final Set<String> allowedIpRanges;
        
        public ActuatorIPFilter(String[] allowedIps) {
            this.allowedIpRanges = Arrays.stream(allowedIps)
                .collect(Collectors.toSet());
        }
        
        @Override
        protected void doFilterInternal(HttpServletRequest request, 
                                       HttpServletResponse response, 
                                       FilterChain filterChain) 
            throws ServletException, IOException {
            
            String clientIp = getClientIpAddress(request);
            
            // 检查IP是否在白名单中
            if (!isIpAllowed(clientIp)) {
                log.warn("Blocked Actuator access from unauthorized IP: {}", clientIp);
                
                // 记录安全事件
                securityEventLogger.logUnauthorizedAccess(
                    clientIp, 
                    request.getRequestURI(),
                    "IP_NOT_ALLOWED"
                );
                
                response.setStatus(HttpStatus.FORBIDDEN.value());
                response.getWriter().write("Access denied: IP not allowed");
                return;
            }
            
            filterChain.doFilter(request, response);
        }
        
        private String getClientIpAddress(HttpServletRequest request) {
            String xfHeader = request.getHeader("X-Forwarded-For");
            if (xfHeader != null) {
                return xfHeader.split(",")[0];
            }
            return request.getRemoteAddr();
        }
        
        private boolean isIpAllowed(String ip) {
            if (allowedIpRanges.isEmpty()) {
                return true; // 没有配置IP白名单,允许所有
            }
            
            for (String allowedRange : allowedIpRanges) {
                if (ipMatchesRange(ip, allowedRange)) {
                    return true;
                }
            }
            return false;
        }
        
        private boolean ipMatchesRange(String ip, String range) {
            // 支持CIDR格式: 192.168.1.0/24
            // 支持通配符: 192.168.1.*
            // 支持单个IP: 192.168.1.100
            if (range.contains("/")) {
                return isInSubnet(ip, range);
            } else if (range.contains("*")) {
                String regex = range.replace(".", "\\.").replace("*", ".*");
                return ip.matches(regex);
            } else {
                return ip.equals(range);
            }
        }
    }
    
    // 自定义请求头过滤器
    public class ActuatorHeaderFilter extends OncePerRequestFilter {
        private final ActuatorHeaderValidator validator;
        
        public ActuatorHeaderFilter(ActuatorHeaderValidator validator) {
            this.validator = validator;
        }
        
        @Override
        protected void doFilterInternal(HttpServletRequest request,
                                       HttpServletResponse response,
                                       FilterChain filterChain)
            throws ServletException, IOException {
            
            // 验证自定义安全头
            if (!validator.validateHeaders(request)) {
                log.warn("Invalid security headers for Actuator request");
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                return;
            }
            
            // 添加响应头
            response.setHeader("X-Actuator-Access", "secured");
            response.setHeader("X-Content-Type-Options", "nosniff");
            
            filterChain.doFilter(request, response);
        }
    }
}

// 端点访问规则实体
@Data
@AllArgsConstructor
class EndpointAccessRule {
    private String endpointId;
    private SensitivityLevel sensitivity;
    private Set<String> requiredRoles;
    private Set<HttpMethod> allowedMethods;
    private boolean requireSsl;
    
    enum SensitivityLevel {
        PUBLIC,      // 公开访问
        RESTRICTED,  // 受限访问
        SENSITIVE,   // 敏感信息
        CRITICAL     // 关键系统
    }
}

// 请求头验证器
@Component
class ActuatorHeaderValidator {
    
    @Value("${security.actuator.header.name:X-Actuator-Token}")
    private String headerName;
    
    @Value("${security.actuator.header.value:}")
    private String expectedValue;
    
    @Value("${security.actuator.header.enabled:false}")
    private boolean enabled;
    
    public boolean validateHeaders(HttpServletRequest request) {
        if (!enabled) {
            return true;
        }
        
        String headerValue = request.getHeader(headerName);
        
        if (headerValue == null) {
            log.debug("Missing required header: {}", headerName);
            return false;
        }
        
        // 支持JWT Token验证
        if (headerValue.startsWith("Bearer ")) {
            return validateJwtToken(headerValue.substring(7));
        }
        
        // 简单Token验证
        return headerValue.equals(expectedValue);
    }
    
    private boolean validateJwtToken(String token) {
        try {
            // 验证JWT Token
            Jwt jwt = jwtDecoder.decode(token);
            
            // 检查权限
            Collection<String> authorities = jwt.getClaimAsStringList("authorities");
            return authorities.contains("ACTUATOR_ACCESS");
            
        } catch (Exception e) {
            log.warn("Invalid JWT token for Actuator access", e);
            return false;
        }
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

1.3 端点敏感信息过滤

java

复制

下载

复制代码
// 端点响应过滤器 - 防止敏感信息泄露
@Component
public class ActuatorResponseFilter implements Filter {
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Value("${management.endpoint.sensitive.masking.enabled:true}")
    private boolean maskingEnabled;
    
    // 需要过滤的敏感关键词
    private static final Set<String> SENSITIVE_KEYS = Set.of(
        "password", "secret", "key", "token", "credential",
        "private", "authorization", "apikey", "jwt"
    );
    
    // 需要完全隐藏的端点路径
    private static final Set<String> SENSITIVE_ENDPOINTS = Set.of(
        "/actuator/env",
        "/actuator/configprops",
        "/actuator/beans"
    );
    
    @Override
    public void doFilter(ServletRequest request, 
                        ServletResponse response, 
                        FilterChain chain) 
        throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // 创建响应包装器
        ContentCachingResponseWrapper responseWrapper = 
            new ContentCachingResponseWrapper(httpResponse);
        
        chain.doFilter(request, responseWrapper);
        
        // 获取响应内容
        byte[] responseBody = responseWrapper.getContentAsByteArray();
        
        if (responseBody.length > 0) {
            // 处理响应内容
            String filteredBody = filterSensitiveContent(
                httpRequest.getRequestURI(),
                new String(responseBody, responseWrapper.getCharacterEncoding())
            );
            
            // 写入过滤后的响应
            responseWrapper.resetBuffer();
            responseWrapper.getWriter().write(filteredBody);
        }
        
        responseWrapper.copyBodyToResponse();
    }
    
    /**
     * 过滤敏感内容
     */
    private String filterSensitiveContent(String requestUri, String originalContent) {
        if (!maskingEnabled) {
            return originalContent;
        }
        
        try {
            // 根据端点类型应用不同的过滤策略
            if (SENSITIVE_ENDPOINTS.contains(requestUri)) {
                return filterSensitiveEndpoints(originalContent);
            } else if (requestUri.startsWith("/actuator/env/")) {
                return filterEnvEndpoint(originalContent, requestUri);
            }
            
            return originalContent;
            
        } catch (Exception e) {
            log.error("Failed to filter sensitive content", e);
            return originalContent; // 发生异常时返回原始内容
        }
    }
    
    /**
     * 过滤敏感端点响应
     */
    private String filterSensitiveEndpoints(String content) throws JsonProcessingException {
        JsonNode rootNode = objectMapper.readTree(content);
        
        if (rootNode.isObject()) {
            ObjectNode filteredNode = filterObjectNode((ObjectNode) rootNode);
            return objectMapper.writeValueAsString(filteredNode);
        }
        
        return content;
    }
    
    /**
     * 过滤环境变量端点
     */
    private String filterEnvEndpoint(String content, String requestUri) 
        throws JsonProcessingException {
        
        // 提取请求的属性名
        String propertyName = extractPropertyNameFromUri(requestUri);
        
        JsonNode rootNode = objectMapper.readTree(content);
        
        if (rootNode.has("property") && rootNode.has("value")) {
            String property = rootNode.get("property").asText();
            String value = rootNode.get("value").asText();
            
            // 检查是否为敏感属性
            if (isSensitiveProperty(property)) {
                ObjectNode maskedNode = objectMapper.createObjectNode();
                maskedNode.put("property", property);
                maskedNode.put("value", "***MASKED***");
                maskedNode.put("sensitive", true);
                
                return objectMapper.writeValueAsString(maskedNode);
            }
        }
        
        return content;
    }
    
    /**
     * 递归过滤对象节点
     */
    private ObjectNode filterObjectNode(ObjectNode node) {
        ObjectNode filteredNode = objectMapper.createObjectNode();
        
        Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
        while (fields.hasNext()) {
            Map.Entry<String, JsonNode> field = fields.next();
            String fieldName = field.getKey();
            JsonNode fieldValue = field.getValue();
            
            if (isSensitiveKey(fieldName)) {
                // 敏感字段,进行脱敏
                filteredNode.put(fieldName, "***MASKED***");
            } else if (fieldValue.isObject()) {
                // 递归过滤子对象
                filteredNode.set(fieldName, filterObjectNode((ObjectNode) fieldValue));
            } else if (fieldValue.isArray()) {
                // 过滤数组
                ArrayNode filteredArray = filterArrayNode((ArrayNode) fieldValue);
                filteredNode.set(fieldName, filteredArray);
            } else {
                // 非敏感字段,直接复制
                filteredNode.set(fieldName, fieldValue);
            }
        }
        
        return filteredNode;
    }
    
    /**
     * 过滤数组节点
     */
    private ArrayNode filterArrayNode(ArrayNode arrayNode) {
        ArrayNode filteredArray = objectMapper.createArrayNode();
        
        for (JsonNode element : arrayNode) {
            if (element.isObject()) {
                filteredArray.add(filterObjectNode((ObjectNode) element));
            } else {
                filteredArray.add(element);
            }
        }
        
        return filteredArray;
    }
    
    /**
     * 判断是否为敏感键
     */
    private boolean isSensitiveKey(String key) {
        String lowerKey = key.toLowerCase();
        return SENSITIVE_KEYS.stream().anyMatch(lowerKey::contains);
    }
    
    /**
     * 判断是否为敏感属性
     */
    private boolean isSensitiveProperty(String property) {
        // 检查常见的敏感属性模式
        return property.toLowerCase().matches(".*(password|secret|key|token|credential).*") ||
               property.endsWith(".password") ||
               property.endsWith(".secret") ||
               property.contains("api.key") ||
               property.contains("jwt.secret");
    }
    
    /**
     * 从URI中提取属性名
     */
    private String extractPropertyNameFromUri(String uri) {
        // 格式: /actuator/env/property.sources[0].propertyName
        String prefix = "/actuator/env/";
        if (uri.startsWith(prefix)) {
            return uri.substring(prefix.length());
        }
        return "";
    }
}

// 端点访问审计
@Component
public class ActuatorAccessAudit {
    
    @Autowired
    private AuditEventRepository auditEventRepository;
    
    @EventListener
    public void handleAuthenticationSuccess(AuthenticationSuccessEvent event) {
        Authentication auth = event.getAuthentication();
        HttpServletRequest request = ((ServletRequestAttributes) 
            RequestContextHolder.getRequestAttributes()).getRequest();
        
        // 只记录Actuator端点的访问
        if (request.getRequestURI().startsWith("/actuator")) {
            AuditEvent auditEvent = new AuditEvent(
                auth.getName(),
                "ACTUATOR_ACCESS",
                buildAuditData(request, auth)
            );
            
            auditEventRepository.add(auditEvent);
            
            log.info("Actuator access granted: user={}, endpoint={}, roles={}",
                auth.getName(),
                request.getRequestURI(),
                auth.getAuthorities()
            );
        }
    }
    
    @EventListener
    public void handleAuthenticationFailure(AuthenticationFailureBadCredentialsEvent event) {
        HttpServletRequest request = ((ServletRequestAttributes) 
            RequestContextHolder.getRequestAttributes()).getRequest();
        
        if (request.getRequestURI().startsWith("/actuator")) {
            log.warn("Failed Actuator access attempt: endpoint={}, ip={}",
                request.getRequestURI(),
                request.getRemoteAddr()
            );
            
            // 记录失败尝试(可用于实现锁定策略)
            recordFailedAttempt(request.getRemoteAddr());
        }
    }
    
    private Map<String, Object> buildAuditData(HttpServletRequest request, 
                                              Authentication auth) {
        Map<String, Object> data = new HashMap<>();
        
        data.put("endpoint", request.getRequestURI());
        data.put("method", request.getMethod());
        data.put("ip", request.getRemoteAddr());
        data.put("userAgent", request.getHeader("User-Agent"));
        data.put("timestamp", Instant.now());
        data.put("roles", auth.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.toList()));
        
        return data;
    }
    
    private void recordFailedAttempt(String ip) {
        // 实现IP失败次数记录,可用于自动锁定
        // ...
    }
}

二、JMX导出原理深度解析

2.1 JMX架构与核心组件

java

复制

下载

复制代码
// Spring Boot JMX导出架构
┌─────────────────────────────────────────────────────────────┐
│                 Spring Boot Application                      │
├─────────────────────────────────────────────────────────────┤
│  @SpringBootApplication                                      │
│  public class Application {                                  │
│      @Bean                                                   │
│      public EndpointMBeanExporter mbeanExporter() {         │
│          return new EndpointMBeanExporter();                │
│      }                                                       │
│  }                                                           │
├─────────────────────────────────────────────────────────────┤
│                JMX基础设施层                                  │
│  ┌─────────────────────────────────────────────────────┐    │
│  │   MBeanServer (平台MBean服务器)                       │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │   MBean Exporter (Bean导出器)                        │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │   JMX Connector (连接器)                             │    │
│  │   - RMI                                              │    │
│  │   - JMXMP                                            │    │
│  │   - HTML Adaptor                                     │    │
│  └─────────────────────────────────────────────────────┘    │
├─────────────────────────────────────────────────────────────┤
│                监控工具层                                    │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   JConsole  │  │  VisualVM   │  │   JMXTool   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

2.2 JMX MBean导出器实现

java

复制

下载

复制代码
// 自定义Endpoint MBean导出器
@Component
public class EndpointMBeanExporter implements ApplicationListener<ApplicationReadyEvent> {
    
    private static final String MBEAN_DOMAIN = "com.example.actuator";
    
    @Autowired
    private MBeanServer mBeanServer;
    
    @Autowired
    private ApplicationContext applicationContext;
    
    // 导出的MBean映射
    private final Map<String, ObjectName> registeredMBeans = new ConcurrentHashMap<>();
    
    // 端点操作映射
    private final Map<String, EndpointOperation> endpointOperations = new HashMap<>();
    
    @PostConstruct
    public void init() {
        // 初始化端点操作映射
        endpointOperations.put("health", new HealthEndpointOperation());
        endpointOperations.put("metrics", new MetricsEndpointOperation());
        endpointOperations.put("info", new InfoEndpointOperation());
        endpointOperations.put("env", new EnvEndpointOperation());
    }
    
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        try {
            // 1. 导出健康检查端点
            exportHealthEndpoint();
            
            // 2. 导出指标端点
            exportMetricsEndpoint();
            
            // 3. 导出信息端点
            exportInfoEndpoint();
            
            // 4. 导出环境变量端点
            exportEnvEndpoint();
            
            // 5. 导出自定义业务MBean
            exportBusinessMBeans();
            
            // 6. 注册JMX通知监听器
            registerNotificationListeners();
            
            log.info("Successfully exported {} MBeans to JMX", registeredMBeans.size());
            
        } catch (Exception e) {
            log.error("Failed to export MBeans to JMX", e);
        }
    }
    
    /**
     * 导出健康检查端点
     */
    private void exportHealthEndpoint() throws Exception {
        HealthEndpointMBean healthMBean = new HealthEndpointMBean();
        ObjectName objectName = new ObjectName(MBEAN_DOMAIN + ":type=Endpoint,name=Health");
        
        mBeanServer.registerMBean(healthMBean, objectName);
        registeredMBeans.put("health", objectName);
        
        log.debug("Exported Health endpoint as MBean: {}", objectName);
    }
    
    /**
     * 导出指标端点
     */
    private void exportMetricsEndpoint() throws Exception {
        MetricsEndpointMBean metricsMBean = new MetricsEndpointMBean();
        ObjectName objectName = new ObjectName(MBEAN_DOMAIN + ":type=Endpoint,name=Metrics");
        
        mBeanServer.registerMBean(metricsMBean, objectName);
        registeredMBeans.put("metrics", objectName);
        
        log.debug("Exported Metrics endpoint as MBean: {}", objectName);
    }
    
    /**
     * 健康检查端点MBean
     */
    public class HealthEndpointMBean implements HealthEndpointMBeanMBean {
        
        @Autowired
        private HealthEndpoint healthEndpoint;
        
        @Override
        public String getHealthStatus() {
            Health health = healthEndpoint.health();
            return health.getStatus().toString();
        }
        
        @Override
        public Map<String, Object> getHealthDetails() {
            Health health = healthEndpoint.health();
            return health.getDetails();
        }
        
        @Override
        public boolean isDatabaseHealthy() {
            Health health = healthEndpoint.health();
            if (health.getDetails().containsKey("db")) {
                Map<String, Object> dbDetails = (Map<String, Object>) health.getDetails().get("db");
                return "UP".equals(dbDetails.get("status"));
            }
            return false;
        }
        
        @Override
        public boolean isRedisHealthy() {
            Health health = healthEndpoint.health();
            if (health.getDetails().containsKey("redis")) {
                Map<String, Object> redisDetails = (Map<String, Object>) health.getDetails().get("redis");
                return "UP".equals(redisDetails.get("status"));
            }
            return false;
        }
        
        @Override
        public void refreshHealth() {
            // 触发健康检查刷新
            // 实际实现中可能需要调用某些刷新机制
        }
    }
    
    /**
     * 指标端点MBean
     */
    public class MetricsEndpointMBean implements MetricsEndpointMBeanMBean {
        
        @Autowired
        private MetricsEndpoint metricsEndpoint;
        
        private final Map<String, MetricSample> metricCache = new ConcurrentHashMap<>();
        private long lastCacheUpdate = 0;
        private static final long CACHE_TTL = 5000; // 5秒缓存
        
        @Override
        public long getJvmMemoryUsed() {
            return getMetricValue("jvm.memory.used");
        }
        
        @Override
        public long getJvmMemoryMax() {
            return getMetricValue("jvm.memory.max");
        }
        
        @Override
        public double getSystemCpuUsage() {
            return getMetricValue("system.cpu.usage");
        }
        
        @Override
        public long getHttpRequestsTotal() {
            return getMetricValue("http.server.requests", tags -> 
                tags.containsKey("method") && tags.get("method").equals("GET")
            );
        }
        
        @Override
        public double getHttpRequestErrorRate() {
            long totalRequests = getMetricValue("http.server.requests");
            long errorRequests = getMetricValue("http.server.requests", tags ->
                tags.containsKey("status") && 
                Integer.parseInt(tags.get("status")) >= 400
            );
            
            if (totalRequests == 0) {
                return 0.0;
            }
            
            return (double) errorRequests / totalRequests * 100;
        }
        
        @Override
        public Map<String, Double> getCustomMetrics() {
            Map<String, Double> customMetrics = new HashMap<>();
            
            // 获取自定义业务指标
            List<String> metricNames = metricsEndpoint.listNames().getNames();
            
            for (String metricName : metricNames) {
                if (metricName.startsWith("custom.")) {
                    double value = getMetricValue(metricName);
                    customMetrics.put(metricName, value);
                }
            }
            
            return customMetrics;
        }
        
        @Override
        public void resetMetric(String metricName) {
            // 重置特定指标
            // 注意:不是所有指标都支持重置
            log.info("Resetting metric: {}", metricName);
        }
        
        /**
         * 获取指标值(带缓存)
         */
        private double getMetricValue(String metricName) {
            return getMetricValue(metricName, tags -> true);
        }
        
        private double getMetricValue(String metricName, Predicate<Map<String, String>> tagFilter) {
            // 检查缓存
            String cacheKey = metricName + tagFilter.hashCode();
            MetricSample cached = metricCache.get(cacheKey);
            
            if (cached != null && 
                System.currentTimeMillis() - cached.timestamp < CACHE_TTL) {
                return cached.value;
            }
            
            // 从端点获取指标
            try {
                Metric<?> metric = metricsEndpoint.metric(metricName, null);
                if (metric != null) {
                    // 过滤和计算指标值
                    double value = calculateMetricValue(metric, tagFilter);
                    
                    // 更新缓存
                    metricCache.put(cacheKey, new MetricSample(value, System.currentTimeMillis()));
                    
                    return value;
                }
            } catch (Exception e) {
                log.warn("Failed to get metric: {}", metricName, e);
            }
            
            return 0.0;
        }
        
        private double calculateMetricValue(Metric<?> metric, Predicate<Map<String, String>> tagFilter) {
            // 根据指标类型计算值
            if (metric instanceof Gauge) {
                return ((Gauge<?>) metric).value();
            } else if (metric instanceof Counter) {
                return ((Counter) metric).count();
            } else if (metric instanceof Timer) {
                Timer timer = (Timer) metric;
                return timer.mean(timer.baseTimeUnit());
            } else if (metric instanceof DistributionSummary) {
                DistributionSummary summary = (DistributionSummary) metric;
                return summary.mean();
            }
            
            return 0.0;
        }
        
        static class MetricSample {
            final double value;
            final long timestamp;
            
            MetricSample(double value, long timestamp) {
                this.value = value;
                this.timestamp = timestamp;
            }
        }
    }
    
    /**
     * 自定义业务MBean导出
     */
    private void exportBusinessMBeans() throws Exception {
        // 1. 导出数据库连接池MBean
        exportDataSourceMBean();
        
        // 2. 导出缓存MBean
        exportCacheMBean();
        
        // 3. 导出线程池MBean
        exportThreadPoolMBean();
        
        // 4. 导出消息队列MBean
        exportMessageQueueMBean();
    }
    
    /**
     * 导出数据源MBean
     */
    private void exportDataSourceMBean() throws Exception {
        Map<String, DataSource> dataSources = applicationContext.getBeansOfType(DataSource.class);
        
        for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
            String beanName = entry.getKey();
            DataSource dataSource = entry.getValue();
            
            if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) {
                // Tomcat JDBC连接池
                exportTomcatDataSourceMBean(beanName, (org.apache.tomcat.jdbc.pool.DataSource) dataSource);
            } else if (dataSource instanceof com.zaxxer.hikari.HikariDataSource) {
                // HikariCP连接池
                exportHikariDataSourceMBean(beanName, (com.zaxxer.hikari.HikariDataSource) dataSource);
            }
        }
    }
    
    private void exportTomcatDataSourceMBean(String beanName, 
                                            org.apache.tomcat.jdbc.pool.DataSource dataSource) 
        throws Exception {
        
        TomcatDataSourceMBean mbean = new TomcatDataSourceMBean(dataSource);
        ObjectName objectName = new ObjectName(
            MBEAN_DOMAIN + ":type=DataSource,name=" + beanName
        );
        
        mBeanServer.registerMBean(mbean, objectName);
        registeredMBeans.put("datasource." + beanName, objectName);
    }
    
    private void exportHikariDataSourceMBean(String beanName,
                                            com.zaxxer.hikari.HikariDataSource dataSource)
        throws Exception {
        
        HikariDataSourceMBean mbean = new HikariDataSourceMBean(dataSource);
        ObjectName objectName = new ObjectName(
            MBEAN_DOMAIN + ":type=DataSource,name=" + beanName
        );
        
        mBeanServer.registerMBean(mbean, objectName);
        registeredMBeans.put("datasource." + beanName, objectName);
    }
    
    /**
     * Tomcat数据源MBean
     */
    public class TomcatDataSourceMBean implements TomcatDataSourceMBeanMBean {
        
        private final org.apache.tomcat.jdbc.pool.DataSource dataSource;
        
        public TomcatDataSourceMBean(org.apache.tomcat.jdbc.pool.DataSource dataSource) {
            this.dataSource = dataSource;
        }
        
        @Override
        public int getActiveConnections() {
            return dataSource.getActive();
        }
        
        @Override
        public int getIdleConnections() {
            return dataSource.getIdle();
        }
        
        @Override
        public int getMaxActiveConnections() {
            return dataSource.getMaxActive();
        }
        
        @Override
        public int getWaitCount() {
            return dataSource.getWaitCount();
        }

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】​​​

复制代码
        
        @Override
        public void resetStat() {
            dataSource.resetStat();
        }
        
        @Override
        public String getConnectionPoolConfiguration() {
            return String.format(
                "Active: %d, Idle: %d, MaxActive: %d, MaxIdle: %d, MinIdle: %d",
                dataSource.getActive(),
                dataSource.getIdle(),
                dataSource.getMaxActive(),
                dataSource.getMaxIdle(),
                dataSource.getMinIdle()
            );
        }
    }
    
    /**
     * 注册JMX通知监听器
     */
    private void registerNotificationListeners() {
        // 注册健康状态变化监听器
        NotificationListener healthListener = (notification, handback) -> {
            if (notification.getType().equals("com.example.health.status.change")) {
                log.warn("Health status changed: {}", notification.getMessage());
                // 发送告警通知
                sendHealthAlert(notification.getMessage());
            }
        };
        
        // 为所有MBean添加监听器
        for (ObjectName objectName : registeredMBeans.values()) {
            try {
                mBeanServer.addNotificationListener(objectName, healthListener, null, null);
            } catch (Exception e) {
                log.warn("Failed to add notification listener for: {}", objectName, e);
            }
        }
    }
    
    /**
     * JMX连接器配置
     */
    @Configuration
    @ConditionalOnProperty(name = "management.endpoints.jmx.enabled", havingValue = "true")
    public static class JmxConnectorConfiguration {
        
        @Value("${management.endpoints.jmx.port:1099}")
        private int jmxPort;
        
        @Value("${management.endpoints.jmx.host:localhost}")
        private String jmxHost;
        
        @Value("${management.endpoints.jmx.authentication.enabled:true}")
        private boolean authenticationEnabled;
        
        @Value("${management.endpoints.jmx.ssl.enabled:false}")
        private boolean sslEnabled;
        
        @Bean(initMethod = "start", destroyMethod = "stop")
        public ConnectorServerFactoryBean connectorServer() throws Exception {
            ConnectorServerFactoryBean connectorServer = new ConnectorServerFactoryBean();
            
            // 构建服务URL
            String serviceUrl = String.format(
                "service:jmx:rmi://%s:%d/jndi/rmi://%s:%d/jmxrmi",
                jmxHost, jmxPort, jmxHost, jmxPort
            );
            
            connectorServer.setServiceUrl(serviceUrl);
            
            // 配置JMX远程环境
            Map<String, Object> environment = new HashMap<>();
            
            if (authenticationEnabled) {
                environment.put(JMXConnectorServer.AUTHENTICATOR, 
                    createJmxAuthenticator());
            }
            
            if (sslEnabled) {
                environment.putAll(configureSsl());
            }
            
            connectorServer.setEnvironment(environment);
            
            return connectorServer;
        }
        
        private JMXAuthenticator createJmxAuthenticator() {
            return authentication -> {
                String[] credentials = (String[]) authentication.getCredentials();
                String username = credentials[0];
                String password = credentials[1];
                
                // 验证用户名密码
                if (!isValidJmxUser(username, password)) {
                    throw new SecurityException("Invalid JMX credentials");
                }
                
                // 返回授权主体
                return new JMXPrincipal(username);
            };
        }
        
        private boolean isValidJmxUser(String username, String password) {
            // 从配置文件或数据库验证用户
            // 简化实现:硬编码验证
            Map<String, String> jmxUsers = Map.of(
                "jmxadmin", "admin123",
                "jmxmonitor", "monitor123"
            );
            
            return jmxUsers.containsKey(username) && 
                   jmxUsers.get(username).equals(password);
        }
        
        private Map<String, ?> configureSsl() {
            Map<String, Object> sslConfig = new HashMap<>();
            
            try {
                // 配置SSL上下文
                SSLContext sslContext = SSLContext.getInstance("TLS");
                
                // 加载密钥库和信任库
                KeyManagerFactory kmf = KeyManagerFactory.getInstance(
                    KeyManagerFactory.getDefaultAlgorithm());
                TrustManagerFactory tmf = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
                
                // 从配置文件读取
                String keyStorePath = System.getProperty("javax.net.ssl.keyStore");
                String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
                
                if (keyStorePath != null) {
                    KeyStore keyStore = KeyStore.getInstance("JKS");
                    try (FileInputStream fis = new FileInputStream(keyStorePath)) {
                        keyStore.load(fis, keyStorePassword.toCharArray());
                    }
                    
                    kmf.init(keyStore, keyStorePassword.toCharArray());
                    tmf.init(keyStore);
                    
                    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
                    
                    sslConfig.put("com.sun.management.jmxremote.ssl", true);
                    sslConfig.put("com.sun.management.jmxremote.ssl.need.client.auth", false);
                    sslConfig.put("javax.net.ssl.SSLContext", sslContext);
                }
                
            } catch (Exception e) {
                log.error("Failed to configure SSL for JMX", e);
            }
            
            return sslConfig;
        }
    }
    
    /**
     * JMX MBean接口定义
     */
    // 健康端点MBean接口
    public interface HealthEndpointMBeanMBean {
        String getHealthStatus();
        Map<String, Object> getHealthDetails();
        boolean isDatabaseHealthy();
        boolean isRedisHealthy();
        void refreshHealth();
    }
    
    // 指标端点MBean接口
    public interface MetricsEndpointMBeanMBean {
        long getJvmMemoryUsed();
        long getJvmMemoryMax();
        double getSystemCpuUsage();
        long getHttpRequestsTotal();
        double getHttpRequestErrorRate();
        Map<String, Double> getCustomMetrics();
        void resetMetric(String metricName);
    }
    
    // 数据源MBean接口
    public interface TomcatDataSourceMBeanMBean {
        int getActiveConnections();
        int getIdleConnections();
        int getMaxActiveConnections();
        int getWaitCount();
        void resetStat();
        String getConnectionPoolConfiguration();
    }
    
    public interface HikariDataSourceMBeanMBean {
        int getActiveConnections();
        int getIdleConnections();
        int getTotalConnections();
        int getThreadsAwaitingConnection();
        long getConnectionTimeout();
        String getPoolConfiguration();
    }
    
    /**
     * 动态MBean注册与注销
     */
    public void registerDynamicMBean(String name, Object mbean) throws Exception {
        ObjectName objectName = new ObjectName(MBEAN_DOMAIN + ":type=Dynamic,name=" + name);
        
        if (mBeanServer.isRegistered(objectName)) {
            log.warn("MBean already registered: {}", objectName);
            return;
        }
        
        mBeanServer.registerMBean(mbean, objectName);
        registeredMBeans.put("dynamic." + name, objectName);
        
        log.info("Registered dynamic MBean: {}", objectName);
    }
    
    public void unregisterMBean(String key) throws Exception {
        ObjectName objectName = registeredMBeans.get(key);
        if (objectName != null && mBeanServer.isRegistered(objectName)) {
            mBeanServer.unregisterMBean(objectName);
            registeredMBeans.remove(key);
            log.info("Unregistered MBean: {}", objectName);
        }
    }
    
    /**
     * JMX操作权限控制
     */
    @Component
    public class JmxOperationSecurity implements MBeanRegistration {
        
        @Override
        public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
            // 注册前检查权限
            if (!hasPermissionToRegister(name)) {
                throw new SecurityException("No permission to register MBean: " + name);
            }
            return name;
        }
        
        @Override
        public void postRegister(Boolean registrationDone) {
            if (registrationDone) {
                log.info("MBean registered successfully");
            }
        }
        
        @Override
        public void preDeregister() throws Exception {
            // 注销前检查权限
            if (!hasPermissionToUnregister()) {
                throw new SecurityException("No permission to unregister MBean");
            }
        }
        
        @Override
        public void postDeregister() {
            log.info("MBean unregistered");
        }
        
        private boolean hasPermissionToRegister(ObjectName name) {
            // 检查当前上下文是否有权限注册MBean
            SecurityManager security = System.getSecurityManager();
            if (security != null) {
                try {
                    security.checkPermission(new MBeanPermission(
                        name.getCanonicalName(),
                        "registerMBean"
                    ));
                    return true;
                } catch (SecurityException e) {
                    return false;
                }
            }
            return true; // 没有SecurityManager,允许所有
        }
        
        private boolean hasPermissionToUnregister() {
            // 类似实现...
            return true;
        }
    }
}

2.3 JMX监控指标收集器

java

复制

下载

复制代码
// JMX指标收集与导出器
@Component
public class JmxMetricsCollector {
    
    @Autowired
    private MBeanServer mBeanServer;
    
    @Autowired
    private MeterRegistry meterRegistry;
    
    private final Map<String, Gauge> registeredGauges = new ConcurrentHashMap<>();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    
    @PostConstruct
    public void init() {
        // 定期收集JMX指标
        scheduler.scheduleAtFixedRate(this::collectJmxMetrics, 0, 30, TimeUnit.SECONDS);
        
        // 动态发现和注册MBean
        scheduler.scheduleAtFixedRate(this::discoverNewMBeans, 0, 60, TimeUnit.SECONDS);
    }
    
    /**
     * 收集JMX指标
     */
    public void collectJmxMetrics() {
        try {
            // 1. 收集JVM内存指标
            collectJvmMemoryMetrics();
            
            // 2. 收集GC指标
            collectGarbageCollectionMetrics();
            
            // 3. 收集线程指标
            collectThreadMetrics();
            
            // 4. 收集类加载指标
            collectClassLoadingMetrics();
            
            // 5. 收集操作系统指标
            collectOperatingSystemMetrics();
            
            // 6. 收集自定义MBean指标
            collectCustomMBeanMetrics();
            
        } catch (Exception e) {
            log.error("Failed to collect JMX metrics", e);
        }
    }
    
    /**
     * 收集JVM内存指标
     */
    private void collectJvmMemoryMetrics() {
        try {
            ObjectName memoryMBean = new ObjectName("java.lang:type=Memory");
            
            // 堆内存使用
            CompositeData heapUsage = (CompositeData) mBeanServer.getAttribute(
                memoryMBean, "HeapMemoryUsage");
            
            long heapUsed = (Long) heapUsage.get("used");
            long heapCommitted = (Long) heapUsage.get("committed");
            long heapMax = (Long) heapUsage.get("max");
            
            // 注册到Micrometer
            registerGauge("jvm.memory.used", heapUsed, "area", "heap");
            registerGauge("jvm.memory.committed", heapCommitted, "area", "heap");
            if (heapMax != -1) {
                registerGauge("jvm.memory.max", heapMax, "area", "heap");
            }
            
            // 非堆内存
            CompositeData nonHeapUsage = (CompositeData) mBeanServer.getAttribute(
                memoryMBean, "NonHeapMemoryUsage");
            
            long nonHeapUsed = (Long) nonHeapUsage.get("used");
            long nonHeapCommitted = (Long) nonHeapUsage.get("committed");
            
            registerGauge("jvm.memory.used", nonHeapUsed, "area", "nonheap");
            registerGauge("jvm.memory.committed", nonHeapCommitted, "area", "nonheap");
            
        } catch (Exception e) {
            log.warn("Failed to collect JVM memory metrics", e);
        }
    }
    
    /**
     * 收集垃圾回收指标
     */
    private void collectGarbageCollectionMetrics() {
        try {
            Set<ObjectName> gcMBeans = mBeanServer.queryNames(
                new ObjectName("java.lang:type=GarbageCollector,*"), null);
            
            for (ObjectName gcMBean : gcMBeans) {
                String gcName = (String) mBeanServer.getAttribute(gcMBean, "Name");
                
                // 收集次数和时间
                Long collectionCount = (Long) mBeanServer.getAttribute(gcMBean, "CollectionCount");
                Long collectionTime = (Long) mBeanServer.getAttribute(gcMBean, "CollectionTime");
                
                // 注册计数器
                Counter.builder("jvm.gc.collections")
                    .tag("gc", gcName)
                    .register(meterRegistry)
                    .increment(collectionCount);
                
                // 注册计时器
                Timer.builder("jvm.gc.pause")
                    .tag("gc", gcName)
                    .register(meterRegistry)
                    .record(collectionTime, TimeUnit.MILLISECONDS);
            }
            
        } catch (Exception e) {
            log.warn("Failed to collect GC metrics", e);
        }
    }
    
    /**
     * 收集线程指标
     */
    private void collectThreadMetrics() {
        try {
            ObjectName threadMBean = new ObjectName("java.lang:type=Threading");
            
            Integer threadCount = (Integer) mBeanServer.getAttribute(threadMBean, "ThreadCount");
            Integer peakThreadCount = (Integer) mBeanServer.getAttribute(threadMBean, "PeakThreadCount");
            Long totalStartedThreadCount = (Long) mBeanServer.getAttribute(threadMBean, "TotalStartedThreadCount");
            
            registerGauge("jvm.threads.live", threadCount);
            registerGauge("jvm.threads.peak", peakThreadCount);
            registerGauge("jvm.threads.started", totalStartedThreadCount);
            
            // 获取死锁线程
            long[] deadlockedThreads = (long[]) mBeanServer.getAttribute(threadMBean, "ThreadDeadlock");
            if (deadlockedThreads != null) {
                registerGauge("jvm.threads.deadlock", (long) deadlockedThreads.length);
            }
            
        } catch (Exception e) {
            log.warn("Failed to collect thread metrics", e);
        }
    }
    
    /**
     * 发现新的MBean
     */
    private void discoverNewMBeans() {
        try {
            // 查询所有MBean
            Set<ObjectName> allMBeans = mBeanServer.queryNames(null, null);
            
            for (ObjectName mbeanName : allMBeans) {
                String domain = mbeanName.getDomain();
                
                // 只处理我们感兴趣的域
                if (domain.startsWith("com.example") || 
                    domain.equals("java.lang") ||
                    domain.equals("Tomcat") ||
                    domain.equals("Spring")) {
                    
                    // 检查是否已经注册
                    if (!isMBeanRegistered(mbeanName)) {
                        // 分析MBean属性并注册相应指标
                        analyzeAndRegisterMBean(mbeanName);
                    }
                }
            }
            
        } catch (Exception e) {
            log.error("Failed to discover new MBeans", e);
        }
    }
    
    /**
     * 分析并注册MBean指标
     */
    private void analyzeAndRegisterMBean(ObjectName mbeanName) throws Exception {
        MBeanInfo mbeanInfo = mBeanServer.getMBeanInfo(mbeanName);
        MBeanAttributeInfo[] attributes = mbeanInfo.getAttributes();
        
        for (MBeanAttributeInfo attribute : attributes) {
            // 只处理可读的数值属性
            if (attribute.isReadable() && isNumericType(attribute.getType())) {
                String metricName = buildMetricName(mbeanName, attribute.getName());
                
                // 创建动态Gauge
                Gauge gauge = Gauge.builder(metricName, () -> {
                    try {
                        Object value = mBeanServer.getAttribute(mbeanName, attribute.getName());
                        return convertToDouble(value);
                    } catch (Exception e) {
                        log.warn("Failed to read attribute: {}", attribute.getName(), e);
                        return Double.NaN;
                    }
                })
                .tags(buildTags(mbeanName))
                .description(attribute.getDescription())
                .register(meterRegistry);
                
                registeredGauges.put(metricName, gauge);
                
                log.debug("Registered dynamic gauge for MBean: {} -> {}", 
                    mbeanName, attribute.getName());
            }
        }
    }
    
    /**
     * 构建指标名称
     */
    private String buildMetricName(ObjectName mbeanName, String attributeName) {
        String domain = mbeanName.getDomain().replace('.', '_');
        String type = mbeanName.getKeyProperty("type");
        String name = mbeanName.getKeyProperty("name");
        
        StringBuilder metricName = new StringBuilder("jmx.");
        metricName.append(domain);
        
        if (type != null) {
            metricName.append(".").append(type);
        }
        
        if (name != null) {
            metricName.append(".").append(name);
        }
        
        metricName.append(".").append(attributeName.toLowerCase());
        
        return metricName.toString();
    }
    
    /**
     * 构建标签
     */
    private Tags buildTags(ObjectName mbeanName) {
        List<Tag> tags = new ArrayList<>();
        
        // 添加MBean的所有键值对作为标签
        for (Map.Entry<String, String> entry : mbeanName.getKeyPropertyList().entrySet()) {
            tags.add(Tag.of(entry.getKey(), entry.getValue()));
        }
        
        return Tags.of(tags);
    }
    
    /**
     * 判断是否为数值类型
     */
    private boolean isNumericType(String type) {
        return type.equals("int") || type.equals("long") || type.equals("double") || 
               type.equals("float") || type.equals("java.lang.Integer") ||
               type.equals("java.lang.Long") || type.equals("java.lang.Double") ||
               type.equals("java.lang.Float");
    }
    
    /**
     * 转换为double
     */
    private double convertToDouble(Object value) {
        if (value == null) {
            return Double.NaN;
        }
        
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }
        
        try {
            return Double.parseDouble(value.toString());
        } catch (NumberFormatException e) {
            return Double.NaN;
        }
    }
    
    /**
     * 注册Gauge
     */
    private void registerGauge(String name, Number value, String... tags) {
        String gaugeKey = name + Arrays.toString(tags);
        
        Gauge gauge = registeredGauges.get(gaugeKey);
        if (gauge == null) {
            gauge = Gauge.builder(name, () -> value.doubleValue())
                .tags(tags)
                .register(meterRegistry);
            registeredGauges.put(gaugeKey, gauge);
        }
    }
}

2.4 性能优化与缓存策略

java

复制

下载

复制代码
// JMX性能优化与缓存
@Component
public class JmxPerformanceOptimizer {
    
    private final Cache<MBeanQuery, Object> mbeanCache = Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(5, TimeUnit.SECONDS)
        .recordStats()
        .build();
    
    private final Cache<String, MBeanAttributeMetadata> attributeMetadataCache = 
        Caffeine.newBuilder()
            .maximumSize(500)
            .expireAfterAccess(10, TimeUnit.MINUTES)
            .build();
    
    /**
     * 带缓存的MBean属性读取
     */
    public Object getCachedAttribute(ObjectName objectName, String attribute) 
        throws Exception {
        
        MBeanQuery query = new MBeanQuery(objectName, attribute);
        
        return mbeanCache.get(query, key -> {
            try {
                return readAttributeWithRetry(key.objectName, key.attribute);
            } catch (Exception e) {
                throw new RuntimeException("Failed to read attribute", e);
            }
        });
    }
    
    /**
     * 带重试的属性读取
     */
    private Object readAttributeWithRetry(ObjectName objectName, String attribute) 
        throws Exception {
        
        int maxRetries = 3;
        int retryDelay = 100; // 毫秒
        
        for (int attempt = 1; attempt <= maxRetries; attempt++) {
            try {
                return mBeanServer.getAttribute(objectName, attribute);
            } catch (InstanceNotFoundException e) {
                // MBean不存在,不需要重试
                throw e;
            } catch (Exception e) {
                if (attempt == maxRetries) {
                    throw e;
                }
                
                log.warn("Failed to read attribute {}.{}, attempt {}/{}",
                    objectName, attribute, attempt, maxRetries, e);
                
                Thread.sleep(retryDelay * attempt);
            }
        }
        
        throw new IllegalStateException("Should not reach here");
    }
    
    /**
     * 批量读取MBean属性(优化性能)
     */
    public Map<String, Object> getAttributesBatch(ObjectName objectName, 
                                                 String[] attributes) {
        
        Map<String, Object> results = new HashMap<>();
        List<String> uncachedAttributes = new ArrayList<>();
        
        // 检查缓存
        for (String attribute : attributes) {
            MBeanQuery query = new MBeanQuery(objectName, attribute);
            Object cachedValue = mbeanCache.getIfPresent(query);
            
            if (cachedValue != null) {
                results.put(attribute, cachedValue);
            } else {
                uncachedAttributes.add(attribute);
            }
        }
        
        // 批量读取未缓存的属性
        if (!uncachedAttributes.isEmpty()) {
            try {
                AttributeList attributeList = mBeanServer.getAttributes(
                    objectName, uncachedAttributes.toArray(new String[0]));
                
                for (Attribute attr : attributeList.asList()) {
                    results.put(attr.getName(), attr.getValue());
                    
                    // 更新缓存
                    MBeanQuery query = new MBeanQuery(objectName, attr.getName());
                    mbeanCache.put(query, attr.getValue());
                }
                
            } catch (Exception e) {
                log.error("Failed to batch read attributes", e);
            }
        }
        
        return results;
    }
    
    /**
     * MBean查询缓存键
     */
    static class MBeanQuery {
        final ObjectName objectName;
        final String attribute;
        
        MBeanQuery(ObjectName objectName, String attribute) {
            this.objectName = objectName;
            this.attribute = attribute;
        }
        
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            MBeanQuery that = (MBeanQuery) o;
            return objectName.equals(that.objectName) && 
                   attribute.equals(that.attribute);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(objectName, attribute);
        }
    }
    
    /**
     * MBean属性元数据
     */
    static class MBeanAttributeMetadata {
        String name;
        String type;
        String description;
        boolean readable;
        boolean writable;
        boolean isIs; // 是否为isXXX格式的布尔属性
        
        // 性能统计
        long lastAccessTime;
        long accessCount;
        double averageReadTime;
    }
    
    /**
     * JMX连接池(用于高并发场景)
     */
    @Component
    public static class JmxConnectionPool {
        
        private final BlockingQueue<JMXConnector> connectionPool;
        private final String serviceUrl;
        private final Map<String, String> environment;
        
        private final int maxConnections = 10;
        private final int minConnections = 2;
        
        public JmxConnectionPool(@Value("${jmx.service.url}") String serviceUrl) {
            this.serviceUrl = serviceUrl;
            this.environment = new HashMap<>();
            this.connectionPool = new ArrayBlockingQueue<>(maxConnections);
            
            initializePool();
        }
        
        private void initializePool() {
            for (int i = 0; i < minConnections; i++) {
                try {
                    connectionPool.add(createConnection());
                } catch (Exception e) {
                    log.error("Failed to create JMX connection", e);
                }
            }
        }
        
        public JMXConnector borrowConnection() throws Exception {
            JMXConnector connector = connectionPool.poll();
            
            if (connector == null) {
                // 池中没有可用连接,创建新连接
                if (connectionPool.size() < maxConnections) {
                    connector = createConnection();
                } else {
                    // 等待连接释放
                    connector = connectionPool.take();
                }
            }
            
            // 检查连接是否有效
            if (!isConnectionValid(connector)) {
                connector.close();
                connector = createConnection();
            }
            
            return connector;
        }
        
        public void returnConnection(JMXConnector connector) {
            if (connector != null && isConnectionValid(connector)) {
                connectionPool.offer(connector);
            } else {
                try {
                    if (connector != null) {
                        connector.close();
                    }
                } catch (IOException e) {
                    log.warn("Failed to close JMX connection", e);
                }
            }
        }
        
        private JMXConnector createConnection() throws IOException {
            JMXServiceURL url = new JMXServiceURL(serviceUrl);
            return JMXConnectorFactory.connect(url, environment);
        }
        
        private boolean isConnectionValid(JMXConnector connector) {
            try {
                connector.getMBeanServerConnection().getMBeanCount();
                return true;
            } catch (Exception e) {
                return false;
            }
        }
        
        public void closeAllConnections() {
            while (!connectionPool.isEmpty()) {
                try {
                    JMXConnector connector = connectionPool.take();
                    connector.close();
                } catch (Exception e) {
                    log.warn("Failed to close JMX connection", e);
                }
            }
        }
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

三、整合配置与最佳实践

3.1 完整的application.yml配置

yaml

复制

下载

复制代码
# application-actuator-jmx.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
        exclude: env,beans,configprops,loggers
      base-path: /manage
      cors:
        allowed-origins: "https://trusted-domain.com"
        allowed-methods: GET,HEAD
      path-mapping:
        health: healthcheck
        metrics: performance
      
      # 端点安全配置
      access:
        health: PUBLIC
        info: AUTHENTICATED
        metrics: MONITOR_ROLE
        env: ADMIN_ROLE
        loggers: ADMIN_ROLE
      
    jmx:
      exposure:
        include: health,metrics,info
        exclude: env
      domain: com.example.app
      unique-names: true
    
    # 端点敏感度配置
    sensitive:
      health: false
      info: false
      metrics: true
      env: true
      loggers: true
      beans: true
      configprops: true
  
  endpoint:
    health:
      show-details: when_authorized
      show-components: when_authorized
      roles: MONITOR,ADMIN
      probes:
        liveness:
          enabled: true
          path: /manage/health/live
        readiness:
          enabled: true
          path: /manage/health/ready
      
    metrics:
      enabled: true
      export:
        prometheus:
          enabled: true
          step: 1m
      distribution:
        percentiles-histogram:
          http.server.requests: true
        percentiles: 0.5,0.75,0.95,0.99
        sla:
          http.server.requests: 100ms,200ms,500ms,1s
  
  # JMX配置
  jmx:
    enabled: true
    host: localhost
    port: 1099
    ssl:
      enabled: false
    authentication:
      enabled: true
      users:
        - username: jmxadmin
          password: ${JMX_ADMIN_PASSWORD:admin123}
          roles: ADMIN
        - username: jmxmonitor
          password: ${JMX_MONITOR_PASSWORD:monitor123}
          roles: MONITOR

# 安全配置
security:
  actuator:
    enabled: true
    ips: 192.168.1.0/24,10.0.0.0/8
    header:
      name: X-Actuator-Token
      value: ${ACTUATOR_TOKEN:}
      enabled: true
    roles:
      admin: ACTUATOR_ADMIN
      monitor: ACTUATOR_MONITOR
      user: ACTUATOR_USER
    
  # 用户配置(内存存储示例)
  user:
    name: ${ACTUATOR_USERNAME:actuator}
    password: ${ACTUATOR_PASSWORD:}
    roles: MONITOR,USER

# 日志配置
logging:
  level:
    org.springframework.boot.actuate: INFO
    com.example.actuator: DEBUG

3.2 最佳实践总结

  1. 安全最佳实践

    • 始终在生产环境启用Actuator安全

    • 使用最小权限原则配置端点访问

    • 定期轮换认证凭据

    • 监控和审计所有Actuator访问

  2. JMX最佳实践

    • 在生产环境启用JMX认证

    • 使用防火墙限制JMX端口访问

    • 定期监控JMX连接数

    • 考虑使用JMX over SSH隧道

  3. 性能最佳实践

    • 为高频率访问的端点启用缓存

    • 批量读取JMX属性减少RPC调用

    • 使用连接池管理JMX连接

    • 监控JMX操作响应时间

  4. 监控最佳实践

    • 集成Actuator指标到现有监控系统

    • 设置合理的告警阈值

    • 定期review端点暴露配置

    • 实现自动化健康检查

通过这种全面的安全过滤和JMX导出实现,可以在保障系统安全的前提下,提供强大的监控和管理能力。

相关推荐
chipsense3 小时前
守住安全底线:2026年电动汽车充电桩漏电流(RCD)检测技术深度博弈
安全·充电桩·磁通门传感器·漏电流检测
小北方城市网3 小时前
Redis 分布式锁高可用实现:从原理到生产级落地
java·前端·javascript·spring boot·redis·分布式·wpf
ʚB҉L҉A҉C҉K҉.҉基҉德҉^҉大4 小时前
自动化机器学习(AutoML)库TPOT使用指南
jvm·数据库·python
哈__5 小时前
多模融合 一体替代:金仓数据库 KingbaseES 重构企业级统一数据基座
数据库·重构
老邓计算机毕设5 小时前
SSM医院病人信息管理系统e7f6b(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·医院信息化·ssm 框架·病人信息管理
毕设源码-钟学长5 小时前
【开题答辩全过程】以 基于SpringBoot的智能书城推荐系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
2601_949613025 小时前
flutter_for_openharmony家庭药箱管理app实战+药品分类实现
大数据·数据库·flutter
cyforkk5 小时前
MyBatis Plus 字段自动填充:生产级实现方案与原理分析
mybatis
九皇叔叔6 小时前
【03】SpringBoot3 MybatisPlus BaseMapper 源码分析
java·开发语言·mybatis·mybatis plus
dyyx1116 小时前
使用Scikit-learn进行机器学习模型评估
jvm·数据库·python