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,实现后台向前端推送信息的功能。
348 1
|
2月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
48 4
|
18天前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
31 2
|
2月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
60 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
29天前
|
Java Spring 容器
SpringBoot读取配置文件的6种方式,包括:通过Environment、@PropertySource、@ConfigurationProperties、@Value读取配置信息
SpringBoot读取配置文件的6种方式,包括:通过Environment、@PropertySource、@ConfigurationProperties、@Value读取配置信息
57 3
|
2月前
|
XML Java 数据格式
提升效率!Spring Boot 开发中的常见失误轻松规避
本文深入探讨了在 Spring Boot 开发中常见的失误,包括不当使用注解、不良异常处理、低效日志记录等,提供了有效的规避策略,帮助开发者提升代码质量和系统性能,构建更健壮、高效的应用程序。
|
23天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
31 0
|
3月前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
2455 16
|
2月前
|
Java 数据库连接 Spring
【2021Spring编程实战笔记】Spring开发分享~(下)
【2021Spring编程实战笔记】Spring开发分享~(下)
30 1
|
2月前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
68 2