简易版Dubbo方法级性能监控(TP90、TP99)

代码:https://gitee.com/szile20/lagou_stage3_module3

一、实现思路

  1. 创建API模块,提供consumer和provider公用的服务接口及服务方法。consumer模块和provider模块依赖该模块。
  2. 创建filter模块,创建性能监控Filter类,实现TP90和TP99的性能监控。通过dubbo的SPI功能暴露Filter。consumer模块依赖该模块。
    • 该模块功能实现是最难的且是最主要的。
    • 通过定时任务每5秒中计算一次TP90和TP99数据。这里使用SingleThreadScheduledExecutor实现定时任务。
    • 计算TP90和TP99需要存储过去一分钟每个方法调用的响应时间,并进行排序。这里使用ConcurrentHashMap<String, DelayQueue<SpentTime>>来存储每个方法每次响应时间。Map的key为方法名,Map的value为方法响应时间数据。SpentTime类实现了java.util.concurrent.Delayed接口,存放方法响应时间及过期时间。这里为什么使用DelayQueue?这是因为DelayQueue的数据有过期时间,可以移除过期数据。
  3. 创建provider模块,依赖API模块,实现服务方法。向zookeeper注册服务。
  4. 创建consumer模块,依赖API模块,从zookeeper中获取服务,调用服务方法。

二、代码实现

1. API模块

创建服务接口,提供服务方法。

public interface CustomServiceApi {String method1() throws InterruptedException;String method2() throws InterruptedException;String method3() throws InterruptedException;
}

2. filter模块

  1. 创建TPMonitorFilter类,实现org.apache.dubbo.rpc.Filter接口,显示TP90和TP99性能监控。

    定时任务的主要步骤:

    1. 获取方法响应时长数据队列:delayQueue
    2. 移除已经过期的数据
    3. 转为数组
    4. 排序
    5. 计算TP90和TP99
    @Activate(group = {CommonConstants.CONSUMER}) // 标明在consumer端启用
    public class TPMonitorFilter implements Filter {/*存放每个方法的响应时间数据*/private final Map<String, DelayQueue<SpentTime>> mappedMethodSpentTime = new ConcurrentHashMap<>();private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();{System.out.println("----开始执行TPMonitor----");// 启动定时任务scheduledExecutorService.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));for (Map.Entry<String, DelayQueue<SpentTime>> entry : mappedMethodSpentTime.entrySet()) {final DelayQueue<SpentTime> delayQueue = entry.getValue();// 移除已经过期的数据delayQueue.removeIf(new Predicate<SpentTime>() {@Overridepublic boolean test(SpentTime spentTime) {return spentTime.getDelay(TimeUnit.MILLISECONDS) <= 0;}});// 取出数据转为数组final SpentTime[] spentTimes = delayQueue.toArray(new SpentTime[]{});// 转为Listfinal List<SpentTime> spentTimeList = Arrays.asList(spentTimes);// 排序Collections.sort(spentTimeList, (o1, o2) -> (int) (o1.getTime() - o2.getTime()));final int size = spentTimeList.size();System.out.println(entry.getKey() + "() -- TP90:" + spentTimeList.get((int) Math.ceil(0.9d * size)).getTime() + "毫秒 -- TP99:" + spentTimeList.get((int) Math.ceil(0.99d * size)).getTime() + "毫秒, 当前有效个数:" + size);}}}, 30, 5, TimeUnit.SECONDS);  // 初始延迟30秒之后开始每5秒执行一次}@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {long startTime = System.currentTimeMillis();Result invoke = null;try {invoke = invoker.invoke(invocation);} catch (RpcException e) {e.printStackTrace();} finally {final long spentTime = System.currentTimeMillis() - startTime;final String serviceName = invocation.getServiceName();final String methodName = invocation.getMethodName();final String key = serviceName + "." + methodName;DelayQueue<SpentTime> delayQueue = mappedMethodSpentTime.get(key);if (delayQueue == null) {delayQueue = new DelayQueue<>();}// 记录方法响应时间,过期时间60秒delayQueue.put(new SpentTime(spentTime, 60 * 1000));mappedMethodSpentTime.put(key, delayQueue);}return invoke;}static class SpentTime implements Delayed {/* 响应时长 毫秒*/private final long time;/* 过期时间 */private final long expire;SpentTime(long time, long delay) {this.time = time;this.expire = System.currentTimeMillis() + delay;}public long getTime() {return time;}@Overridepublic long getDelay(TimeUnit unit) {return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);}@Overridepublic int compareTo(Delayed o) {final SpentTime o1 = (SpentTime) o;return (int) (this.getTime() - o1.getTime());}}
    }
    
  2. 使用dubbo的SPI暴露服务

    在resources下创建/META-INF/dubbo文件夹,然乎创建org.apache.dubbo.rpc.Filter文件,文件内容:

    tpFilter=com.szile.dubbo.filter.TPMonitorFilter
    

3. provider模块

  1. 依赖API模块,创建CustomServiceImpl类实现ServiceApi接口。

    package com.szile.dubbo.provider.service;import com.szile.dubbo.api.CustomServiceApi;
    import org.apache.dubbo.config.annotation.Service;import java.util.Random;@Service  // 这里的Service不要引错包了
    public class CustomServiceImpl implements CustomServiceApi {private Random random = new Random();@Overridepublic String method1() throws InterruptedException {Thread.sleep(random.nextInt(100));return "invoke method1()";}@Overridepublic String method2() throws InterruptedException {Thread.sleep(random.nextInt(100));return "invoke method2()";}@Overridepublic String method3() throws InterruptedException {Thread.sleep(random.nextInt(100));return "invoke method3()";}
    }
    
  2. 创建启动类,启动类中配置zookeeper,注册服务

    public class ProviderApp {public static void main(String[] args) throws IOException {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);context.start();System.in.read();}@Configuration@EnableDubbo(scanBasePackages = {"com.szile.dubbo.provider.service"})@PropertySource("classpath:/dubbo-provider.properties")static class ProviderConfiguration {@Beanpublic RegistryConfig registryConfig() {RegistryConfig registryConfig = new RegistryConfig();registryConfig.setAddress("zookeeper://127.0.0.1:2181");return registryConfig;}}
    }
    
  3. dubbo-provider.properties

    dubbo.application.name=dubbo-demo-provider
    dubbo.protocol.name=dubbo
    dubbo.protocol.port=20890
    #dubbo.registry.address=zookeeper://127.0.0.1:2181
    

4. consumer模块

  1. 依赖API模块和filter模块

  2. 创建RemoteServiceProxy类,调用远程服务方法

    @Component
    public class RemoteServiceProxy {@Reference  // 必须使用 org.apache.dubbo.config.annotation.Referenceprivate CustomServiceApi serviceApi;public void invokeMethod1() throws InterruptedException {serviceApi.method1();}public void invokeMethod2() throws InterruptedException {serviceApi.method2();}public void invokeMethod3() throws InterruptedException {serviceApi.method3();}
    }
    
  3. 启动类ConsumerApp

    启动类中使用了ThreadPoolExecutor线程池,循环的向线程池中提交调用远程服务方法的任务。这里执行三分钟停下。

    public class ConsumerApp {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfig.class);context.start();RemoteServiceProxy serviceProxy = context.getBean(RemoteServiceProxy.class);ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(100, 100, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50));long startTime = System.currentTimeMillis();int minutes = 3;int count = 0;while (true) {try {// 这里适当的休息一会,否则线程池不够用,会拒绝服务Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}try {threadPoolExecutor.execute(() -> {try {serviceProxy.invokeMethod1();} catch (InterruptedException e) {System.out.println(e.getLocalizedMessage());}});threadPoolExecutor.execute(() -> {try {serviceProxy.invokeMethod2();} catch (InterruptedException e) {System.out.println(e.getLocalizedMessage());}});threadPoolExecutor.execute(() -> {try {serviceProxy.invokeMethod3();} catch (InterruptedException e) {System.out.println(e.getLocalizedMessage());}});} catch (Exception e) {e.printStackTrace();}count++;// 执行三分钟结束if ((System.currentTimeMillis() - startTime) > minutes * 60 * 1000) break;}System.out.println("平均每分钟调用 - " + count / minutes + " - 次");}@Configuration@EnableDubbo@PropertySource(value = "classpath:/dubbo-consumer.properties")@ComponentScan(basePackages = {"com.szile.dubbo.consumer.service"})static class ConsumerConfig {}}
    

三、结果验证

验证步骤:

  1. 确保zookeeper服务启动
  2. 启动provider服务
  3. 启动consumer服务

consumer控制台输出信息

平均每分钟每个方法调用10851次,一分钟的TP90是90毫秒,TP99是99毫秒。

四、视频讲解

链接:https://pan.baidu.com/s/1sN_DWMv7p1SZ7BhL7SwbvQ
提取码:bfs0

简易版Dubbo方法级性能监控(TP90、TP99)相关推荐

  1. 简易版Dubbo方法级性能监控(实现TP90、TP99)

    一.具体要求 在真实业务场景中,经常需要对各个业务接口的响应性能进行监控(常用指标为:TP90.TP99) 通过扩展Dubbo的Filter(TPMonitorFilter),完成简易版本 Dubbo ...

  2. 性能调优之Java系统级性能监控及优化

    性能调优之Java系统级性能监控及优化 对于性能调优而言,通常我们需要经过以下三个步骤:1,性能监控:2,性能剖析:3,性能调优 性能调优:通过分析影响Application性能问题根源,进行优化Ap ...

  3. java -jar 未响应_Java 方法性能监控和统计工具 MyPerf4J

    一个针对高并发.低延迟应用设计的高性能 Java 性能监控和统计工具. 特性 高性能: 单线程支持每秒 1000 万次 响应时间的记录,每次记录只花费 73 纳秒 无侵入: 采用 JavaAgent ...

  4. Informix 11.5 SQL 语句性能监控方法及实现

    本文主要介绍 Informix 11.5 中 SQL 语句性能监控的基本方法及实现,希望能够使大家有一个比较全面的了解. 我们知道,在数据库应用系统中,SQL 语句的性能好坏至关重要.如果 SQL 语 ...

  5. 《深入理解Java虚拟机第3版》垃圾收集器与内存分配策略、虚拟机性能监控故障处理工具

    目录 往期博客:Java课堂篇3_初识JMM.常量池简单理解(字符串常量池.静态常量池.大整型常量池) 为什么要了解垃圾收集和内存分配? 如何判断对象已死? 引用计数算法 可达性分析算法 JDK1.2 ...

  6. [NewLife.XCode]实体列表缓存(最土的方法实现百万级性能)

    NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode. 整个系列教程会大量结合示例代码和 ...

  7. 磁盘 I/O 性能监控指标和调优方法

    在介绍磁盘 I/O 监控命令前,我们需要了解磁盘 I/O 性能监控的指标,以及每个指标的所揭示的磁盘某方面的性能.磁盘 I/O 性能监控的指标主要包括: 指标 1:每秒 I/O 数(IOPS 或 tp ...

  8. Duilib版视频监控客户端(简易版)

    Duilib版视频监控客户端(简易版) 基于目前物联网发展的形式,网络视频监控几乎无处不在,对监控的视频的查看和回放自然成了必不可少的部分,下面介绍一下使用Duilib来开发的视频监控客户端例子. 界 ...

  9. linux如何脚本监控tps,对Linux进行详细的性能监控的方法

    这是我们正在进行的Linux命令和性能监控系列的一部分.vmstat和iostat两个命令都适用于所有主要的类unix系统(Linux/unix/FreeBSD/Solaris). 如果vmstat和 ...

最新文章

  1. python 获取excel文件内sheet名称列表
  2. ubuntu15.10避免图形界面无法登录的jdk配置
  3. leetcode322. 零钱兑换
  4. SpringCloud Eureka自我保护机制
  5. 测试驱动开发 测试前移_我如何以及为什么认为测试驱动开发值得我花时间
  6. python不满足条件重新输入_如果Python中不满足条件,则拒绝用户输入
  7. 从最年轻的白手起家富豪到身陷囹圄,这个80后创始人也就用了3年
  8. 冷藏温度范围_机械式、干冰式、冷板式、液氮式等冷藏车制冷方式横向对比
  9. linux 下的包管理器 —— apt-get 与 dpkg
  10. java 抽象类 模板_Java学习day21-抽象类(abstract class)和模板设计模式(TemplateMethod)...
  11. 逻辑回归分析与回归分析_逻辑回归从零开始的情感分析
  12. Facebook登陆服务器校验,权限请求以及数据获取
  13. 从 Factor Zoo 到 Factor War,实证资产定价走向何方?
  14. 学习笔记整理:网络应用技术-实验-路由器交换机配置命令
  15. Unity 知识点 - 3D游戏 - 视角跟随和键盘移动
  16. eclipes和idea常用快捷键及缩写大全
  17. matlab multiply,MATLAB Matrix Multiply Code效率
  18. vmware不支持linux,vmware不支持opengl
  19. mysql excel 同步数据_mysql导入excel数据
  20. 25岁西装男子地铁痛哭:成年人的崩溃,都是从缺钱开始的

热门文章

  1. 阿飞之死(故事新编)
  2. Linux中proc浅析
  3. 2021年中国加湿器行业发展回顾及2022年行业发展趋势预测:需求规模增长,产品趋于高端化与智能化[图]
  4. 膜拜大佬!各路诸侯的知识点总结!
  5. Matlab学习过程(一)
  6. 重磅!腾源会与WasmEdge的双份开源好礼来了
  7. 享受高成长:国内数据中心IDC全分析(附19年机柜排名)
  8. java中i++和++i的区别
  9. C#实现文本关键词过滤
  10. Java全栈(二)JavaSE:11.IDEA详细介绍