springboot2项目集成prometheus说明

springboot2项目集成prometheus说明

1、pom文件引入相关依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.prometheus</groupId>
    <artifactId>simpleclient</artifactId>
    <version>0.6.0</version>
</dependency>
<dependency>
    <groupId>io.prometheus</groupId>
    <artifactId>simpleclient_common</artifactId>
    <version>0.6.0</version>
</dependency>
<dependency>
    <groupId>io.prometheus</groupId>
    <artifactId>simpleclient_hotspot</artifactId>
    <version>0.6.0</version>
</dependency>
2、application.properties添加暴露采集点信息
management.metrics.distribution.percentiles-histogram.http=true
management.endpoints.web.base-path=/actuator
management.endpoints.web.exposure.include=prometheus,metrics
management.metrics.export.prometheus.enabled=true
management.metrics.export.prometheus.descriptions=true
management.metrics.export.prometheus.step=1m
management.metrics.web.server.auto-time-requests=true
3、创建Configuration类来注入需要metrics对象
    @Configuration
    public class PrometheusBeanConfig {
        /**
         * http请求总数量Counter
         * @return Counter对象
         */
        @Bean
        public io.prometheus.client.Counter appHttpRequestTotalCountCollector(){
            return io.prometheus.client.Counter.build()
                    .name("app_request_total")
                    .labelNames("appId","businessType","method","returnCode")
                    .help("init app_request_total").create()
                    .register();
        }
        @Bean
        public io.prometheus.client.Histogram requestsLatencySeconds(){
            return io.prometheus.client.Histogram.build()
                    .name("requests_latency_seconds")
                    .labelNames("appId","businessType","method")
                    .help("init requests_latency_seconds")
                    .create()
                    .register();
        }
    
        /**
         * 密码资产接口指标
         * @return Gauge对象
         */
        @Bean
        public io.prometheus.client.Gauge cipherAssetsInventoryGaugeCollector(){
            return io.prometheus.client.Gauge.build()
                    .name("cipher_assets_inventory")
                    .labelNames("type")
                    .help("init cipher_assets_inventory")
                    .create()
                    .register();
        }
    
    }
4、创建拦截器或者aop进行metric埋点,根据具体需要自行选择埋点方式(埋点部分)
(1)、使用aop统一拦截处理埋点
   @Autowired
   private Gauge gauge;

   @Autowired
   private Counter counter;

   @Autowired
   private Histogram histogram;

   private Histogram.Timer histogramRequestTimer;
   @Before("execution(public * com.infosec.cssp.nettsa.controller.NetTsaController.*(..))")
   public void doBefore(JoinPoint joinPoint) {
       Object args[] = joinPoint.getArgs();
       JSONObject requestJson = (JSONObject) args[1];
       HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
       String appId = request.getHeader("appId");
       String method = requestJson.getJSONObject(FieldNameConstant.FIELD_MSG_HEADER).getString(FieldNameConstant.FIELD_BUSINESS_TYPE);
       // http响应时间埋点
       histogramRequestTimer = histogram.labels(appId, BUSSINESS_TYPE, method).startTimer();

   }
   @AfterReturning(pointcut = "execution(public * com.infosec.cssp.nettsa.controller.NetTsaController.*(..))",returning = "result")
   public void doAfterReturning(JoinPoint joinPoint, Object result){
       Object args[] = joinPoint.getArgs();
       String requestParam = args[1].toString();
       JSONObject requestJson = JSONObject.parseObject(requestParam);
       JSONObject responseJson = (JSONObject) JSON.toJSON(result);
       HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
       String appId = request.getHeader("appId");
       rmp.setAppId(appId);
       //TODO appName需要从接口中取
       rmp.setAppName(AppType.getName(appId));
       String returnCode = responseJson.getJSONObject(Constants.HEAD).getString(Constants.CODE);
       String method = requestJson.getJSONObject(FieldNameConstant.FIELD_MSG_HEADER)
               .getString(FieldNameConstant.FIELD_BUSINESS_TYPE);
       String success = "0";
       String error = "-1";
       if (success.contentEquals(returnCode) && method != null && CommonConstant.GENERATE_TIME_STAMP.equals(method)) {
           gauge.labels(BUSSINESS_TYPE).inc();
       }

       // http请求量counter埋点
       counter.labels(appId, BUSSINESS_TYPE, method, success.equals(returnCode) ? returnCode : error).inc();
       histogramRequestTimer.observeDuration();
   }

注:这种方法经过压测时发现会导致得到的平均响应时间出现问题,因为并发导致,所以改为Around方式,且需要调用Histogram的time方法(解决多线程问题),比如

 //写histogram类型指标信息
 String finalMethodName = methodName;
 Object result = histogram.labels(appId, CommonConstant.BUSINESS_TYPE, methodName).time(new Callable<Object>() {
     @Override
     public Object call() {
         try {
             //注意,如果调用joinPoint.proceed()方法,则修改的参数值不会生效,必须调用joinPoint.proceed(Object[] args),
             // 此时才真的去调业务方法,调用顺序为:around->before->method->after
             return joinPoint.proceed(args);
         } catch (Throwable throwable) {
             logger.error("调用方法{}出错,{}", finalMethodName, throwable);
             return new ResultDto(errorCode, "调用方法" + finalMethodName + "出错:" + throwable.getMessage(), null);
         }
     }
 });
(2)、采用自定义注解方式实现埋点。可以对特定的方法进行埋点处理
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface PrometheusAopLog {
}
@Aspect
@Component
public class PrometheusLogAspect {
    private static Logger logger = LoggerFactory.getLogger(PrometheusLogAspect.class);
    @Autowired
    private Counter counter;
    @Autowired
    private Histogram histogram;
    /**
     * 只拦截特定的注解
     */
    @Pointcut("@annotation(com.xxx.yyy.annotation.PrometheusAopLog)")
    public void pointCut() {
    }

    @Around(value = "pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        ResultDto dto = new ResultDto();
        String errorCode = "-1";
        String errorMsg = "unknow error";
        String methodName = null;
        try {
            //获取方法参数值数组
            Object[] args = joinPoint.getArgs();
            methodName = joinPoint.getSignature().getName();
            if (!StringUtils.isEmpty(RequestContextHolder.getRequestAttributes())) {
                String finalMethodName = methodName;
                //写histogram类型指标信息,用于统计接口响应耗时
                Object result = histogram.labels(PrometheusConstant.SERVER_TYPE, methodName).time(() -> {
                    try {
                        return joinPoint.proceed(args);
                    } catch (Throwable throwable) {
                        logger.error("调用方法{}出错,{}", finalMethodName, throwable);
                        dto.setResultDto("-1", "调用方法【" + finalMethodName + "】出错", null);
                        return dto;
                    }
                });
                ResponseCraMa tmp = (ResponseCraMa) result;
                errorCode = tmp.getResponse().getHeader().getErrnum();
                //写counter指标
                counter.labels(PrometheusConstant.SERVER_TYPE, methodName, "0".equals(errorCode) ? "0" : "-1").inc();
                //返回方法最终处理结果
                return result;
            }
        } catch (Exception e) {
            logger.error("aop around error:{}", e);
            errorMsg = "aop调用方法【" + methodName + "】出错";
        }
        dto.setResultDto(errorCode, errorMsg, null);
        return dto;
    }
}
使用时,直接在Controller层的方法上使用@PrometheusAopLog 注解即可。
5、自定义采集请求url,供prometheus服务调用(采集部分)
@RestController
@CrossOrigin
@RequestMapping("monitor")
public class MonitorController {
     @RequestMapping(value = "/prometheus",produces = {"application/json;charset=UTF-8"})
    public void exportMetrics(HttpServletResponse response) {
        StringWriter writer = new StringWriter();
        try {
            TextFormat.write004(writer, CollectorRegistry.defaultRegistry.metricFamilySamples());
        } catch (IOException var3) {
            throw new RuntimeException(var3);
        }
        String response2 = writer.toString();
        try (OutputStream os = response.getOutputStream()) {
            os.write(response2.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
6、验证是否埋点成功
  • 启动项目
  • 确定prometheus服务成功连接到项目
    可以到prometheus管理界面的Status菜单的Targets下看当期项目的状态是UP还是DOWN
    如果是DOWN,则说明项目提供的请求url不正确。
  • 访问项目某个接口
  • 到prometheus提供的操作界面中访问埋点数据
    比如埋点指标为:app_request_total,在prometheus管理界面的Graph菜单下输入该指标后点击Execute,
    查看返回数据.(注意:不同指标,prometheus会生成不同指标名称)
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值