使用Prometheus对微服务性能自定义指标监控
背景
随着云计算和容器化技术的不断发展,微服务架构逐渐成为现代软件开发的主流趋势。微服务架构将大型应用程序拆分成多个小型、独立的服务,每个服务都可以独立开发、部署和扩展。这种架构模式提高了系统的可伸缩性、灵活性和可靠性,但同时也带来了服务监控和管理的挑战。
在微服务架构中,服务之间的依赖关系变得复杂,服务数量众多,因此需要一种有效的监控和管理工具来确保系统的稳定性和可靠性。监控工具可以帮助开发人员实时了解服务的运行状态、性能指标和异常情况,从而及时发现问题并进行处理。同时,管理工具还可以提供自动化的部署、配置和扩展功能,提高开发效率和运维质量。
Prometheus的优势
-
请求、数据库查询、消息队列等应用指标。
-
高效数据存储:Prometheus采用时间序列数据库(TSDB)来存储监控数据,具有高效的数据压缩和查询性能。
-
丰富查询语言:Prometheus提供了强大的数据查询语言PromQL,可以方便地对监控数据进行过滤、聚合和计算。
-
灵活告警机制:Prometheus支持基于规则的告警机制,可以根据监控数据的阈值触发告警通知,支持多种告警方式,如邮件、短信、Slack等。
springBoot集成Prometheus
导入Pom依赖
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId><version>2.2.1.RELEASE</version>
</dependency>
<dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId><version>1.3.1</version>
</dependency>
修改springBoot配置文件
开启prometheus监控配置
management:endpoint:prometheus:enabled: trueendpoints:web:exposure:include: 'prometheus'
修改默认的Prometheus监控度量名称
prometheus默认指标中有个http.server.requests的度量名称,记录了http请求调用情况;现在以这个为例,修改名称
新建一个@Configuration 类 PrometheusConfig
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.NamingConvention;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;@Configuration
public class PrometheusConfig {/** 用于替换 Prometheus中 公共的 http.server.requests度量名替换* @return*/@BeanMeterRegistryCustomizer<MeterRegistry> metricsConfig() {return registry -> registry.config().namingConvention(new NamingConvention() {@Overridepublic String name(String name, Meter.Type type, String baseUnit) {String collect = "";if(name.contains("http.server.requests")){collect = Arrays.stream(name.replaceAll("http.server.requests", "jiang.xiao.yu.http").split("\\.")).filter(Objects::nonNull).collect(Collectors.joining("_"));}else {collect = Arrays.stream(name.split("\\.")).filter(Objects::nonNull).collect(Collectors.joining("_"));}return collect;}});}
}
自定义Prometheus监控指标
使用拦截器监控指标
利用拦截器实现所有HTTP接口的监控
利用HTTP的拦截器添加Prometheus的监控指标,首先创建一个拦截器CustomInterceptor 实现HandlerInterceptor接口,然后重写里面的 前置处理、后置处理;
import io.micrometer.core.instrument.*;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.ToDoubleFunction;public class CustomInterceptor implements HandlerInterceptor {private static final String CUSTOM_KPI_NAME_TIMER = "custom.kpi.timer"; //耗时private static final String CUSTOM_KPI_NAME_COUNTER = "custom.kpi.counter"; //api调用次数。private static final String CUSTOM_KPI_NAME_SUMMARY = "custom.kpi.summary"; //汇总率private static MeterRegistry registry;private long startTime;private GaugeNumber gaugeNumber = new GaugeNumber();void getRegistry(){if(registry == null){//这里使用的时SpringUtil获取Bean,没有用@Autowired注解,Autowired会因为加载时机问题导致拿不到;SpringUtil.getBean网上实现有很多,可以自行搜索;registry = SpringUtil.getBean(MeterRegistry.class);}}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {getRegistry();//记录接口开始调用的时间startTime = System.currentTimeMillis();return HandlerInterceptor.super.preHandle(request, response, handler);}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//统计调用次数registry.counter(CUSTOM_KPI_NAME_COUNTER,"uri", request.getRequestURI(), "method", request.getMethod(),"status", response.getStatus() + "", "exception", ex == null ? "" : ex.getMessage(), "outcome", response.getStatus() == 200 ? "SUCCESS" : "CLIENT_ERROR").increment();//统计单次耗时registry.timer(CUSTOM_KPI_NAME_TIMER,"uri", request.getRequestURI(), "method", request.getMethod(),"status", response.getStatus() + "", "exception", ex == null ? "" : ex.getMessage(), "outcome", response.getStatus() == 200 ? "SUCCESS" : "CLIENT_ERROR").record(System.currentTimeMillis() - startTime, TimeUnit.MILLISECONDS);//统计调用成功率,根据过滤Counter对象,获取计数Collection<Meter> meters = registry.get(CUSTOM_KPI_NAME_COUNTER).tag("uri", request.getRequestURI()).tag("method", request.getMethod()).meters();double total = 0;double success = 0;for (Meter meter : meters) {Counter counter = (Counter) meter;total += counter.count();String status = meter.getId().getTag("status");if (status.equals("200")){success+= counter.count();}}//保存对应的成功率到Map中String key = request.getMethod() + request.getRequestURI();gaugeNumber.setPercent(key, Double.valueOf(success / total * 100L));registry.gauge(CUSTOM_KPI_NAME_SUMMARY, Tags.of("uri", request.getRequestURI(), "method", request.getMethod()), gaugeNumber, new ToDoubleFunction<GaugeNumber>() {@Overridepublic double applyAsDouble(GaugeNumber value) {return value.getPercent(key);}});HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}// gauge监控某个对象,所以用内部类替代,然后根据tag标签区分对应的成功率;key 为 method + uriclass GaugeNumber {Map<String,Double> map = new HashMap<>();public Double getPercent(String key) {return map.get(key);}public void setPercent(String key, Double percent) {map.put(key, percent);}}
}
注册自定义拦截器给Spring
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class CustomInterceptors implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/**");}
}
大功告成,启动程序测试吧
使用AOP记录监控指标
自定义指标注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodMetrics {String name() default "";String desc() default "";String[] tags() default {};//是否记录时间间隔boolean withoutDuration() default false;
}
切面实现
@Aspect
public class PrometheusAnnotationAspect {@Autowiredprivate MeterRegistry meterRegistry;@Pointcut("@annotation(com.smac.prometheus.annotation.MethodMetrics)")public void pointcut() {}@Around(value = "pointcut()")public Object process(ProceedingJoinPoint joinPoint) throws Throwable {Method targetMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();Method currentMethod = ClassUtils.getUserClass(joinPoint.getTarget().getClass()).getDeclaredMethod(targetMethod.getName(), targetMethod.getParameterTypes());if (currentMethod.isAnnotationPresent(MethodMetrics.class)) {MethodMetrics methodMetrics = currentMethod.getAnnotation(MethodMetrics.class);return processMetric(joinPoint, currentMethod, methodMetrics);} else {return joinPoint.proceed();}}private Object processMetric(ProceedingJoinPoint joinPoint, Method currentMethod, MethodMetrics methodMetrics) {String name = methodMetrics.name();if (!StringUtils.hasText(name)) {name = currentMethod.getName();}String desc = methodMetrics.desc();if (!StringUtils.hasText(desc)) {desc = currentMethod.getName();}//不需要记录时间if (methodMetrics.withoutDuration()) {Counter counter = Counter.builder(name).tags(methodMetrics.tags()).description(desc).register(meterRegistry);try {return joinPoint.proceed();} catch (Throwable e) {throw new IllegalStateException(e);} finally {counter.increment();}}//需要记录时间(默认)Timer timer = Timer.builder(name).tags(methodMetrics.tags()).description(desc).register(meterRegistry);return timer.record(() -> {try {return joinPoint.proceed();} catch (Throwable e) {throw new IllegalStateException(e);}});}
}
在需要记监控的地方加上这个注解
@MethodMetrics(name="sms_send",tags = {"vendor"})
public void send(String mobile, SendMessage message) throws Exception {//do something
}
相关文章:
使用Prometheus对微服务性能自定义指标监控
背景 随着云计算和容器化技术的不断发展,微服务架构逐渐成为现代软件开发的主流趋势。微服务架构将大型应用程序拆分成多个小型、独立的服务,每个服务都可以独立开发、部署和扩展。这种架构模式提高了系统的可伸缩性、灵活性和可靠性,但同时…...
深入解析 Lombok 的实现原理:以 @Builder 为例的实战演示(三)
文章目录 Lombok 的实现原理概述以 Builder 为例:解析 Lombok 如何生成 Builder 模式示例代码:没有 Lombok 的 Builder 模式使用 Lombok 的 Builder 简化代码 Lombok 如何实现 Builder:源码解析案例演示:自定义构造逻辑Lombok 的代…...
SEO基础:什么是SERP?【百度SEO专家】
SEO基础:什么是SERP? 大家好,我是林汉文(百度SEO专家),在进行SEO(搜索引擎优化)时,理解SERP是一个非常重要的基础概念。那么,究竟什么是SERP呢?本…...
HTML5教程(一)- 网页与开发工具
1. 什么是网页 网页 基于浏览器阅读的应用程序,是数据(文本、图像、视频、声音、链接等)展示的载体常见的是以 .html 或 .htm 结尾的文件 网站 使用 HTML 等制作的用于展示特定内容相关的网页集合。 2. 网页的组成 浏览器 代替用户向服务…...
Java进阶篇设计模式之二 ----- 工厂模式
前言 在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法。本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式、工厂方法和抽象工厂模式。 简单工厂模式 简单工厂模式是属于创建型模式,又叫做静态工厂方法模式。…...
考研篇——数据结构王道3.2.2_队列的顺序实现
目录 1.实现方式说明2.代码实现2.12.1.1 代码12.1.2 代码22.1.3 代码3 2.22.2.1 代码42.2.5 代码52.2.6 代码6 总结 1.实现方式说明 多在选择题中考察 队尾指针(rear)有两种指向方式: 队尾指针指向队尾元素的位置,队尾指针指向…...
从零开始理解 Trie 树:高效字符串存储与查找的利器【自动补全、拼写检查】
题目分析 这道题让我们实现一个 Trie 类(也称为前缀树),以便高效地插入和查询字符串。前缀树是一种特殊的树形数据结构,适用于快速存储和检索字符串数据集中的键,比如实现 自动补全 和 拼写检查。 题目要求 Trie 类…...
关于sse、websocket与流式渲染
一、SSE是什么? 网络中的 SSE (Server-Sent Events) 是一种服务器向浏览器单向推送数据的机制,常用于需要实时更新的数据传输,如新闻推送、股票行情、聊天应用等。 SSE 的特点: 单向通信:服务器向客户端推送数据&…...
Python 语法与数据类型详解
Python 语法与数据类型详解 Python 以其简洁易读的语法和丰富多样的数据类型在编程领域占据重要地位。深入理解 Python 的语法和数据类型是掌握这门语言的关键。 一、Python 语法概述 (一)缩进规则 Python 独特的缩进规则是其语法的重要特征之一。与…...
LeetCode题练习与总结:扁平化嵌套列表迭代器--341
一、题目描述 给你一个嵌套的整数列表 nestedList 。每个元素要么是一个整数,要么是一个列表;该列表的元素也可能是整数或者是其他列表。请你实现一个迭代器将其扁平化,使之能够遍历这个列表中的所有整数。 实现扁平迭代器类 NestedIterato…...
51单片机快速入门之 AD(模数) DA(数模) 转换 2024/10/25
51单片机快速入门之 AD(模数) DA(数模) 转换 2024/10/25 声明:本文图片来源于网络 A模拟信号特点: 电压或者电流 缓慢上升 随着时间连续缓慢上升或下降 D数字信号特点:电压或者电流 保持一段时间的高/低电平 状态 / 突变 (高电压瞬间低电压) 数字电路中 通常将0-1v电压称…...
Typora 、 Minio and PicGo 图床搭建
流程介绍 本地安装Typora笔记工具拥有一台装有docker的服务器配置minio云图床管理控制页面下载PicGo上传工具服务器Docker环境搭建—Ubuntu系统 删除旧docker的所有依赖(非root用户) # 删除docker及安装时自动安装的所有包 sudo apt-get autoremove docker docker-ce docker…...
【计网】UDP Echo Server与Client实战:从零开始构建简单通信回显程序
目录 前言: 1.实现udpserver类 1.1.创建udp socket 套接字 --- 必须要做的 socket()讲解 代码实现:编辑 代码讲解: 1.2.填充sockaddr_in结构 代码实现: 代码解析: 1.3.bind sockfd和…...
微服务网关Zuul
一、Zuul简介 Zuul是Netflix开源的微服务网关,包含对请求的路由和过滤两个主要功能。 1)路由功能:负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。 2)过滤功能:负责对请求的过程…...
BuildCTF线上赛WP
Build::CTF flag不到啊战队--WP 萌新战队,还请多多指教~ 目录 Build::CTF flag不到啊战队--WP Web ez!http find-the-id Pwn 我要成为沙威玛传奇 Misc what is this? 一念愚即般若绝,一念智即般若生 别真给我开盒了哥 四妹,你听…...
《使用Gin框架构建分布式应用》阅读笔记:p143-p207
《用Gin框架构建分布式应用》学习第10天,p143-p207总结,总计65页。 一、技术总结 1.auth0 本人实际工作中未遇到过,mark一下,参考:https://auth0.com/。 2.使用template (1)c.File() (2)router.Static() (3)rou…...
华为网络管理配置实例
目录 组网需求 数据规划 配置思路 操作步骤 结果验证 配置脚本 管理员可以通过eSight网管系统对FW进行监控和管理,接收FW的告警。 组网需求 如图1所示,某企业在网络边界处部署了FW作为安全网关,并部署了eSight网管系统对网络设备进行集中…...
大语言模型数据处理方法(基于llama模型)
文章目录 前言一、基于huggingface的DataCollatorForSeq2Seq方法解读1、DataCollatorForSeq2Seq方法2、batch最长序列填充3、指定长度填充二、构建大语言模型数据加工模块1、数据读取2、数据加工1、数据格式2、预训练(pretrain)数据加工3、微调(sft)数据加工①、sft数据加工…...
爱奇艺大数据多 AZ 统一调度架构
01# 导语 爱奇艺大数据技术广泛应用于运营决策、用户增长、广告分发、视频推荐、搜索、会员营销等场景,为公司的业务增长和用户体验提供了重要的数据驱动引擎。 多年来,随着公司业务的发展,爱奇艺大数据平台已积累了海量数据,这…...
【C++篇】栈的层叠与队列的流动:在 STL 的韵律中探寻数据结构的优雅之舞
文章目录 C 栈与队列详解:基础与进阶应用前言第一章:栈的介绍与使用1.1 栈的介绍1.2 栈的使用1.2.1 最小栈1.2.2 示例与输出 1.3 栈的模拟实现 第二章:队列的介绍与使用2.1 队列的介绍2.2 队列的使用2.2.1 示例与输出 2.3 队列的模拟实现2.3.…...
使用 Flask 实现简单的登录注册功能
目录 1. 引言 2. 环境准备 3. 数据库设置 4. Flask 应用基本配置 5. 实现用户注册 6. 实现用户登录 7. 路由配置 8. 创建前端页面 9. 结论 1. 引言 在这篇文章中,我们将使用 Flask 框架创建一个简单的登录和注册系统。Flask 是一个轻量级的 Python Web 框架…...
计算机毕业设计Python+大模型微博情感分析 微博舆情预测 微博爬虫 微博大数据 舆情分析系统 大数据毕业设计 NLP文本分类 机器学习 深度学习 AI
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 《Python大模型微博情感分析…...
CTF--Misc题型小结
(萌新笔记,多多关照,不足之处请及时提出。) 不定时更新~ 目录 密码学相关 文件类型判断 file命令 文件头类型 strings读取 隐写术 尺寸修改 文件头等缺失 EXIF隐写 thumbnail 隐写 文件分离&提取 binwalk foremo…...
深度学习系列——RNN/LSTM/GRU,seq2seq/attention机制
1、RNN/LSTM/GRU可参考: https://zhuanlan.zhihu.com/p/636756912 (1)对于这里面RNN的表示中,使用了输入x和h的拼接描述,其他公式中也是如此 (2)各符号图含义如下 2、关于RNN细节,…...
通过call指令来学习指令摘要表的细节
E8 cw cw 表示E8后面跟随2 字节 (什么数不知道) rel16 指在与指令同一代码段内的相对地址偏移 D ,指向Instruction Operand Encoding 表中的D列, 他告诉我们 操作数1 是一个0FFSET N.S. 在64位模式下,某些指令需要使用“地址覆盖前缀”(address over…...
10分钟使用Strapi(无头CMS)生成基于Node.js的API接口,告别繁琐开发,保姆级教程,持续更新中。
一、什么是Strapi? Strapi 是一个开源的无头(headless) CMS,开发者可以自由选择他们喜欢的开发工具和框架,内容编辑人员使用自有的应用程序来管理和分发他们的内容。得益于插件系统,Strapi 是一个灵活的 C…...
创建插件 DLL 项目
Step 1: 创建插件 DLL 项目 在 Visual Studio 中创建一个新的 DLL 项目,并添加以下文件和代码。 头文件:CShapeBase.h cpp 复制代码 #pragma once #include <afxwin.h> // MFC 必需头文件 #include <string> #include <vector> #i…...
OpenCV双目相机外参标定C++
基于OpenCV库实现双目测量系统外参标定过程。通过分析双目测量系统左右相机拍摄的棋盘格标定板图像,包括角点检测、立体标定、立体校正和畸变校正的步骤,获取左右相机的相对位置关系和姿态。 a.检测每张图像中的棋盘格角点,并进行亚像素级精…...
【GESP】C++一级练习BCQM3055,4位数间隔输出
一级知识点取余、整除运算和格式化输出知识点应用。其实也可以用string去处理,那就属于GESP三级的知识点范畴了,孩子暂未涉及。 题目题解详见:https://www.coderli.com/gesp-1-bcqm3055/ https://www.coderli.com/gesp-1-bcqm3055/https://w…...
纯血鸿蒙的最难时刻才开始
关注卢松松,会经常给你分享一些我的经验和观点。 纯血鸿蒙(HarmonyOS NEXT)也正式发布了,绝对是一个历史性时刻,但最难的鸿蒙第二个阶段,也就是生态圈的建设,才刚刚开始。 目前,我劝你现在不要升级到鸿蒙…...
长沙网络营销推广公司/广州网站制作实力乐云seo
首先, clone 一个远端仓库,到其目录下: $ Git clone git://example.com/myproject $ cd myproject 然后,看看你本地有什么分支: $ git branch * master 但是有些其他分支你在的仓库里面是隐藏的,你可以加上-…...
如何做淘宝客个人网站/东莞谷歌推广
CentOS 7下启动、关闭、重启、查看MySQL服务 xufengzhu 本文链接:https://blog.csdn.net/xufengzhu/article/details/81110982 1.启动命令 [rootxufeng Desktop]# service mysqld startRedirecting to /bin/systemctl start mysqld.service 2.关闭命令 […...
抚顺市营商环境建设局网站/怎么可以在百度发布信息
我有一个很难搞清楚其中的锁定文件了。 在Linux上,只需使用strace,但不要忘了跟随子进程与-f选项:strace -f -eopen git credential-store --file~/mystore store < credsopen("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) 3open(…...
济南网站建设铭盛信息/百度招聘官网首页
我的第一个socket 编程。 首先创建一个server 类, 然后在类中添加四个成员. 初始化。 TcpServer (listenPort) 记得在初始化servAddr 的时候要 先清零. 因为该成员的最后八位是0 。为了方便所以直接清零。 bzero(&servAddr, sizeof(servAddr)); 要记得加 &…...
建设基础化学网站的经验/今日军事新闻头条
选自GoogleAIblog 作者:Django Stars本文转自机器之心(nearhuman2014)原文链接:https://djangostars.com/blog/python-ide/前言写 Python 代码最好的方式莫过于使用集成开发环境(IDE)了。它们不仅能使你的…...
学生做任务赚钱的网站/新闻最新消息10条
大家好,我是小毛羊的村长,在今后的时间里跟大家一起聊一聊信用卡的各种玩法,教你如何从一个屌丝,通过玩转信用卡实现向高富帅的转变。真的不是村长吹牛逼,信用卡有大用处!快速跳过原始积累,走向人生巅峰&am…...