能用机器完成的,千万别堆工作量|持续集成中的性能自动化测试

1.背景

当前闲鱼在精益开发模式下,整个技术团队面临了诸多的能力落地和挑战,尤其是效能方面的2-1-1的目标(2周需求交付周期,1周需求开发周期,1小时达到发布标准),具体可见 闲鱼工程师是如何构建持续集成流水线,让研发效率翻倍的 ,在这个大目标下,就必须把每个环节都做到极致。自动化的建设是决定CI成败的关键能力,今天分享一下闲鱼Android客户端性能自动化环节的实践。

2.面临的问题

2.1 主要是两个方面的问题

  • 工具缺失:

目前淘宝系,对于线上性能水位的监控有一套完善的体系,但是针对新功能的性能测试,每个业务团队都有对应的性能专项小组,产出的工具都是根据自己业务特点的定制开发的,闲鱼客户端目前使用Flutter做为客户端主开发语言,对于Flutter性能数据的获取及UI自动化测试支撑工具目前是缺失的,同时业界对Flutter自动化和性能相关的实践几乎没有;

  • 测试工作量翻N倍(N=一个版本周期内的分支数):

原先的开发模式是功能测试集成测试一起进行的,所以性能测试只需要针对集成后的包进行测试即可,到现在转变为泳道的开发模式,一个版本内会一般包含十几个左右的泳道分支甚至更多,我们必须确保每个泳道的分支的性能是达标的,如果有性能问题需要第一时间反馈出来,如果遗留到集成阶段,问题的排查(十几个分支中筛查),问题的解决将会耗费大量的时间,效率很难得到大的提升;

2.2 问题思考

体系化解决,要让每个泳道分支都得到有效测试覆盖,测试件能够自动化执行,持续反馈结果

3. 解决方案

综合上述问题,梳理如下解决方案:

  • 针对Flutter性能数据的获取(比如,Flutter有自己的SurfaceView,原有Native计算FPS的方式无法直接使用)
  • 针对Flutter UI自动化的实现(Flutter/Native UI混合栈的处理)
  • 性能自动化脚本 / 性能数据自动采集、上报 融入CI流程
  • 性能问题的通知 / 报表展示 / 分析

3.1 性能数据

[FPS]

解析处理 adb shell dumpsys SurfaceFlinger --latency 的数据,详细请见文末参考链接(该方式兼容Flutter及Native的解决方案,已在Android4.x-9.x验证可行),处理SurfaceFlinger核心代码如下:

dumpsys SurfaceFlinger --latency-clear
#echo "dumpsys SurfaceFlinger..."
if [[ $isflutter = 0 ]];thenwindow=`dumpsys window windows | grep mCurrent | $bb awk '{print $3}'|$bb tr -d '}'` # Get the current windowecho $window
fi
if [[ $isflutter = 1 ]];thenwindow=`dumpsys SurfaceFlinger --list |grep '^SurfaceView'|$bb awk 'NR==1{print $0}'`
if [ -z "$window" ]; then window="SurfaceView"
fi
echo $window
fi
$bb usleep $sleep_t
dumpsys SurfaceFlinger --latency "$window"|$bb awk -v time=$uptime -v target=$target -v kpi=$KPI '{if(NR==1){r=$1/1000000;if(r<0)r=$1/1000;b=0;n=0;w=1}else{if(n>0&&$0=="")O=1;if(NF==3&&$2!=0&&$2!=9223372036854775807){x=($3-$1)/1000000/r;if(b==0){b=$2;n=1;d=0;D=0;if(x<=1)C=r;if(x>1){d+=1;C=int(x)*r;if(x%1>0)C+=r};if(x>2)D+=1;m=r;o=0}else{c=($2-b)/1000000;if(c>1000){O=1}else{n+=1;if(c>=r){C+=c;if(c>kpi)o+=1;if(c>=m)m=c;if(x>1)d+=1;if(x>2)D+=1;b=$2}else{C+=r;b=sprintf("%.0f",b+r*1000000)}}};if(n==1)s=sprintf("%.3f",$2/1000000000)};if(n>0&&O==1){O=0;if(n==1)t=sprintf("%.3f",s+C/1000);else t=sprintf("%.3f",b/1000000000);T=strftime("%F %T",time+t)"."sprintf("%.0f",(time+t)%1*1000);f=sprintf("%.2f",n*1000/C);m=sprintf("%.0f",m);g=f/target;if(g>1)g=1;h=kpi/m;if(h>1)h=1;e=sprintf("%.2f",g*60+h*20+(1-o/n)*20);print s","t","T","f+0","n","d","D","m","o","e","w;n=0;if($0==""){b=0;w+=1}else{b=$2;n=1;d=0;D=0;if(x<=1)C=r;if(x>1){d+=1;C=int(x)*r;if(x%1>0)C+=r};if(x>2)D+=1;m=r;o=0}}}}' >>$file

[CPU]

使用的是top的命令获取(该方式获取性能数据时,数据收集带来的损耗最少)

export bb="/data/local/tmp/busybox"
$bb top -b -n 1|$bb awk 'NR==4{print NF-1}'

[内存]

解析 dumpsys meminfo $package 拿到 Java Heap,Java Heap Average,Java Heap Peak,Native Heap,Native Heap Average,Native Heap Peak,Graphics,Unknown,Pss 数据

do_statistics() {((COUNT+=1))isExist="$(echo $OUTPUT | grep "Dalvik Heap")" if [[ ! -n $isExist ]] ; thenold_dumpsys=trueelseold_dumpsys=falsefiif [[ $old_dumpsys = true ]] ; thenjava_heap="$(echo "$OUTPUT" | grep "Dalvik" | $bb awk '{print $6}' | $bb tr -d '\r')"elsejava_heap="$(echo "$OUTPUT" | grep "Dalvik Heap[^:]" | $bb awk '{print $8}' | $bb tr -d '\r')"fiecho "1."$JAVA_HEAP_TOTAL "2."$java_heap "3."$JAVA_HEAP_TOTAL((JAVA_HEAP_TOTAL+=java_heap))((JAVA_HEAP_AVG=JAVA_HEAP_TOTAL/COUNT))if [[ $java_heap -gt $JAVA_HEAP_PEAK ]] ; thenJAVA_HEAP_PEAK=$java_heapfiif [[ $old_dumpsys = true ]] ; thennative_heap="$(echo "$OUTPUT" | grep "Native" | $bb awk '{print $6}' | $bb tr -d '\r')"elsenative_heap="$(echo "$OUTPUT" | grep "Native Heap[^:]" | $bb awk '{print $8}' | $bb tr -d '\r' | $bb tr -d '\n')"fi((NATIVE_HEAP_TOTAL+=native_heap))((NATIVE_HEAP_AVG=NATIVE_HEAP_TOTAL/COUNT))if [[ $native_heap -gt $NATIVE_HEAP_PEAK ]] ; thenNATIVE_HEAP_PEAK=$native_heapfig_Str="Graphics"if [[ $OUTPUT == *$g_Str* ]] ; thenecho "Found Graphics..."Graphics="$(echo "$OUTPUT" | grep "Graphics" | $bb awk '{print $2}' | $bb tr -d '\r')"elseecho "Not Found Graphics..."Graphics=0fiUnknown="$(echo "$OUTPUT" | grep "Unknown" | $bb awk '{print $2}' | $bb tr -d '\r')"total="$(echo "$OUTPUT" | grep "TOTAL"|$bb head -1| $bb awk '{print $2}' | $bb tr -d '\r')"
}

[流量]

通过 dumpsys package packages 解析出当前待测试包来获取流量信息

uid="$(dumpsys package packages|$bb grep -E "Package |userId"|$bb awk -v OFS=" " '{if($1=="Package"){P=substr($2,2,length($2)-2)}else{if(substr($1,1,6)=="userId")print P,substr($1,8,length($1)-7)}}'|grep $package|$bb awk '{print $2}')"
echo "Net:"$uid
initreceive=`$bb awk -v OFS=" " 'NR>1{if($2=="wlan0"){wr[$4]+=$6;wt[$4]+=$8}else{if($2=="rmnet0"){rr[$4]+=$6;rt[$4]+=$8}}}END{for(i in wr){print i,wr[i]/1000,wt[i]/1000,"wifi"};for(i in rr){print i,rr[i]/1000,rt[i]/1000,"data"}}' /proc/net/xt_qtaguid/stats | grep $uid|$bb awk '{print $2}'`
inittransmit=`$bb awk -v OFS=" " 'NR>1{if($2=="wlan0"){wr[$4]+=$6;wt[$4]+=$8}else{if($2=="rmnet0"){rr[$4]+=$6;rt[$4]+=$8}}}END{for(i in wr){print i,wr[i]/1000,wt[i]/1000,"wifi"};for(i in rr){print i,rr[i]/1000,rt[i]/1000,"data"}}' /proc/net/xt_qtaguid/stats | grep $uid|$bb awk '{print $3}'`echo "initnetarray"$initreceive","$inittransmit
getnet(){local data_t=`date +%Y/%m/%d" "%H:%M:%S`netdetail=`$bb awk -v OFS=, -v initreceive=$initreceive -v inittransmit=$inittransmit -v datat="$data_t" 'NR>1{if($2=="wlan0"){wr[$4]+=$6;wt[$4]+=$8}else{if($2=="rmnet0"){rr[$4]+=$6;rt[$4]+=$8}}}END{for(i in wr){print datat,i,wr[i]/1000-initreceive,wt[i]/1000-inittransmit,"wifi"};for(i in rr){print datat,i,rr[i]/1000-initreceive,rt[i]/1000-inittransmit,"data"}}' /proc/net/xt_qtaguid/stats | grep $uid`echo $netdetail>>$filenet
}

3.2 性能自动化脚本

  • 基于Appium的自动化用例,这个技术业界已经有非常多的实践了,这里我不再累述,如果不了解的同学,可以到Appium官网 http://appium.io
  • Flutter和Native页面切换使用App内的Schema跳转
  • Flutter页面的文本输入等交互性较强的场景使用基于Flutter框架带的Integration Test来操作

An integration test

Generally, an integration test runs on a real device or an OS emulator, such as iOS Simulator or Android Emulator. The app under test is typically isolated from the test driver code to avoid skewing the results.

Flutter的UI自动化及Flutter/Native混合页面的处理在测试上的应用后续单独开文章介绍,原理相关可以先参考 千人千面录制回放技术

3.3 性能自动化CI流程

3.4 性能数据报表

FPS相关

  • Framediff: 绘制帧的开始时间和结束时间差
  • FPS: 每秒展示的帧数
  • Frames: 一个刷新周期内所有的帧
  • jank: 一帧开始绘制到结束超过16.67ms 就记一次jank,jank非零代表硬件绘制掉帧,和屏幕硬件性能及相关驱 动性能有关
  • jank2: 一帧开始绘制到结束超过33.34ms 就记一次jank2
  • MFS: 在一个刷新周期内单帧最大耗时(每两行垂直同步的时间差代表两帧绘制的帧间隔)
  • OKT: 在一个刷新周期内,帧耗时超过16.67ms的次数
  • SS: 流畅度,通过FPS,MFS,OKT计算出来,流畅度 = 实际帧率比目标帧率比值60【目标帧率越高越好】 + 目标时间和两帧时间差比值20【两帧时间差越低越好】 + (1-超过16ms次数/帧数)*20【次数越少越好】

4. 成果展示

4.1 指定泳道分支性能监控

泳道分支出现了性能问题再报表上一目了然

4.2 性能专项支撑

1、Flutter商品详情页重构 14轮测试

2、客户端图片统一资源测试 4轮测试

5. 总结

性能自动化只是整个CI流程中的一个环节,为了极致效率的大目标,闲鱼质量团队还产出了很多支撑工具,CI平台,遍历测试,AI错误识别,用例自动生成等等,后续也会分享给大家。


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/519734.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

获取redis中以某些字符串为前缀的KEY列表

public static final String prefix "helllojava_*"; //这个*一定要加&#xff0c;否则无法模糊查询 Set<String> keys stringRedisTemplate.keys(prefix); for(String key : keys){System.out.println(key); }链接地址&#xff1a; https://blog.csdn.net/l…

详解GPU技术关键参数和应用场景

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | Hardy责编 | 阿秃随着云计算&#xff0c;大数据和人工智能技术发展&#xff0c;边缘计算发挥着越来越重要的作用&#xff0c;补充数据中心算力需求。计算架构要求多样化&#xff0c;需要不同的CPU架构来满足不断增长的算力需…

5款神器级别Github 的Chrome插件

文章目录1. Chrome插件一&#xff1a;octotree2. Chrome插件二&#xff1a;sourcegraph3. Chrome插件三&#xff1a;Enhanced GitHub4. Chrome插件四&#xff1a;octolinker5. Chrome插件五&#xff1a;gitzip for github1. Chrome插件一&#xff1a;octotree Octotree是一个 …

Tensorflow源码解析3 -- TensorFlow核心对象 - Graph

1 Graph概述 计算图Graph是TensorFlow的核心对象&#xff0c;TensorFlow的运行流程基本都是围绕它进行的。包括图的构建、传递、剪枝、按worker分裂、按设备二次分裂、执行、注销等。因此理解计算图Graph对掌握TensorFlow运行尤为关键。 2 默认Graph 默认图替换 之前讲解…

用AI说再见!“辣眼睛”的买家秀

阿里妹导读&#xff1a;提起买家秀和卖家秀&#xff0c;相信大家脑中会立刻浮现出诸多画面。同一件衣服在不同人、光线、角度下&#xff0c;会呈现完全不同的状态。运营小二需从大量的买家秀中挑选出高质量的图片。如果单纯靠人工来完成&#xff0c;工作量过于巨大。下面&#…

mysql相关

查询指定时间相近的记录SELECT * abs(UNIX_TIMESTAMP(t.create_time)-UNIX_TIMESTAMP(2020-06-04 10:10:39)) as min from t_video_history t WHERE t.ip 10.0.5.124 GROUP BY min asc limit 1查询一个月之前的数据 select * from t_video_history t where date_format(t.cr…

云+X案例展 | 电商零售类:WakeData助力叁拾加数字化变革

本案例由WakeData投递并参与评选&#xff0c;CSDN云计算独家全网首发&#xff1b;更多关于【云X 案例征集】的相关信息&#xff0c;点击了解详情丨挖掘展现更多优秀案例&#xff0c;为不同行业领域带来启迪&#xff0c;进而推动整个“云行业”的健康发展。在新零售时代下&#…

linux环境安装Kafka最新版本 jdk1.8

文章目录一、环境分布二、实战1. kafka下载2. 解压3. 配置4. 编写启动脚本5. 编写关闭脚本6. 赋予脚本可执行权限7. 脚本使用案例一、环境分布 软件版本jdk1.8kafkakafka_2.13-2.5.0 二、实战 kafka官网地址&#xff1a; http://kafka.apache.org/downloads 1. kafka下载 …

BeanUtils对象之间的复制

1、maven依赖<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.4</version> </dependency>2、常用API // 把orig对象copy到dest对象中. public void copyProperties…

基于泛型编程的序列化实现方法

写在前面 序列化是一个转储-恢复的操作过程&#xff0c;即支持将一个对象转储到临时缓冲或者永久文件中和恢复临时缓冲或者永久文件中的内容到一个对象中等操作&#xff0c;其目的是可以在不同的应用程序之间共享和传输数据&#xff0c;以达到跨应用程序、跨语言和跨平台的解耦…

两大硬件设计被OCP官方接受,腾讯成国内互联网公司第一家

刚刚获悉&#xff0c;腾讯在光网络设备和数据中心领域的两大硬件自研设计“OPC-4”和“TMDC”顺利通过OCP&#xff08;Open Compute Project&#xff09;审核并正式接受为官方开源贡献。这是腾讯在硬件领域的开源设计首次被OCP官方正式认可&#xff0c;同时&#xff0c;腾讯也成…

java 集成kafka单机版 适配jdk1.8

文章目录一、环境分布1. 版本声明2. 依赖2. case测试2. case2测试一、环境分布 1. 版本声明 linux服务器软件版本jdk1.8kafkakafka_2.13-2.4.0注&#xff1a;建议版本和应用依赖的客户端版本依赖保持一致&#xff0c;如果需要更高版本&#xff0c;可以尝试 但是有一点&#x…

微服务架构下,解决数据一致性问题的实践

随着业务的快速发展&#xff0c;应用单体架构暴露出代码可维护性差、容错率低、测试难度大和敏捷交付能力差等诸多问题&#xff0c;微服务应运而生。微服务的诞生一方面解决了上述问题&#xff0c;但是另一方面却引入新的问题&#xff0c;其中主要问题之一就是&#xff1a;如何…

2019阿里云开年Hi购季满返活动火热报名中!

2019阿里云云上采购季活动已经于2月25日正式开启&#xff0c;从已开放的活动页面来看&#xff0c;活动分为三个阶段&#xff1a; 2月25日-3月04日的活动报名阶段、3月04日-3月16日的新购满返5折抢购阶段、3月16日-3月31日的续费抽豪礼5折抢购阶段。 整个大促活动包含1个主会场…

mybatis中resultType取出数据顺序不一致解决方法

原来我的查询返回resultType “map” &#xff0c; 也就是这个map&#xff0c;打乱了顺序。因为map并不能保证存入取出数据一致。 解决方法&#xff1a;resultType "map" 改为 resultType"java.util.LinkedHashMap"

2019云计算高光时刻:乱云飞渡 传统IT大溃败

前言&#xff1a;2019年&#xff0c;物理机最后一张王牌也败给了云计算&#xff0c;无论从成本还是性能的角度&#xff0c;都没有不选云计算的理由&#xff0c;这是一个时代的终结。 2019的云计算市场格局&#xff0c;依旧是马太效应凸显、大者恒大的趋势继续&#xff0c;但在…

java 集成 kafka 0.8.2.1 适配jdk1.6

文章目录一、版本说明二、实战2.1. 依赖2.2. 生产者代码2.3. 消费端代码2.4. 测试三、小伙伴疑难解答3.1. 首先新建一个maven项目3.2. 把我的依赖和代码复制过去3.3. 把我写的case调试通3.4. 找到左边External Libraries3.5. jar处理3.6. 打开非maven项目&#xff0c;添加jar3.…

阿里云MWC 2019发布7款重磅产品,助力全球企业迈向智能化

当地时间2月25日&#xff0c;在巴塞罗那举行的MWC 2019上&#xff0c;阿里云面向全球发布了7款重磅产品&#xff0c;涵盖无服务器计算、高性能存储、全球网络、企业级数据库、大数据计算等主要云产品&#xff0c;可满足电子商务、物流、金融科技以及制造等各行业企业的数字化转…

Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel

自 Spring Cloud 官方宣布 Spring Cloud Netflix 进入维护状态后&#xff0c;我们开始制作《Spring Cloud Alibaba迁移指南》系列文章&#xff0c;向开发者提供更多的技术选型方案&#xff0c;并降低迁移过程中的技术难度。 第一篇&#xff0c;我们对Hystrix、Resilience4j 和…

util中注入service

Autowiredprivate GovCustomerService service;private static GovCustomerService govCustomerService;PostConstruct //完成对service的注入public void init() {govCustomerService service;}