searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

接口调用监控数据统计demo的实现

2023-08-25 05:31:19
79
0

       在现代软件开发中,对于应用程序的监控和统计数据收集变得越来越重要。通过监控接口调用,我们可以获得有关应用程序性能、稳定性和用户行为的关键洞察。本文将描述笔者使用切面(AOP)和Redis实现的一个简易接口调用监控数据统计Demo。该Demo的主要功能是提供指定时间内(5min、10min)接口调用的成功率,以及一段时间内调用失败的接口暴露。

一、开发环境

       因为接口调用监控数据统计demo的实现,其业务相对简单,涉及到的技术栈为springboot+redis。本文采用的开发环境信息如下:

环境信息  版本
操作系统 Windows Server2016
JDK 1.8.0_192
Redis-server 3.2.100
IDE IntelliJ IDEA 2023.1.4

二、项目Maven依赖

       本文采用Maven进行版本管理,表中展示了项目的核心依赖信息:

核心依赖 版本
spring-boot-starter-parent 2.6.13
spring-boot-starter-data-redis 2.6.13

三、源代码目录结构

四、核心代码

(1)MonitorAspect

        该类主要提供监控调用接口的数据,统计调用次数并写入到Redis。

//统计调用次数,写入到Redis
String key_total = String.format(Constant.REDIS_KEY_API_COLLECT_TOTAL, methodName);
String key_success = String.format(Constant.REDIS_KEY_API_COLLECT_SUCCESS, methodName);
String key_fail = String.format(Constant.REDIS_KEY_API_COLLECT_FAIL, methodName);
Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet().rangeWithScores(key_total, 0, -1);
System.out.println("redis_key_total_hello: " + key_total);
//设置uuid作为调用动作的id,对应zset中的value
String uuidValue;
uuidValue = UUID.randomUUID().toString() + TimeUnit.MILLISECONDS;
redisTemplate.opsForZSet().add(key_total, uuidValue, System.currentTimeMillis());
// 调用目标方法,并获取响应数据
        Object result = join_point.proceed();
        if (result instanceof ResponseEntity) {
            ResponseEntity<?> responseEntity = (ResponseEntity<?>) result;
            // 获取HTTP状态码
            int statusCode = responseEntity.getStatusCodeValue();
            //统计调用成功次数,写入到Redis
            if(statusCode==200){
                uuidValue = UUID.randomUUID().toString() + TimeUnit.MILLISECONDS;
                redisTemplate.opsForZSet().add(key_success, uuidValue, System.currentTimeMillis());
//                redisTemplate.expire(key_success, expiryMillis, TimeUnit.MILLISECONDS);
                System.out.println(methodName + ":本次调用成功");
            }else{
                //调用失败set key_fail
                uuidValue = UUID.randomUUID().toString() + TimeUnit.MILLISECONDS;
                redisTemplate.opsForZSet().add(key_fail, uuidValue, System.currentTimeMillis());
//                redisTemplate.expire(key_success, expiryMillis, TimeUnit.MILLISECONDS);
                System.out.println(methodName + ":本次调用失败");
            }
        }

(2)MonitorService

         该类提供了对Redis中接口调用数据的统计和处理。

public MethodCollectDetail querySuccessRateByApiName(String apiName){
    String key_total = String.format(Constant.REDIS_KEY_API_COLLECT_TOTAL, apiName);
    System.out.println(key_total);
    String key_success = String.format(Constant.REDIS_KEY_API_COLLECT_SUCCESS, apiName);
    long total_count = 0;
    long success_count = 0;
    Long count = 0L;
    long now_time = System.currentTimeMillis();
    long one_minute_ago = (now_time - 60000L);
    System.out.println("redis_key_total_monitor: " + key_total);
    //从Redis中读取近1分钟调用的总次数
    count = redisTemplate.opsForZSet().count(key_total, one_minute_ago, now_time);
    total_count = (count != null) ? count : 0;

    //从Redis中读取近1分钟调用成功的次数
    count = redisTemplate.opsForZSet().count(key_success, one_minute_ago, now_time);
    success_count = (count != null) ? count : 0;
    System.out.println("total_count:" + total_count);
    System.out.println("success_count:" + success_count);
    //计算得到调用成功率
    double success_rate = (total_count != 0) ? ((double)success_count / total_count) : 1;
    String success_rate_str = String.format("%.2f",success_rate * 100) + "%";
    MethodCollectDetail methodCollectDetail = new MethodCollectDetail(apiName, success_count, total_count, success_rate_str);
    //返回结果
    return methodCollectDetail;
}
    public List<MethodFailDetail> queryFailRecord(){
        List<MethodFailDetail> detailList = new ArrayList<>();
        String query_key = String.format(Constant.REDIS_KEY_API_COLLECT_FAIL, "*");
        System.out.println("query_key:" + query_key);
        //获取当前存在的调用接口失败记录
        Set<String> keys_fail = redisTemplate.keys(query_key);
        if(keys_fail.size() == 0){
            System.out.println("未查到");
            return null;
        }
        //设置要查询的时间(可为参数传入)暂定3分钟
        long endTime = System.currentTimeMillis();
        long startTime = endTime - 180000L;
        for (String key_fail : keys_fail) {
            System.out.println("处理……");
            String methodName = key_fail.substring(query_key.length() - 1);
            System.out.println(methodName);
            String key_success = key_fail.replace(":fail:", ":success:");
            Long fail_count = redisTemplate.opsForZSet().count(key_fail, startTime, endTime);
            Long success_count = redisTemplate.opsForZSet().count(key_success, startTime, endTime);
            fail_count = (fail_count != null) ? fail_count:0;
            success_count  = (success_count != null) ? success_count:0;
            MethodFailDetail methodFailDetail = new MethodFailDetail(methodName, success_count, fail_count);
            detailList.add(methodFailDetail);
        }
        return detailList;
    }

(3)CleanMonitorDataScheduled

         该类提供了定时清除Redis中过期数据的功能。

@Scheduled(cron = "0 */1 * * * ?")
public void run(){
    String clean_prefix = Constant.REDIS_KEY_API_COLLECT_PREFIX + "*";
    Set<String> keys = redisTemplate.keys(clean_prefix);
    //删除过期数据,目前定为超过2分钟(方便测试)
    long now_time = System.currentTimeMillis();
    long expire_time = now_time - 2*60*1000;
    log.info("开始清理超过2分钟的过期监控数据……");
    for(String key: keys){
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, expire_time);
    }
    log.info("清理完成!");
}

五、总结

      本文通过结合切面编程和Redis实现了一个简单的接口调用监控数据统计Demo。这种方法将监控逻辑与核心业务逻辑解耦,使我们能够更轻松地收集和分析有关应用程序性能的重要数据。当然,实际的监控系统会更加复杂和全面,本文的demo仅作为个人学习的记录。

0条评论
0 / 1000
l****n
4文章数
0粉丝数
l****n
4 文章 | 0 粉丝
原创

接口调用监控数据统计demo的实现

2023-08-25 05:31:19
79
0

       在现代软件开发中,对于应用程序的监控和统计数据收集变得越来越重要。通过监控接口调用,我们可以获得有关应用程序性能、稳定性和用户行为的关键洞察。本文将描述笔者使用切面(AOP)和Redis实现的一个简易接口调用监控数据统计Demo。该Demo的主要功能是提供指定时间内(5min、10min)接口调用的成功率,以及一段时间内调用失败的接口暴露。

一、开发环境

       因为接口调用监控数据统计demo的实现,其业务相对简单,涉及到的技术栈为springboot+redis。本文采用的开发环境信息如下:

环境信息  版本
操作系统 Windows Server2016
JDK 1.8.0_192
Redis-server 3.2.100
IDE IntelliJ IDEA 2023.1.4

二、项目Maven依赖

       本文采用Maven进行版本管理,表中展示了项目的核心依赖信息:

核心依赖 版本
spring-boot-starter-parent 2.6.13
spring-boot-starter-data-redis 2.6.13

三、源代码目录结构

四、核心代码

(1)MonitorAspect

        该类主要提供监控调用接口的数据,统计调用次数并写入到Redis。

//统计调用次数,写入到Redis
String key_total = String.format(Constant.REDIS_KEY_API_COLLECT_TOTAL, methodName);
String key_success = String.format(Constant.REDIS_KEY_API_COLLECT_SUCCESS, methodName);
String key_fail = String.format(Constant.REDIS_KEY_API_COLLECT_FAIL, methodName);
Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet().rangeWithScores(key_total, 0, -1);
System.out.println("redis_key_total_hello: " + key_total);
//设置uuid作为调用动作的id,对应zset中的value
String uuidValue;
uuidValue = UUID.randomUUID().toString() + TimeUnit.MILLISECONDS;
redisTemplate.opsForZSet().add(key_total, uuidValue, System.currentTimeMillis());
// 调用目标方法,并获取响应数据
        Object result = join_point.proceed();
        if (result instanceof ResponseEntity) {
            ResponseEntity<?> responseEntity = (ResponseEntity<?>) result;
            // 获取HTTP状态码
            int statusCode = responseEntity.getStatusCodeValue();
            //统计调用成功次数,写入到Redis
            if(statusCode==200){
                uuidValue = UUID.randomUUID().toString() + TimeUnit.MILLISECONDS;
                redisTemplate.opsForZSet().add(key_success, uuidValue, System.currentTimeMillis());
//                redisTemplate.expire(key_success, expiryMillis, TimeUnit.MILLISECONDS);
                System.out.println(methodName + ":本次调用成功");
            }else{
                //调用失败set key_fail
                uuidValue = UUID.randomUUID().toString() + TimeUnit.MILLISECONDS;
                redisTemplate.opsForZSet().add(key_fail, uuidValue, System.currentTimeMillis());
//                redisTemplate.expire(key_success, expiryMillis, TimeUnit.MILLISECONDS);
                System.out.println(methodName + ":本次调用失败");
            }
        }

(2)MonitorService

         该类提供了对Redis中接口调用数据的统计和处理。

public MethodCollectDetail querySuccessRateByApiName(String apiName){
    String key_total = String.format(Constant.REDIS_KEY_API_COLLECT_TOTAL, apiName);
    System.out.println(key_total);
    String key_success = String.format(Constant.REDIS_KEY_API_COLLECT_SUCCESS, apiName);
    long total_count = 0;
    long success_count = 0;
    Long count = 0L;
    long now_time = System.currentTimeMillis();
    long one_minute_ago = (now_time - 60000L);
    System.out.println("redis_key_total_monitor: " + key_total);
    //从Redis中读取近1分钟调用的总次数
    count = redisTemplate.opsForZSet().count(key_total, one_minute_ago, now_time);
    total_count = (count != null) ? count : 0;

    //从Redis中读取近1分钟调用成功的次数
    count = redisTemplate.opsForZSet().count(key_success, one_minute_ago, now_time);
    success_count = (count != null) ? count : 0;
    System.out.println("total_count:" + total_count);
    System.out.println("success_count:" + success_count);
    //计算得到调用成功率
    double success_rate = (total_count != 0) ? ((double)success_count / total_count) : 1;
    String success_rate_str = String.format("%.2f",success_rate * 100) + "%";
    MethodCollectDetail methodCollectDetail = new MethodCollectDetail(apiName, success_count, total_count, success_rate_str);
    //返回结果
    return methodCollectDetail;
}
    public List<MethodFailDetail> queryFailRecord(){
        List<MethodFailDetail> detailList = new ArrayList<>();
        String query_key = String.format(Constant.REDIS_KEY_API_COLLECT_FAIL, "*");
        System.out.println("query_key:" + query_key);
        //获取当前存在的调用接口失败记录
        Set<String> keys_fail = redisTemplate.keys(query_key);
        if(keys_fail.size() == 0){
            System.out.println("未查到");
            return null;
        }
        //设置要查询的时间(可为参数传入)暂定3分钟
        long endTime = System.currentTimeMillis();
        long startTime = endTime - 180000L;
        for (String key_fail : keys_fail) {
            System.out.println("处理……");
            String methodName = key_fail.substring(query_key.length() - 1);
            System.out.println(methodName);
            String key_success = key_fail.replace(":fail:", ":success:");
            Long fail_count = redisTemplate.opsForZSet().count(key_fail, startTime, endTime);
            Long success_count = redisTemplate.opsForZSet().count(key_success, startTime, endTime);
            fail_count = (fail_count != null) ? fail_count:0;
            success_count  = (success_count != null) ? success_count:0;
            MethodFailDetail methodFailDetail = new MethodFailDetail(methodName, success_count, fail_count);
            detailList.add(methodFailDetail);
        }
        return detailList;
    }

(3)CleanMonitorDataScheduled

         该类提供了定时清除Redis中过期数据的功能。

@Scheduled(cron = "0 */1 * * * ?")
public void run(){
    String clean_prefix = Constant.REDIS_KEY_API_COLLECT_PREFIX + "*";
    Set<String> keys = redisTemplate.keys(clean_prefix);
    //删除过期数据,目前定为超过2分钟(方便测试)
    long now_time = System.currentTimeMillis();
    long expire_time = now_time - 2*60*1000;
    log.info("开始清理超过2分钟的过期监控数据……");
    for(String key: keys){
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, expire_time);
    }
    log.info("清理完成!");
}

五、总结

      本文通过结合切面编程和Redis实现了一个简单的接口调用监控数据统计Demo。这种方法将监控逻辑与核心业务逻辑解耦,使我们能够更轻松地收集和分析有关应用程序性能的重要数据。当然,实际的监控系统会更加复杂和全面,本文的demo仅作为个人学习的记录。

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
1
1