spring boot metrics信息推送开发

简介:     上一篇文章是关于 “spring boot +RabbitMQ +InfluxDB+Grafara监控实践” 主要讲spring boot应用新能监控信息的收集方案实践   实践是hystrix信息推送的mq而metrics信息需要扫描,文章的最后也有相应的思考metrics信息能不能是应用本身也推送到mq那?   本篇文章就实践关于metrics信息的推送实现     有了上面的思考之后我就回过头来去看hystrix是怎么实现推送的。

 

  上一篇文章是关于 “spring boot +RabbitMQ +InfluxDB+Grafara监控实践” 主要讲spring boot应用新能监控信息的收集方案实践

  实践是hystrix信息推送的mq而metrics信息需要扫描,文章的最后也有相应的思考metrics信息能不能是应用本身也推送到mq那?

  本篇文章就实践关于metrics信息的推送实现

 

  有了上面的思考之后我就回过头来去看hystrix是怎么实现推送的。经过一番跟踪之后找到了具体干活的task代码

  

  有了这个代码就可以参考具体怎样实现metrics信息的推送了

  但是还有一个问题就是metrics信息虽然暴露了url接口但是应用内我怎么获取那???

  这里又引发了我们一探究竟的兴趣!。。。。。。继续看源码!!!!!!!!!!!

  从spring boot启动展示的日志中我们可以发现线索,具体/metrics路径具体执行的是哪里

  

Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()

  从org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()这里我们发现了端倪

  好的 我们就去这个包去找相关线索

  

  好的我们找到了这个包往下看

  终于找到他了这里我们就可以用定时器进行轮训调用了。基础准备已经ok,好了不多说了直接上写好的代码


package com.zjs.mic.metrics.stream;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.cloud.stream.config.BindingProperties;
import org.springframework.cloud.stream.config.BindingServiceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.MessageChannel;
import org.springframework.scheduling.annotation.EnableScheduling;


@RefreshScope
@Configuration
@ConditionalOnClass({EnableBinding.class })
@ConditionalOnProperty(value = "metrics.stream.queue.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@EnableScheduling
@EnableBinding(MetricsStreamClient.class)
public class MetricsStreamAutoConfiguration {

    @Autowired
    private BindingServiceProperties bindings;

    @Autowired
    private MetricsStreamProperties properties;

    @Autowired
    @Output(MetricsStreamClient.OUTPUT)
    private MessageChannel outboundChannel;

    @Autowired(required = false)
    private Registration registration;
    
    
    @Autowired
    MetricsMvcEndpoint mme;
    

    @Bean
    public HasFeatures metricsStreamQueueFeature() {
        return HasFeatures.namedFeature("Metrics Stream (Queue)",
                MetricsStreamAutoConfiguration.class);
    }

    @PostConstruct
    public void init() {
        BindingProperties outputBinding = this.bindings.getBindings()
                .get(MetricsStreamClient.OUTPUT);
        if (outputBinding == null) {
            this.bindings.getBindings().put(MetricsStreamClient.OUTPUT,
                    new BindingProperties());
        }
        BindingProperties output = this.bindings.getBindings()
                .get(MetricsStreamClient.OUTPUT);
        if (output.getDestination() == null) {
            output.setDestination(this.properties.getDestination());
        }
        if (output.getContentType() == null) {
            output.setContentType(this.properties.getContentType());
        }
    }
    @Bean
    public MetricsStreamTask metricsStreamTask(SimpleDiscoveryClient simpleDiscoveryClient) {
        ServiceInstance serviceInstance = this.registration;
        if (serviceInstance == null) {
            serviceInstance = simpleDiscoveryClient.getLocalServiceInstance();
        }
        return new MetricsStreamTask(this.outboundChannel, serviceInstance,
                this.properties,this.mme);
    }
}


package com.zjs.mic.metrics.stream;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("metrics.stream.queue")
public class MetricsStreamProperties {

    private boolean enabled = true;

    private boolean prefixMetricName = true;

    private boolean sendId = true;

    private String destination = "springCloudMetricsStream";

    private String contentType = "application/json";
    
    private String pathTail = "mem.*|heap.*|threads.*|gc.*|nonheap.*|classes.*";

    private long sendRate = 1000;

    private long gatherRate = 1000;

    private int size = 1000;


    public String getPathTail() {
        return pathTail;
    }

    public void setPathTail(String pathTail) {
        this.pathTail = pathTail;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public boolean isPrefixMetricName() {
        return prefixMetricName;
    }

    public void setPrefixMetricName(boolean prefixMetricName) {
        this.prefixMetricName = prefixMetricName;
    }

    public boolean isSendId() {
        return sendId;
    }

    public void setSendId(boolean sendId) {
        this.sendId = sendId;
    }

    public String getDestination() {
        return destination;
    }

    public void setDestination(String destination) {
        this.destination = destination;
    }

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public long getSendRate() {
        return sendRate;
    }

    public void setSendRate(long sendRate) {
        this.sendRate = sendRate;
    }

    public long getGatherRate() {
        return gatherRate;
    }

    public void setGatherRate(long gatherRate) {
        this.gatherRate = gatherRate;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
}


package com.zjs.mic.metrics.stream;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.util.Assert;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;


@EnableScheduling
public class MetricsStreamTask {
    private final static Logger log = LoggerFactory.getLogger(MetricsStreamTask.class);

    private MessageChannel outboundChannel;

    private ServiceInstance registration;

    private MetricsStreamProperties properties;

    private MetricsMvcEndpoint mme;

    // Visible for testing
    final LinkedBlockingQueue<String> jsonMetrics;

    private final JsonFactory jsonFactory = new JsonFactory();

    public MetricsStreamTask(MessageChannel outboundChannel,
                             ServiceInstance registration, MetricsStreamProperties properties, MetricsMvcEndpoint mme) {
        Assert.notNull(outboundChannel, "outboundChannel may not be null");
        Assert.notNull(registration, "registration may not be null");
        Assert.notNull(properties, "properties may not be null");
        Assert.notNull(mme, "properties may not be null");
        this.outboundChannel = outboundChannel;
        this.registration = registration;
        this.properties = properties;
        this.jsonMetrics = new LinkedBlockingQueue<>(properties.getSize());
        this.mme=mme;
    }
    // TODO: use integration to split this up?
    @Scheduled(fixedRateString = "${metrics.stream.queue.sendRate:1000}")
    public void sendMetrics() {

        log.info("推送metrics信息");
        
        ArrayList<String> metrics = new ArrayList<>();
        this.jsonMetrics.drainTo(metrics);

        if (!metrics.isEmpty()) {
            if (log.isTraceEnabled()) {
                log.trace("sending stream Metrics metrics size: " + metrics.size());
            }
            for (String json : metrics) {
                // TODO: batch all metrics to one message
                try {
                    // TODO: remove the explicit content type when s-c-stream can handle
                    // that for us
                    this.outboundChannel.send(MessageBuilder.withPayload(json)
                            .setHeader(MessageHeaders.CONTENT_TYPE,
                                    this.properties.getContentType())
                            .build());
                }
                catch (Exception ex) {
                    if (log.isTraceEnabled()) {
                        log.trace("failed sending stream Metrics metrics: " + ex.getMessage());
                    }
                }
            }
        }
    }

    
    
    @Scheduled(fixedRateString = "${metrics.stream.queue.gatherRate:1000}")
    public void gatherMetrics() {
        log.info("开始获取metrics信息");
        try {
            
            StringWriter jsonString = new StringWriter();
            JsonGenerator json = this.jsonFactory.createGenerator(jsonString);
            json.writeStartObject();
            json.writeObjectField("instanceId",registration.getServiceId() + ":" + registration.getHost() + ":"
                    + registration.getPort());
            json.writeObjectField("type", "metrics");
            json.writeObjectField("currentTime",System.currentTimeMillis());
            @SuppressWarnings("unchecked")
            Map<String, Object> map = (Map<String, Object>) mme.value(this.properties.getPathTail());
            
            for (String str : map.keySet()) {
                json.writeObjectField(str, map.get(str));
            }
            
            json.writeEndObject();
            json.close();
            
            
            // output to stream
            this.jsonMetrics.add(jsonString.getBuffer().toString());
            
        }
        catch (Exception ex) {
            log.error("Error adding metrics metrics to queue", ex);
        }
    }

    

}


package com.zjs.mic.metrics.stream;

import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;

public interface MetricsStreamClient {
    String OUTPUT = "metricsStreamOutput";

    @Output(OUTPUT)
    MessageChannel metricsStreamOutput();
}


package com.zjs.mic.metrics.stream;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MetricsStreamAutoConfiguration.class)
@EnableConfigurationProperties({MetricsStreamProperties.class})
public @interface EnableMetricsStream {

}

    已经将上面的代码包装成注解打好包 在入口类加@EnableMetricsStream 注解就能生效

    剩下的就是我们去mq接收信息传递到响应数据库中进行处理就行了

  从而我们在“spring boot +RabbitMQ +InfluxDB+Grafara监控实践” 这篇文章中的图就变成下面这样了

 

    好实践部分就到这里

  总结思考

    监控信息hystrix和metrics到底是拉取好还是主动推送好!一下简单分析:

    拉取,对于被监控的应用来说值引用少量的包节省了推送信息的线程,基本没有什么开发量,对于一些严格权限控制的springboot应用,就需要额外开接口或者拉取进行权限验证很不方便

    推送,应用主动推送应用相关的包和注解占用对应的线程资源,应用可以进行严格的权限控制不用对接口做例外不需要扫描程序开发。

  我的结论是两者并存,不知道大家有没有什么其他想法可以说来听听!

  

 

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
2月前
|
开发框架 前端开发 网络协议
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
589 1
|
2月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
60 4
|
11天前
|
XML JSON Java
Spring Boot 开发中常见的错误
本文总结了 Java 开发中常见的几个问题及其改进方法,包括:1. 过度使用 `@Component` 注解;2. `@ResponseBody` 注解的错误用法;3. `@Autowired` 的不当使用;4. `application.properties` 管理不善;5. 异常处理不当。每部分详细解释了错误情况和建议的改进方案,并提供了相应的代码示例。
41 11
|
4天前
|
人工智能 前端开发 Java
Spring AI Alibaba + 通义千问,开发AI应用如此简单!!!
本文介绍了如何使用Spring AI Alibaba开发一个简单的AI对话应用。通过引入`spring-ai-alibaba-starter`依赖和配置API密钥,结合Spring Boot项目,只需几行代码即可实现与AI模型的交互。具体步骤包括创建Spring Boot项目、编写Controller处理对话请求以及前端页面展示对话内容。此外,文章还介绍了如何通过添加对话记忆功能,使AI能够理解上下文并进行连贯对话。最后,总结了Spring AI为Java开发者带来的便利,简化了AI应用的开发流程。
134 0
|
11天前
|
IDE Java 测试技术
互联网应用主流框架整合之Spring Boot开发
通过本文的介绍,我们详细探讨了Spring Boot开发的核心概念和实践方法,包括项目结构、数据访问层、服务层、控制层、配置管理、单元测试以及部署与运行。Spring Boot通过简化配置和强大的生态系统,使得互联网应用的开发更加高效和可靠。希望本文能够帮助开发者快速掌握Spring Boot,并在实际项目中灵活应用。
28 5
|
9天前
|
前端开发 Java 开发者
这款免费 IDEA 插件让你开发 Spring 程序更简单
Feign-Helper 是一款支持 Spring 框架的 IDEA 免费插件,提供 URL 快速搜索、Spring Web Controller 路径一键复制及 Feign 与 Controller 接口互相导航等功能,极大提升了开发效率。
|
29天前
|
前端开发 JavaScript Java
如何使用 Spring Boot 和 Angular 开发全栈应用程序:全面指南
如何使用 Spring Boot 和 Angular 开发全栈应用程序:全面指南
39 1
|
16天前
|
XML Java 数据格式
Spring Boot 开发中的常见失误
本文深入分析了Spring Boot开发中常见的失误,包括不当使用@Component、@ResponseBody、@Autowired注解,以及不良的异常处理和日志记录实践,提供了有效的规避策略,帮助开发者提升代码质量和系统性能。
|
1月前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
45 2
|
2月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
81 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块