newrelic的android sdk实现原理解析(二)

来源:互联网 发布:淘宝手机如何装修店铺 编辑:程序博客网 时间:2024/05/01 00:06

2.2、newrelic.agent
newrelic.agent的整体框架主要分成android api注入,数据采集器,数据任务队列,measurement生产者消费者模型,数据预处理和发送模块;
newrelic.agent模块的整体框架如下图所示;

图2.2 newrelic.agent的模块框架

2.2.1、android api注入
android api的注入工作在rewriter.agent中完成,根据type_map.properties文件中配置的目标api注入方式进行相应的替换(REPLACE_CALL_SITE)和包裹(WRAP_METHOD)注入;

2.2.2、数据采集器
采集器采集的数据种类主要有StatsEngine数据,sampler数据,TransactionState数据,activityTrace数据,threadLoadTrace数据,HttpTransaction数据,HttpError数据,Activity生命周期数据;
TraceMachine作为sampler,TransactionState,activityTrace,threadLoadTrace数据,HttpTransaction数据,HttpError数据,Activity生命周期数据的采集器;
2.2.2.1、性能数据采集方式
主要有以下几种;
1)method的性能数据采集
将enterMethod和exitMethod方法注入(注入工作在rewriter.agent中完成)到替换android api的api开始和结束处,hook的目的用来采集该api执行的性能数据;其中sampler,SummaryMetricMeasurementConsumer都实现了TraceLifecycleAware接口并在TraceMachine中注册,当已被hook api触发TraceMachine.enterMethod和exitMethod方法时,会启动和结束TraceMachine中已注册类实例的性能数据采样工作;
比如,当应用执行sqlite的query方法时会触发TraceMachine.enterMethod(“SQLiteDatabase#query”),在enterMethod方法中启动Trace跟踪和调用sampler的onEnterMethod方法启动内存数据的采样。当sqlite结束时会触发TraceMachine.exitMethod(),在exitMethod方法中结束trace跟中和调用sampler的onExitMethod方法停止内存采样;最后将trace放到TaskQueue中,TaskQueue会负责将trace分发到对应的Measurement中;
2)事务的性能数据采集
将TransactionState实例注入(注入工作在rewriter.agent中完成)到替换android api的api中,TransactionState实例会记录本次事务过程中产生的性能数据;
比如,当应用执行HttpRequest的execute方法时会被替换成使用HttpInstrumentation中的execute方法,其中会使用TransactionState实例记录整个request到response过程中的数据,数据包括request uri,运营商信息,header,response code,response data等;
3)activity的性能数据采集
主要hook了activity生命周期内的onStart,activityStarted和onStop方法;在这些方法中注入enterMethod和exitMethod方法,在执行时会通过registerNewTrace方法创建childTrace并加入到TraceMachine的rootActivityTrace中;
4)StatsEngine异常数据采集
StatsEngine用来记录newrlic.agent本身的异常数据,采集在与后端服务器数据交互的周期内agent本身发生的一些异常信息,它继承了HarvestAdapter类,因此会在Harvester发生CONNECTIONED状态时被触发onHarvest方法->populateMetrics方法将异常数据包装成Metric通过TaskQueue发送给对应的HarvestData中machineMeasurements;

2.2.3、任务队列
TaskQueue类为newrelic.agent中的任务队列,它在Measurements中实例化,用来接收不同数据采集器采集到的性能数据,并根据类型将他们分别转发到下游的Measurements工厂和Harvest中;其中Measurements为生产者和消费者模型,进行性能数据预处理、统计和组装工作;

2.2.4、Measurements”工厂”
Measurements作为所有MeasurementProducer和MeasurementConsumer的管理者,通过MeasurementEngine的MeasurementPool增删producer和consumer,它主要负责生产和消费不同种类性能数据;
比如,当收到TaskQueue的Trace数据时,会使用methodMeasurementProducer的produceMeasurement方法包装trace数据并通过MeasurementPool的broadcastMeasurements方法通知对应的trace数据消费者methodMeasurementConsumer,消费者使用consumeMeasurement将处理后的数据加入到自己的MetricStore中;

2.2.5、Harvest
Harvest负责对所有数据最终收集、验证、打包和回传服务器;HarvestTimer作为定时器会周期性的触发数据收集和回传服务器的动作,具体的过程为,HarvestTimer定时器触发Harvester的execute方法,若CONNECTED状态则会触发fireOnHarvestBefore,fireOnHarvest,fireOnHarvestFinalize方法,fireOnXXX方法中会遍历所有已实现HarvestLifecycleAware接口并且注册在Harvester的XXXMeasurementConsumer,XXXMeasurementConsumer中的onHarvestXXX方法会将采集、处理过的数据统一添加到HarvestData中并打包;最后通过Harvester的connected方法发送本周期的性能数据到服务器上;

2.3、plugin部分
这部分比较简单,按照正常的plugin开发流程写就是了;
主要步骤;
1)在plugin.xml中把导出的class.rewriter.jar和newrelic.android.jar加入到Classpath中;
2)开发实现IStartup接口的Bootstrap类,并实现earlyStartup方法;在这里需要通过tools.jar的com.sun.tools.attach.VirtualMachine动态加载Instrumentation代理(class.rewriter.jar中的rewriter.agent),另外,runtime中装载tools.jar前面已经说过;

主要的代码如下;

import com.sun.tools.attach.VirtualMachine;import java.io.File;import java.io.IOException;import java.lang.management.ManagementFactory;import org.eclipse.ui.IStartup;public class Bootstrap implements IStartup{    public Bootstrap(){}    public void earlyStartup(){        String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();        int p = nameOfRunningVM.indexOf('@');        String pid = nameOfRunningVM.substring(0, p);        String jarFilePath;        try{        jarFilePath = com.apmbe.mtrix.agent.compile.RewriterAgent.class.getProtectionDomain().getCodeSource().getLocation().getPath().toString();            jarFilePath = (new File(jarFilePath)).getCanonicalPath();        }        catch(IOException e){            throw new RuntimeException(e);        }        try{            VirtualMachine vm = VirtualMachine.attach(pid);            vm.loadAgent(jarFilePath, "debug=true;");            vm.detach();        }        catch(NoClassDefFoundError e){            Util.dialogMessage("Unfortunately, apmbe was unable to load properly.  It's likely you're attempting to run Eclipse using the JRE instead of the JDK.  To run Eclipse using the JDK, you can either prepend the JDK to your system PATH or launch Eclipse with the -vm <path/to/jdk/javaw.exe> argument.  For more information, see http://www.apmbe.com/help.");            throw new RuntimeException(e);        }        catch(Exception e){            String message = (new StringBuilder("Unfortunately, xxx was unable to load properly.  Please contact support@mtrix.com and include the following error message: ")).append(e.getMessage()).toString();            Util.dialogMessage(message);            throw new RuntimeException(e);        }        Util.NRSingleton.getInstance().setBooted();    }}

3)开发IObjectActionDelegate实现newrelic.android.jar的自动安装
右键install会将newrelic.android.jar安装到工程libs中;主要代码如下;

public class Install implements IObjectActionDelegate{    final String localAgentVersion;    private ISelection selection;    IProject project;        public Install(){    localAgentVersion = com.apmbe.mtrix.agent.android.Agent.getVersion();    }    public void run(IAction action){        if(selection instanceof IStructuredSelection){            Object selected = ((IStructuredSelection)selection).getFirstElement();            if(selected instanceof IProject)                project = (IProject)selected;            else            if(selected instanceof IAdaptable)            project = (IProject)((IAdaptable)selected).getAdapter(org.eclipse.core.resources.IProject.class);            else                return;            if(!Util.NRSingleton.getInstance().isBooted()){                Util.dialogMessage("Unfortunately, apmbe was unable to load and cannot continue.  Please run Eclipse with the -consoleLog option and forward the log to support@mtrix.com");                return;            }            try{            File jarFile = new File(com.apmbe.mtrix.agent.android.xxx.class.getProtectionDomain().getCodeSource().getLocation().getPath().toString());                File libFile = project.getLocation().append("libs").append("apmbe.android.jar").toFile();                addNature();                if(libFile.exists()){                    String currentVersion = jarVersion(libFile);                    if(isOtherVersion(libFile)){                        copyFile(jarFile, libFile);                        Util.dialogMessage((new StringBuilder("Update complete, I found version ")).append(currentVersion).append(" of the agent and replaced it with ").append(localAgentVersion).toString());                    }                     else{                        copyFile(jarFile, libFile);                        Util.dialogMessage((new StringBuilder("Hurray!  It looks like you already have the latest version (")).append(localAgentVersion).append(") of the apmbe Agent installed.").toString());                    }                }                 else{                    File libDir = project.getLocation().append("libs").toFile();                    if(!libDir.exists())                        libDir.mkdirs();                    copyFile(jarFile, libFile);                    Util.dialogMessage((new StringBuilder("Congratulations!  Version ")).append(localAgentVersion).append(" of the apmbe Android Agent has been installed in your libs directory.  ").append("Please check that apmbe.android.jar is included on your build path.  Additionally, make sure you've added the code necessary ").append("to boot the agent to your app's initilization section and we look forward to seeing you at http://www.apmbe.com.").toString());                }                project.refreshLocal(2, null);            }            catch(IOException e){                Util.dialogMessage((new StringBuilder("Woops, something went wrong while trying to copy the apmbe agent jar: ")).append(e.getMessage()).toString());                e.printStackTrace();            }            catch(CoreException e){                Util.dialogMessage((new StringBuilder("Woops, something went wrong while trying to add the apmbe nature to your project: ")).append(e.getMessage()).toString());                e.printStackTrace();            }        }    }

2.4、山寨
源代码位置:https://github.com/fifa2002nb/xxxrelic
山寨sdk eclipse插件在线安装地址:http://www.apmbe.com:8070/android

0 0
原创粉丝点击