具体实施方式
图2是本发明实施例提供的用于监控方法执行时间的监控方法流程图。
如图2所示,该流程包括:
步骤201,在被监控方法初始化或者首次被调用时,将用于调用监控对象的调用接口加入到所述被监控方法中,其中,所述监控对象是预先生成的用于执行监控操作的对象。
步骤202,所述监控对象记录所述被监控方法被调用的时间作为开始时间,将所述开始时间和所述被监控方法的信息压入监控栈中。
步骤203,所述监控对象记录所述被监控方法执行完毕的时间作为结束时间,将压入所述监控栈的所述开始时间和所述被监控方法的信息弹出所述监控栈。
步骤204,根据所述结束时间和所述开始时间确定所述被监控方法的执行时间。
由图2可见,本发明实施例是在需要监控方法执行时间时,动态地将用于执行监控操作的监控对象的调用接口加入到被监控方法中,如果不需要监控方法的执行时间,例如,在方法正式上线时,则不会动态地将所述监控对象的调用接口加入到方法中,因此,能够避免对被监控方法本身造成污染,降低监控方法上线的出错概率。
本发明实施例所涉及的栈,是一种计算机系统中的数据结构,特点是先进入的元素后弹出,简称先入后出。例如,先调用的方法压入方法栈中(第一次压栈),然后执行该方法,当该方法中再调用其他方法时,将新调用的方法压到栈中(第二次压栈),然后执行新调用的方法,依此类推,当每次有新方法被调用时都将新方法压到栈顶(第N次压栈),直到栈顶的方法退出时,弹出栈顶元素(第一次出栈,对应于最后一次压栈的方法,即先进后出的顺序),继续执行新的栈顶元素,直至栈中的元素全部弹出为止。
可见,本发明实施例中,当需要监控方法执行链中一系列方法的执行时间时,由于这一系列方法中每个方法的执行开始时间进入和弹出监控栈的顺序,与该一系列方法的执行顺序是一致的,即监控栈的数据结构与方法执行栈的数据结构是一致的,因此,可以根据监控栈中各个开始时间弹出的顺序以及在被监控方法执行完毕时记录的结束时间,得到该一系列方法中每个被监控方法的执行时间信息,从而实现对方法执行链上的多个被监控方法依次进行监控,记录其各自的执行时间。
本发明实施例中,可以预先配置监控信息,根据预先配置的所述监控信息确定是否需要对方法进行监控,进而确定是否需要动态地将用于调用监控对象的调用接口加入到方法中。具体地:
预先配置监控信息,所述监控信息包括被监控的类的标识(ID)信息、以及该被监控的类中被监控方法的身份信息,例如,被监控的类在容器中的ID信息、被监控的类中被监控方法的名称信息等。根据预先配置的所述监控信息,确定出被监控的类和被监控的类中的被监控方法,在被监控的类初始化或者首次被调用时,通过为被监控的类中的被监控方法加入用于调用所述监控对象的调用接口生成所述被监控的类的代理类。可见,通过利用被监控的类的所述代理类来取代所述被监控的类,可以实现透明且动态地为被监控的类中的被监控方法加入监控对象。
可见,本发明实施例可以通过修改预先配置的监控信息灵活地实现对方法执行时间的监控,而且,不会对被监控方法本身的代码产生污染,比如,在需要监控某一方法的执行时间时,将该方法所属的类和该方法在该类中的身份信息写入到所述监控信息中,从而实现监控,而当方法正式上线时,一般不会配置所述监控信息,因此,也不会为正式上线的方法加入监控对象的调用接口。
至于在何时为被监控的类生成代理类,这依据应用场景的不同而不同,例如,在应用于Spring容器场景下时,在Spring容器初始化阶段为被监控的类生成代理类,具体地:
在Spring容器初始化时,拦截初始化请求,将所述初始化请求与预先配置的所述监控信息进行对比,如果当前初始化的类是被监控的类,则通过为被监控的类中的被监控方法加入用于调用所述监控对象的调用接口生成所述被监控的类的代理类,将所述代理类返回给所述Spring容器。
可见,在应用于Spring容器的场景下时,本发明实施例在Spring容器初始化时为被监控的类生成代理类,其中,在该代理类中,监控对象的调用接口被加入到被监控方法中,然后向Spring容器返回所述代理类,从而实现了动态且透明地为被监控方法加入了监控对象的调用接口,进而实现为被监控方法监控执行时间。
关于预先生成的监控对象所需要执行的具体监控动作,以及将监控对象的调用接口加入到被监控方法的哪些位置,本发明实施例提出:
用于执行监控操作的监控对象包括第一监控方法和第二监控方法,将第一监控方法的调用接口加入到被监控方法的入口处,将第二监控方法的调用接口加入到被监控方法的出口处;其中,所述第一监控方法记录当前时间作为开始时间,将所述开始时间和所述被监控方法的信息压入监控栈中,所述第二监控方法记录当前时间作为结束时间,将压入所述监控栈的所述开始时间和所述被监控方法的信息弹出所述监控栈。
为了在采用多线程方式运行被监控方法时,仍能监控到被监控方法在各个线程上的执行时间,本发明实施例还提出:
所述第一监控方法从当前线程的方法执行栈获取栈顶元素信息,并记录当前时间作为开始时间,将所述栈顶元素信息和所述开始时间存储在第一存储对象中,将所述第一存储对象压入到监控栈中,所述监控栈存储在只有当前线程能够操作的变量中;所述第二监控方法从所述只有当前线程能够操作的变量中取出所述监控栈,从所述监控栈中取出栈顶元素,并记录当前时间作为结束时间。其中,当前线程的方法在被调用时被压入当前线程的方法执行栈,在执行完毕时从所述当前线程的方法执行栈中弹出。
可见,由于每个线程的监控栈存储在只有该线程自身能够操作的变量中,因此,其他线程无法对该监控栈中的信息进行操作,进而保证了监控栈是线程安全的,避免了采用多线程方式执行被监控方法时的操作混乱,因此在采用多线程方式运行被监控方法时,仍能监控到被监控方法在各个线程上的执行时间。
当需要监控方法执行链中一系列方法的执行时间时,为了能够确定出单个方法自身的执行时间与调用该单个方法的上层方法的执行时间之间的比例关系,进而寻找出方法执行性能的瓶颈所在,便于优化系统性能,本发明实施例还提出:
所述第二监控方法在从所述监控栈中取出栈顶元素时,记录当前取出的栈顶元素与所述监控栈中新的栈顶元素之间的指向关系,将所述指向关系、所述结束时间和从所述监控栈中取出的栈顶元素中存储的信息加入到有序的存储容器中,根据所述有序的存储容器中存储的所述指向关系和被监控方法的执行时间信息,确定出被监控方法的执行时间占调用该被监控方法的上层被监控方法的执行时间的比例信息。其中,所述有序的存储容器包括但不限于队列。
具体地,所述第二监控方法在将所述指向关系、所述结束时间和从所述监控栈中取出的栈顶元素中存储的信息加入到有序的存储容器中以后,判断当前线程的方法执行栈是否为空,如果为空,从所述有序的存储容器的头部开始遍历打印执行时间信息,如果不为空,返回继续执行方法执行栈中的方法。
下面结合附图,分别对本发明实施例中监控对象如何对被监控方法的执行时间进行监控、以及在Spring容器场景下如何透明且动态地为被监控方法加入监控对象进行示例性说明,具体请参见图3和图4。
图3是本发明实施例提供的监控对象对被监控方法的执行时间进行监控的流程图。
如图3所示,其中的步骤301、步骤305和步骤312是被监控方法自身的正常流程,其他步骤是在被监控方法的入口处和出口处加入的监控对象所执行的流程,具体地:
步骤301,进入被监控方法,被监控方法被压入当前线程的方法栈。
步骤302,位于被监控方法入口处的第一监控方法的调用接口调用所述第一监控方法,所述第一监控方法获取当前线程的方法栈信息。
其中,第一监控方法的调用接口通常位于方法执行的第一句,通过执行该调用接口来调用第一监控方法。
步骤303,所述第一监控方法记录当前方法的具体信息和当前时间信息,将当前时间信息确定为执行开始时间。
其中,当前方法的具体信息可以包括但不限于方法名称信息、方法所属的类的名称等,可以将当前方法的具体信息和当前时间信息记录到一个对象中,例如记录到一个Java对象中。
步骤304,所述第一监控方法将记录的信息压入监控栈中。
本步骤中,优选地,可以将所述监控栈存储在只有当前线程能够操作的变量中,从而保证监控栈的线程安全性,避免其他线程对该监控栈进行操作。
步骤305,执行被监控方法本身的内容。
步骤306,位于被监控方法出口处的第二监控方法的调用接口调用所述第二监控方法,所述第二监控方法从所述监控栈取出栈顶元素。
从所述监控栈中取出的栈顶元素包括了在步骤303中记录的当前方法的具体信息和执行开始时间信息。
步骤307,所述第二监控方法记录当前时间作为结束时间。
其中,步骤306和步骤307的顺序可调。
步骤308,所述第二监控方法将弹出的监控栈栈顶元素指向监控栈当前的栈顶元素。
步骤309,所述第二监控方法将弹出的监控栈栈顶元素、记录的所述结束时间、以及监控栈栈顶元素之间的指向关系信息放入到独立的队列中。
步骤310,所述第二监控方法判断方法栈当前是否为空,如果为空,执行步骤311,否则,执行步骤312。
步骤311,从所述队列的头部开始遍历队列,打印被监控方法的执行时间信息。
其中,被监控方法的执行时间信息包括各个被监控方法自身的执行时间,所述第二监控方法还可以根据各个被监控方法之间的指向关系信息,确定被监控方法自身的执行时间占调用该被监控方法的上层被监控方法的执行时间的比例信息,因此,被监控方法的执行时间信息还可以包括所述比例信息。
步骤312,返回方法栈继续执行其他方法。
图4是本发明实施例提供的在Spring容器场景下透明且动态地为被监控方法加入监控对象的流程图。
其中,Spring是一个轻量级的Java开发框架。它是为了解决企业应用开发的复杂性而创建的。通常企业级应用都使用Spring容器来管理应用中的Java对象,即本文中所说的Bean。
如图4所示,该流程包括:
步骤401,为Spring容器配置监控信息,所述监控信息包括需要监控的类在Spring容器中的ID信息、以及需要监控的类中被监控方法的身份信息。
其中,所述身份信通常为被监控方法的方法名称。具体可以在Spring容器的配置文件中配置所述监控信息。
步骤402,Spring容器启动。
步骤403,拦截初始化请求。
本步骤中,可以使用Spring框架的API拦截到初始化Bean的请求。
步骤404,通过与预先配置的监控信息进行比较,判断当前初始化的类是否是需要监控的类,如果是,执行步骤405,否则,执行步骤406。
步骤405,动态生成需要初始化的Bean(拦截到的Bean)的代理类,将所述代理类返回给Spring容器。
本步骤中,根据预先配置的监控信息,确定出被监控的Bean中的被监控方法,通过为被监控的Bean中的被监控方法加入用于调用监控对象的接口来生成所述被监控的Bean的代理类,即,通过对被监控的Bean需要监控的方法加入监控埋点代码来生成被监控的Bean的代理类,将加入了监控对象调用接口的Bean(即所述代理类)返回给Spring容器。
其中,可以使用CGLIB框架提供的面向切面编程(AOP)技术动态生成需要初始化的Bean(拦截到的Bean)的代理类,也可以直接通过ASM框架来实现创建代理类的功能。
其中,CGLIB是一个强大的、高性能、高质量的代码生成类库,它可以在运行期扩展Java类与实现Java接口量级的Java开发框架。AOP是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
步骤406,继续Spring容器的初始化,返回步骤403,直到整个Spring容器初始化完毕。
可见,通过图4所示的方法,Spring容器中注册的就是被本发明实施例动态加入了监控对象的代理类,但该代理类对用户来说是透明的,最终实现了透明地在被监控方法前后加入监控对象的功能。
本发明实施例适用于通过栈来实现方法执行的各种被监控方法,而对被监控方法所采用的程序语言不作特别限定,例如,采用Java编写的方法的执行是通过栈来实现的,方法进入和退出时遵循先入后出的顺序,本发明实施例也利用一个栈(即监控栈)来跟踪方法的调用情况,每产生一次方法调用时,本发明实施例也新生成一个Java对象记录开始时间并压栈,每次方法栈弹出时,本发明所用的监控栈也弹出开始时间并记录结束时间,因为Java的方法调用使用的方法栈和本发明实施例使用的监控栈的数据结构相同,所以本发明实施例可以完整地记录下每次方法调用情况。从而实现对方法调用进行监控的目的。
对于使用Spring框架开发的应用来说,如果采用本发明实施例,则在引入一个jar包后,只需要预先配置监控信息,就可以针对整个应用指定类的指定方法做方法执行时间的监控,方便快速定位执行时间较长的方法,寻找应用性能瓶颈。并且,本发明实施例对应用代码没有任何污染,不存在修改任何代码的风险。
本发明实施例还能够在多线程情况下的监控方法执行时间,实现对方法执行链中一系列方法按顺序监控其执行时间。
根据本发明实施例提供的上述监控方法,本发明实施例还提供了一种用于监控方法执行时间的监控装置,具体请参见图5。
图5是本发明实施例提供的用于监控方法执行时间的监控装置结构示意图。
如图5所示,该装置包括监控对象动态添加模块501和监控栈502。
监控对象动态添加模块501,用于在被监控方法初始化或者首次被调用时,将用于调用监控对象的调用接口加入到所述被监控方法中。
其中,所述监控对象是预先生成的用于执行监控操作的对象,所述监控对象记录所述被监控方法被调用的时间作为开始时间,将所述开始时间和所述被监控方法的信息压入所述监控栈502中,记录所述被监控方法执行完毕的时间作为结束时间,将压入所述监控栈的所述开始时间和所述被监控方法的信息弹出所述监控栈502。
所述监控对象根据所述结束时间和所述开始时间确定所述被监控方法的执行时间。
其中,所述用于执行监控操作的监控对象包括第一监控方法和第二监控方法;监控对象动态添加模块501,用于在被监控方法初始化或者首次被调用时,将所述第一监控方法的调用接口加入到被监控方法的入口处,将所述第二监控方法的调用接口加入到被监控方法的出口处。
所述第一监控方法记录当前时间作为开始时间,将所述开始时间和所述被监控方法的信息压入监控栈502中。
所述第二监控方法记录当前时间作为结束时间,将压入所述监控栈502的所述开始时间和所述被监控方法的信息弹出所述监控栈502。
该装置还可以包括线程安全存储模块。
所述线程安全存储模块,用于将监控栈502存储在只有当前线程能够操作的变量中。
其中,所述第一监控方法从当前线程的方法执行栈获取栈顶元素信息,并记录当前时间作为开始时间,将所述栈顶元素信息和所述开始时间存储在第一存储对象中,将所述第一存储对象压入到所述监控栈502中。
所述第二监控方法从所述只有当前线程能够操作的变量中取出所述监控栈502,从所述监控栈中取出栈顶元素,并记录当前时间作为结束时间。
其中,当前线程的方法在被调用时被压入所述方法执行栈,在执行完毕时从所述方法执行栈中弹出。
该装置还可以包括有序的存储容器。
所述有序的存储容器用于存储监控栈中元素之间的指向关系信息和被监控方法的执行时间信息,以便于确定出被监控方法的执行时间占调用该被监控方法的上层被监控方法的执行时间的比例信息;其中,所述第二监控方法在从所述监控栈中取出栈顶元素时,记录当前取出的栈顶元素与所述监控栈中新的栈顶元素之间的指向关系,将所述指向关系、所述结束时间和从所述监控栈中取出的栈顶元素中存储的信息加入到有序的存储容器中。
其中,监控对象动态添加模块501可以包括预配置模块和动态添加模块。
所述预配置模块,用于预先配置监控信息,所述监控信息包括被监控的类的标识(ID)信息、以及该被监控的类中被监控方法的身份信息。
所述动态添加模块,用于根据预先配置的所述监控信息,确定出被监控的类和被监控的类中的被监控方法,在被监控的类初始化或者首次被调用时,通过为被监控的类中的被监控方法加入用于调用所述监控对象的调用接口生成所述被监控的类的代理类。
所述动态添加模块,用于在Spring容器初始化时,拦截初始化请求,将所述初始化请求与预先配置的所述监控信息进行对比,如果当前初始化的类是被监控的类,则通过为被监控的类中的被监控方法加入用于调用所述监控对象的调用接口生成所述被监控的类的代理类,将所述代理类返回给所述Spring容器。
以上所述仅为本发明的较佳实施例而已,并不用以限制本发明,凡在本发明的精神和原则之内,所做的任何修改、等同替换、改进等,均应包含在本发明保护的范围之内。