zoukankan      html  css  js  c++  java
  • 【Android测试】【随笔】性能采集工具——小松鼠诞生记

     版权声明:本文出自胖喵~的博客,转载必须注明出处。

       转载请注明出处:http://www.cnblogs.com/by-dream/p/4945066.html

    起因


      去年刚加入TX的时候,我便接手了路宝这款App的性能测试工作。

      当时的性能测试的需求是,采集腾讯路宝在“前台导航”“后台导航”等数个场景下的电流值、cpu、内存、流量等数据。而当时的采集手段是:使用GT回放一段固定的gps轨迹,然后发起导航后使用GT来采集数据。具体过程如下:

     

      从上图可见,整个过程中需要人工干预,其中设置GT操作App是一个重复、繁琐的过程。并且为了保证测试的环境一致,减少误差,需要保证每次电池的电量是一致的(尤其在电流的采集中),但是充电过程比较耗费时间,并且需要人监测,浪费了不少精力。在和项目比较紧张的时候,人力都投入到了项目上,导致性能测试只能在项目完成后再进行测试,每次的结果都是比项目上线要滞后一步。

      于是针对以上痛点,我们经过调研和思考决定采取以下方法来解决目前的现状问题:

      1、充电:使用继电器来完成自动冲、放电;

      2、设置GT:手工操作GT的方式,用程序调用GT的SDk来驱动(后来GT支持了广播方式驱动,也可以用广播);

      3、操作App:使用谷歌提供的Uiautomator来进行UI自动化。

    初探


       有了上述的解决思路后,我便开始一步步的去落实每一项任务。我的想法是用一个Android的应用程序(小松鼠的雏形)来总体控制这所有的事情。我们接下来看看这每一步和小松鼠都是怎么联系起来的:

    自动充电

      从智能平台测试组kerlyyuan同事那里,了解到了继电器可以做到这个,但是对于不懂硬件的我,究竟怎么去弄懂这玩意呢?这期间花费了我很多的精力:咨询淘宝卖家、查阅资料、请教前辈。最终终于搞清楚了这个小玩意的用法。

      而最终指定的解决方案是:将usb数据线的电源线,接在继电器的两端来控制usb数据线的开和断。听到这里是不是有点迷糊了?没事往下看,我会慢慢的解释。

      继电器

        PL2303,是一个usb转串口的单独继电器模块,淘宝搜索有很多卖的。它就是用来控制线路的通和断

      

      USB数据线

        用了这么久的usb数据线,估计今天你们才知道它的内部结构吧。各个线的含义:USB电源线(红线)、数据线负(白线)、数据线正(绿线)、地线(黑线)。

     

      连接方式

        1、将USB数据线的电源线(红线)拦腰剪断,去除外部的绝缘层,让金属丝出来

        2、将电源线两头裸露出来的金属丝固定在继电器的后面两个接触点上,如图所示:

        当年为了做这个东西,不知道残害了多少数据线宝贵的生命,曾一度可怜到没数据线用,至今的充电线还是当年被我破坏后,又进行手术挽救回来的生命。 

      使用方法

        使用的时候只需将继电器插在pc的usb口上,然后pc端向串口发送数据,即可控制继电器的开和断,从而觉得手机是充电还是断电。

        控制继电器的pc端程序,我用c++实现了一个“TencentRelay”的程序,两种方式调用:

        1、界面调用:

          直接填写好当前继电器占用的串口号,模块地址默认为1,然后点击连接串口;连接成功后,点击”吸合”打开继电器,点击“断开”断开继电器;

        2、命令行调用:

          调用命令 ”TencentRelay.exe 3 1 open“ 打开继电器;

          调用命令 ”TencentRelay.exe 3 1 close“ 关闭继电器;

          其中参数二为串口号,参数三为模块地址默认为1,参数四为操作命令,只接受open和close; 

      当继电器打开(吸合)后,继电器硬件的红色指示灯点亮,此时可以模拟充电,相反当继电器断开的时候,红色指示灯熄灭,此时处在断电的状态。

      

      小松鼠如何控制

        PC端我用python写了一个简单的服务器,并且用全民wifi搭建出的一个热点,手机的wifi连接到这个热点之后,它们便处于同一局域网中了。小松鼠App注册了一个电量变化的广播接收器(BroadcastReceiver)来随时接受电池电量的百分比,当手机在充电的时候,如果电量达到了预期值,则会通过UDP协议与PC端的服务器进行通信,服务器收到消息后,便会调用TencentRelay.exe来控制关闭继电器,开始性能测试。

      

    设置GT

      GT做为一款优秀的性能组件,其功能非常强大,并且支持了SDK的调用和广播方式的调用(后来才完善的),因此初期的时候,我在小松鼠内部引入了GT的jar包,通过调用SDk来实现gps轨迹的回放,开始采集性能数据,结束采集性能数据,保存文件等。下面是我使用GTsdk时封装的类,可以参考一下:

      1 package com.bryan.testbattery;
      2 
      3 import java.util.Calendar;
      4 
      5 import com.bryan.data.DataTask;
      6 import com.bryan.testbattery.activity.MainActivity;
      7 import com.tencent.wstt.gt.client.*;
      8 import common.CommonVar;
      9 
     10 import android.content.Context;
     11 import android.os.Build.VERSION;
     12 import android.os.Bundle;
     13 import android.widget.EditText;
     14 
     15 public class MyGT
     16 {
     17     public Context mainContext;
     18     
     19     public EditText myEditText = null;
     20     
     21     String savefilenameString = null;
     22     
     23     //--------------new
     24     DataTask m_dataTask;
     25     String m_appname;
     26     String savefilename = null;
     27     public RunShell myRunShell = null;
     28 
     29     public MyGT(Context parent)
     30     {
     31         mainContext = parent;
     32         myRunShell = new RunShell(mainContext);
     33         
     34         ((MainActivity)mainContext).ThreadUpdateLogToUI("GT Construct finishs ",CommonVar.LOG_HIDE);        
     35     }
     36     
     37     /** 将GT挂载到小松鼠上,启动小松鼠,GT也随之启动 */
     38     public void GtConnect()
     39     {
     40         GT.connect(mainContext.getApplicationContext());    
     41         ((MainActivity)mainContext).ThreadUpdateLogToUI("GT is connect with this APP", CommonVar.LOG_SHOW);
     42     }
     43     
     44     public void givedata(DataTask dt, String appname)
     45     {
     46         m_dataTask = dt;
     47         m_appname = appname;
     48         ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: 传递给GT的appname是:"+m_appname, CommonVar.LOG_HIDE);
     49     }
     50     
     51     public void openPerformance()
     52     {        
     53         GTAutoTesterForApp.startTest(m_appname);
     54         if (m_dataTask.stu_performance.cpu)
     55         {
     56             GTAutoTesterForApp.startSample("cpu");
     57             GTAutoTesterForApp.startSample("jif");
     58             ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: startSample CPU,jiffs", CommonVar.LOG_SHOW);
     59         }
     60         if (m_dataTask.stu_performance.pss)
     61         {
     62             GTAutoTesterForApp.startSample("pss");            
     63             ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: startSample PSS", CommonVar.LOG_SHOW);
     64         }
     65         if (m_dataTask.stu_performance.fps)
     66         {
     67             GTAutoTesterForApp.startSample("fps");
     68             ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: startSample FPS", CommonVar.LOG_SHOW);
     69         }
     70         if (m_dataTask.stu_performance.battery)
     71         {
     72             BeginBattery();
     73         }
     74         if (m_dataTask.stu_performance.net)
     75         {
     76             GTAutoTesterForApp.startSample("net");
     77             ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: startSample NET", CommonVar.LOG_SHOW);
     78         }
     79         if (m_dataTask.stu_performance.tcpdump)
     80         {
     81             BeginTcpdump();
     82             ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: start  tcpdump", CommonVar.LOG_SHOW);
     83         }
     84     }
     85 
     86 
     87     public void stopGT()
     88     {
     89         if (m_dataTask.stu_performance.battery)
     90         {
     91             StopBattery();
     92         }
     93         if (m_dataTask.stu_performance.tcpdump)
     94         {
     95             StopTcpdump();
     96         }
     97         GTAutoTesterForApp.endTestAndClear(savefilename);
     98         ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: stopGT and call 'GTAutoTester.endTest( )' save to " + savefilename, CommonVar.LOG_SHOW);
     99     }
    100 
    101     
    102     public String CreateGTDataFileName()
    103     {
    104         
    105         long now = System.currentTimeMillis(); // 取得当前时间
    106         Calendar c = Calendar.getInstance();
    107         c.setTimeInMillis(now);
    108         
    109         String timeString= (c.get(Calendar.MONTH) + 1) + "~" + c.get(Calendar.DATE) + "_" + c.get(Calendar.HOUR) + "~" + c.get(Calendar.MINUTE) ;
    110         String versionString = myRunShell.getVersion(m_appname)+ "." +((c.get(Calendar.MONTH) + 1)*100+ c.get(Calendar.DATE));
    111 
    112         savefilename = "小松鼠-" + timeString + "-"+m_appname+"-"+versionString+"-"+ m_dataTask.describe;
    113         ((MainActivity)mainContext).ThreadUpdateLogToUI("GT:  " + savefilename+" 这个文件将是将来要保存的文件名,这里只是创建了一个字符串,没有创建真正的文件夹 ",  CommonVar.LOG_SHOW);
    114         return savefilename;
    115     }
    116     
    117     
    118     
    119     public String GetSaveFileName()
    120     {
    121         if (savefilename == null)
    122         {
    123             ((MainActivity)mainContext).ThreadUpdateLogToUI("GT:  " + savefilename+" is not exists",  CommonVar.LOG_SHOW);
    124             return ("date");
    125         }
    126         return savefilename;
    127     }
    128 
    129     // 回放GPS
    130     public void ReplayGPS()
    131     {
    132         String gtversion = myRunShell.getVersion("com.tencent.wstt.gt");
    133         ((MainActivity)mainContext).ThreadUpdateLogToUI("GT version is :  " + gtversion,  CommonVar.LOG_SHOW);    
    134         
    135         if (gtversion.compareTo("2.2.1") ==0)
    136         {
    137             myRunShell.CallGPSActivity();
    138             Bundle bundle = new Bundle();
    139             bundle.putString("cmd", "replay");
    140             bundle.putInt("seq", Integer.parseInt(m_dataTask.mockgpsvalue));
    141             GT.setCommand("gps", bundle);
    142             try
    143             {
    144                 Thread.sleep(500);
    145             } catch (InterruptedException e)
    146             {
    147                 e.printStackTrace();
    148             }
    149             //myRunShell.PushHomekey();
    150             ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: sdk-回放第 "+m_dataTask.mockgpsvalue+"个gps 的轨迹", CommonVar.LOG_SHOW);
    151         }
    152         else 
    153         {
    154             myRunShell.ShellONECommand("am broadcast -a com.tencent.wstt.gt.plugin.gps.startReplay --ei seq "+m_dataTask.mockgpsvalue+" --ei progress 0");
    155             ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: 广播-回放第 "+m_dataTask.mockgpsvalue+"个gps 的轨迹", CommonVar.LOG_SHOW);
    156         }
    157         
    158     }
    159 
    160     // 结束GPS的回放
    161     public void StopGPS()
    162     {
    163         Bundle bundle = new Bundle();
    164         bundle.putString("cmd", "stopReplay");
    165         GT.setCommand("gps", bundle);
    166         
    167         ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: stop ReplayGPS", CommonVar.LOG_SHOW);
    168     }
    169 
    170     // 开启电流测试
    171     public void BeginBattery()
    172     {
    173         Bundle bundle2 = new Bundle();
    174         bundle2.putString("cmd", "start");
    175 
    176         GT.setCommand("battery", bundle2);
    177         ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: setCommand Battery", CommonVar.LOG_SHOW);
    178     }
    179 
    180     // 结束电流测试
    181     public void StopBattery()
    182     {
    183         Bundle bundle2 = new Bundle();
    184         bundle2.putString("cmd", "stop");
    185 
    186         GT.setCommand("battery", bundle2);
    187         ((MainActivity)mainContext).ThreadUpdateLogToUI("GT: stop Battery collect", CommonVar.LOG_SHOW);
    188     }
    189     
    190     // 开启抓包
    191     public void BeginTcpdump()
    192     {
    193         myRunShell.ShellONECommand("mkdir -p /storage/emulated/0/GT/GW/"+m_appname+"/"+savefilename);
    194         String cmdString = "/data/data/com.tencent.wstt.gt/files/tcpdump";    
    195         if (m_dataTask.useWifi)
    196         {
    197              cmdString  += " -i wlan0";
    198         }        
    199         cmdString += " -p -s0 -vv -w /storage/emulated/0/GT/GW/"+m_appname+"/"+savefilename+"/res_Capture.pcap";
    200         
    201         final String tmp = cmdString;
    202         new Thread(new Runnable()
    203         {
    204             @Override
    205             public void run()
    206             {
    207                 myRunShell.ShellONECommand(tmp);
    208             }
    209         }).start();
    210         
    211         
    212     }
    213     
    214     // 结束抓包
    215     public void StopTcpdump()
    216     {
    217         ProcessUtils.killprocess("tcpdump");
    218         try
    219         {
    220             Thread.sleep(3000);
    221         } catch (InterruptedException e)
    222         {
    223             e.printStackTrace();
    224         }
    225     }
    226     
    227     //取消挂载GT
    228     public void disconnect()
    229     {
    230         GT.disconnect(mainContext);
    231         try
    232         {
    233             Thread.sleep(2000);
    234         } catch (InterruptedException e)
    235         {
    236             e.printStackTrace();
    237         }
    238     }
    239 }
    GT

    操作App

      之所以选取Uiautomator,两个原因:

      1、可以脱机运行:只需将jar包push到手机中,然后在小松鼠中使用Runtime运行一句命令即可。而这个需求Appium是无法满足我的。

      2、跨进程:一方面可以在没有源码的技术上,进行UI自动化,这个主要用在竞品对比中;另一方面提升UI自动化的稳定性,举一个很简单的例子:有一次我的自动化执行过程中失败了,原因是找不到控件,我会在出错的时候截图,当时记录下的场景是刚好有人在给我的测试机打电话,于是我在我的脚本中增加了这么一条,如果在找不到控件的时候去判断是否有电话来了,如果是,就去挂掉它。所以Robotium也不是我的菜。

      每次的流程是:

      小松鼠调用GT sdk开启性能采集 --> 小松鼠通过Runtime 调用 “Uiautomator” 命令开始模拟App操作  --> 小松鼠调用GT sdk停止性能采集 

            if (dataTask.stu_performance.battery || dataTask.stu_performance.cpu || dataTask.stu_performance.fps || dataTask.stu_performance.net || dataTask.stu_performance.pss
                            || dataTask.stu_performance.tcpdump)
            {
                // 根据性能选择项,启动GT监控测试
                m_gt.openPerformance();
                Thread.sleep(1500);
            }
    
            // 根据uiautomator命令启动APP程序        
            shell.ShellONECommand(dataTask.cmd);
    
            
            if (dataTask.stu_performance.battery || dataTask.stu_performance.cpu || dataTask.stu_performance.fps || dataTask.stu_performance.net || dataTask.stu_performance.pss
                            || dataTask.stu_performance.tcpdump)
            {
                m_gt.stopGT();
                Thread.sleep(3000);
    
            }

      这样的方式调用Uiautomator命令,是一个同步的过程,因此Uiautomator的执行时间就决定了性能采集的时长,因此在写脚本的时候,发起导航后,需要sleep的时间就是到达目的地的时间。

    小有成果


    简介  

      做完上述的所有工作之后,小松鼠第一代诞生了:

      先简单介绍这个工具的各个控件的含义吧:

      参数设置

        预期电量值:当你设置之后,只有当前手机电量的百分比大于等于这个值后,App才会给PC发消息关掉继电器,然后进行后面的流程;

        服务器IP:电脑上共享来的无线网卡的ip地址;

        测试用网络:uiautomator开始运行时使用wifi还是3G网络;

        性能测试项:勾选哪一项,就会通过调用再采集的时候只采集该项;

        执行场景:勾选哪一项,就会执行对应的uiautomator脚本;

        GT保存路径:采集完成之后,性能数据保存的文件夹名称

      当前状态

        这一栏当时在做的过程中主要是调试用的。

        wifi状态代表当前wifi是打开还是关闭;电量状态代表的是当前手机电池电量的百分比。

      log

        当出现错误时,可以初步定为问题,因此保留log在主界面。

      操作

        当配置栏设置好之后,点击开始即触发了任务。结束会停止任务。

    使用步骤

      知道了App的参数含义后,相信大家已经基本会用了吧。每一次的性能测试,我们都可以通过这样的方式来进行:

      1、 将继电器插在PC端,并且使继电器处于吸合(打开)状态,将搭载继电器USB线两端分别连接电源和手机,使手机处于充电状态;

      2、 给手机安装APP TestBattery,启动APP之前,关闭GT和路宝;

      3、 配置好局域网,填好局域网内IP地址,将PC端服务器打开;

      4、 打开手机端APP,设置预期电量值、服务器IP地址、性能测试项、测试场景、日志存放文件夹名称等一系列参数,设置完毕后,点击开始按钮;

      5、此时APP出于检测状态,电量状态当手机实际电量等于填写的预期电量值时,就会触发关闭继电器,开始性能测试,性能测试的整个过程中,GT实时地采集性能数据,待一个场景测试结束后,手机自动连接WIFI给PC端服务器发消息;

      6、 PC端收到数据后就会打开继电器,再次进入充电状态;

      7、 这样一轮测试就结束了,本次保存的性能测试数据存放在手机中的GT目录下;

    不足

      很显然这个方案相比较之前的方案,已经省去了很多的人力,每一次的测试都需要提前准备好继电器,并且配置好App,点击开始即可进行了。但是仍然有可以改进的地方:

      1、UI不美观:

        虽然这是一个自己使用的App,但是它也有一颗登上大舞台的心,梦想还是要有的,万一实现了呢?因此有一套美观的UI也是一个像样点App的最基本的要求;

      2、配置缺陷:

        不知道有没有人发现,每次测试的时候都需要配置一次,并且每次只能测试一个场景,如果要是一次能配置多个场景,让它一个接一个的执行,这样则更加方便了;

      3、无法测试其他App:

        在 “执行场景” 这个地方,Uiautomator脚本和对于的场景是在Android代码里写死了,如果要运行其他程序,则需要改源码。

      4、结果处理:

        目前在一轮性能测试结束后,采集的性能数据结果存放在手机上GT的目录下,不方便即时的查看,不能做到数据的实时展示。

    浴火重生


      就这样我决定针对第一版的不足去大改一次它,即使它已经服役了好几个版本的路宝性能测试的采集工作。

      再经历了两个月后,新版的小松鼠出炉了!!

      你可以看到除了UI上有了很大的进步(做为一个写代码的,ui做成这样,我觉得已经可以了),功能变的更加的强大,解决了第一版的所有缺陷,当然功能越强大带来的问题就是操作性上可能更繁琐了,这里的操作可能更多的就是配置了,因此为了解决这个问题,我在配置这一块支持了外部配置,即使用一个xml的配置文件即可完成配置。好了,我们先看看配置文件的定义吧。

    <?xml version="1.0" encoding="utf-8"?>
    <Performance>
        <!-- 要测试的应用程序的名称 -->
        <Application>
            <Name>com.tencent.navsns</Name>
        </Application>
                
        <!-- pc端服务器 -->
        <Server>
            <Ip>10.2.9.207</Ip>
            <Port>8885</Port>
        </Server>
        
        <!-- 测试内容 会一个一个模块进行执行 -->
        <TestTask>
            <!-- 几个测试的模块-->
            <count>2</Module2>
            <!-- 第一个测试模块项 -->
            <Module1>
                <!-- 电量设置信息 -->
                <Battery>
                    <!-- yes代表使用控制充电技术 no代表不使用-->
                    <Use>yes</Use>
                    <!-- 当电量到达value的百分比值后,断电 -->
                    <Value>90</Value>
                    <!-- 断电后静置的时间,为了防止充完电手机发热影响测试数据 单位s -->
                    <Waittime>60</Waittime>
                </Battery>
                <!-- 回放GT当中的第几个GPS文件(回头改成自己的) -->
                <Mockgps>
                    <!-- yes代表使用回放GPS -->
                    <Use>yes</Use>
                    <!-- 回放第几个文件,从0开始 -->
                    <Value>0</Value>
                </Mockgps>
                <!-- 代表应用程序运行起来后使用的网络 no就使用3G数据 -->
                <Network>
                    <Wifi>yes</Wifi>
                </Network>
                <!-- 测试时的音量值 -->
                <Audio>
                    <!-- 测试时的进行修改音量为指定值 -->
                    <Use>yes</Use>
                    <Value>90</Value>
                </Audio>
                <!-- 测试时的屏幕亮度值 -->
                <Brightness>
                    <!-- 测试时的进行修改屏幕亮度为指定值 -->
                    <Use>yes</Use>
                    <Value>90</Value>
                </Brightness>
                <!-- 测试时是否开启防止手机休眠(即使按下电源键屏幕暗了也不休眠) -->
                <Wakelock>yes</Wakelock>
                <!-- 性能测试项 -->
                <PerformOption>
                    <Battery>yes</Battery>
                    <Cpu>yes</Cpu>
                    <!-- 内存 -->
                    <Pss>yes</Pss>
                    <Fps>yes</Fps>
                    <Net>yes</Net>
                    <!-- 抓包 -->
                    <Tcpdump>yes</Tcpdump>
                </PerformOption>
                <!-- 要进行测试的应用程序的模拟 -->
                <Execution>
                    <Describe>导航前台</Describe>
                    <!-- 模拟应用程序的脚本命令 -->
                    <Command>uiautomator runtest Testxiaolu.jar -c fby.test.uiautomator.NaviBack</Command>
                </Execution>
                <!-- 执行完本模块后休息一段时间再进行下一个模块 单位s -->
                <Sleeptime>10</Sleeptime>
            </Module1>
            
            <!-- 第二个测试模块项 -->
            <Module2>
                <Battery>
                    <Use>no</Use>
                    <Value>90</Value>
                    <Waittime>60</Waittime>
                </Battery>
                
                <!-- ********* -->
                <!-- ********* -->
                <!-- ********* -->        
                
                <Sleeptime>10</Sleeptime>
            </Module2>
        </TestTask>
    </Performance>

      从配置可以看出我们支持了更多的东西:

        1、每次测试的手机环境,除了保证电量的一致性,还可以设置手机的音量大小和屏幕亮度,后台灭屏场景的测试可以设置是否打开wakelock锁。

        2、测试回放的场景的脚本,支持为了可配置,当然为了方便,我把经常用的直接编译到了我的app中。

        选取了对应的场景之后,Uiautomator脚本的运行命令就帮我自动的填写在了这个地方。

        当然你可以自己在可以编辑你的运行命令,或者是在xml中进行配置即可。

      现在每天我都会用最新的提测包,去测试一组性能数据。我的配置文件是固定,存在手机sd卡的根目录下即可。如果每次需要修改,直接修改xml即可。

      当手机中有了配置文件之后,小松鼠会自动读取这些测试场景:

     

      这个时候,只需点击 “开始” 即可。

      因为每次测完一个场景之后,都会进行 “智能充电” 的过程,因此我的这些脚本一般能运行一晚上的时间,我只需要第二天来了之后,处理数据即可。我们看看现场的环境:  

      来张近照

      前面提到,我的PC的服务器,它除了可以接受小松鼠发来的控制继电器的命令外,它还可以接受每次性能数据采集完成之后的发来的性能数据压缩包。

      

      接受到这些压缩包后,服务器端的脚本程序可以对这些压缩包中的数据进行处理,得到每一项指标的数据,

      得到这些数据之后,我们将这些数据组织后,传到我们组的 “八爪鱼” 平台(http://10.123.12.78:8080/AutomacPlatform/index#/about)来进行结果展示:

      目前数据是将当前版本和之前版本的数据进行对比,每个场景的多项指标数据,可以通过滚动来查看:

      

      到此,你就已经了解到了小松鼠是 “一键开始性能测试,到最终所有结果上传到web端进行展示” 的全部过程。

      整个过程中遇到踩了不少坑,但是也积累了很多宝贵的经验,如果你对性能数据的采集也感兴趣,那么请联系我。

      

    未来


      就目前而言,性能采集的部分已经相对其他工具已经算是比较完善了,但是性能之所以做为项目组关注的重点,其分析、定位问题是很重要的,目前这一块,我们还没有深入的进行,因此后面的计划,就是如何采集出更加精确的数据,并且进行更加精准的分析,然后在web的每日监控中发现问题可以更加快速的定位到问题。期待我的下个半年...

     

  • 相关阅读:
    【bzoj1036】【ZJOI2008】树的统计
    AE基础(8)PageLayout属性设置和添加元素
    AE基础(7)布局控件与地图控件关联
    UtilityAction扩展
    UtilityAction
    AE基础(6)数据查询与选择
    NavigationAction
    LayerAction
    AE基础(5)鹰眼功能
    AE基础(4)画几何图形
  • 原文地址:https://www.cnblogs.com/by-dream/p/4945066.html
Copyright © 2011-2022 走看看