共计 4547 个字符,预计需要花费 12 分钟才能阅读完成。
引言
二狗 :二胖快醒醒,连忙看看方才报警邮件,你上次写的保留用户接口耗时(《二胖的参数校验崎岖之路》)大大回升,连忙排查下起因。
二胖:好的,马上看,心田戏可十足(心里却在埋怨,大中午的搅我发财美梦,刚刚梦见我买的股票又涨停了就被叫醒了)。牢骚归牢骚,本人的问题还是得看啊,毕竟是本人写的bug
,含着泪也要把它修复掉。二胖对剖析这种问题还是得心应手的,毕竟曾经是久经职场的老油条了。
测试环境复现问题
二胖首先通过外部的监控工具看了下这段时间的网络是否失常,以及 cpu
的应用状况、数据库
的耗时等,这些指标看起来都是失常的,惟一略微有点区别的是这段时间流量上涨了一些,必定又是公司花钱搞营销砸广告了。接着二胖又通过 cat(公众点评开源监控工具)剖析了几个申请,每个阶段的耗时看下来都ok
。卧槽这可咋办列竟然难倒二胖了,如果生产环境问题能够在测试环境复现就好了,这样解觉问题就简略多了。生产不是流量上涨了一些吗?那测试环境来压测一把吧,二胖果决的下载了一个jmeter(压测工具)在测试环境进行了一把疯狂的压测,果然呈现了和生产一样的问题。可能复现问题就好,这样离解决问题就近了一大步。
arthas 定位问题
问题是复现了,接下来就是找出接口比拟耗时的中央了。个别咱们找接口耗时较长的中央,都是通过记录日志打印每一步的耗时。这是比拟常见做法,不过二胖记得上次部门技术大拿“二狗”分享过一个神器 arthas 能够输入办法门路上的每个节点上耗时。苦于始终没有机会拿它来用于实际操作,明天终于能够拿它来好好练手了。装置什么的就不介绍了,这个官网都写的比拟具体,并且文档也是中文的,非常容易上手。上面咱们就来应用下 arthas
吧。
启动胜利的界面
上面咱们依据 arthas 提供的 trace
命令来看看接口的耗时都是在哪里。
咱们从下面能够看出次要耗时是集中在 org.apache.commons.beanutils.BeanUtils#copyProperties
这个办法下面的,不就一个实体之间的属性赋值转换吗,须要这么耗时这么久吗?不迷信啊,apache
提供的办法还能这么 low
吗?带着这些问题咱们看看其余提供的属性拷贝的工具类效率如何。
应用 JMH 对常见属性赋值操作性能比拟
- 应用
get
、set
办法复制。 -
cglib
的BeanCopier
。 -
Spring
的BeanUtils
-
apache
的BeanUtils
MapStruct
上面咱们就来对下面这些操作来进行一波性能比拟。
编写上面的测试类。
/**
* @author:
* @Date: 2020/7/11
* @Description:
*/
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 5, time = 5)
@Threads(6)
@Fork(1)
@State(value = Scope.Benchmark)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class BeanCopyTest {@Param(value = {"1","10","100"})
private int count;
public UserBO bo;
public BeanCopier copier;
@Setup(Level.Trial) // 初始化办法,在全副 Benchmark 运行之前进行
public void init() {copier = BeanCopier.create(UserBO.class, UserVO.class, false);
bo = new UserBO();
bo.setUserName("java 金融");
bo.setAge(1);
bo.setIdCard("88888888");
bo.setEmail("java 金融 @qq.com");
}
public static void main(String[] args) throws RunnerException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {Options opt = new OptionsBuilder().include(BeanCopyTest.class.getSimpleName()).result("result.json").resultFormat(ResultFormatType.JSON).build();
new Runner(opt).run();}
/**
* 应用 mapStruct 来操作
*/
@Benchmark
public void mapStruct() {for (int i = 1; i <= count; i++) {UserVO vo = UserMapping.INSTANCE.converter(bo);
}
}
/**
* 手动 set 和 Get
*/
@Benchmark
public void setAndGet() {for (int i = 1; i <= count; i++) {UserVO userVO = new UserVO();
userVO.setUserName(bo.getUserName());
userVO.setEmail(bo.getEmail());
userVO.setSex(bo.getSex());
userVO.setIdCard(bo.getIdCard());
userVO.setAge(bo.getAge());
}
}
/**
* 应用 cglib 的 copy 办法
*/
@Benchmark
public void cglibBeanCopier() {for (int i = 1; i <= count; i++) {UserVO vo = new UserVO();
copier.copy(bo, vo, null);
}
}
/**
* 应用 spring 提供的 copyProperties 办法
*/
@Benchmark
public void springBeanUtils() {for (int i = 1; i <= count; i++) {UserVO vo = new UserVO();
BeanUtils.copyProperties(bo, vo);
}
}
/**
* 应用 apache 的 copyProperties 办法
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
@Benchmark
public void apacheBeanUtils() throws InvocationTargetException, IllegalAccessException {for (int i = 1; i <= count; i++) {UserVO vo = new UserVO();
org.apache.commons.beanutils.BeanUtils.copyProperties(vo, bo);
}
}
最初的测试后果如下所示:
Benchmark (count) Mode Cnt Score Error Units
BeanCopyTest.apacheBeanUtils 1 avgt 5 2462103.419 ± 2292830.495 ns/op
BeanCopyTest.apacheBeanUtils 10 avgt 5 21025926.689 ± 11254755.603 ns/op
BeanCopyTest.apacheBeanUtils 100 avgt 5 193235312.113 ± 37929707.246 ns/op
BeanCopyTest.cglibBeanCopier 1 avgt 5 4.936 ± 1.187 ns/op
BeanCopyTest.cglibBeanCopier 10 avgt 5 4.820 ± 1.963 ns/op
BeanCopyTest.cglibBeanCopier 100 avgt 5 4.269 ± 0.890 ns/op
BeanCopyTest.mapStruct 1 avgt 5 4.809 ± 1.720 ns/op
BeanCopyTest.mapStruct 10 avgt 5 4.947 ± 1.320 ns/op
BeanCopyTest.mapStruct 100 avgt 5 4.440 ± 1.191 ns/op
BeanCopyTest.setAndGet 1 avgt 5 3.780 ± 1.785 ns/op
BeanCopyTest.setAndGet 10 avgt 5 3.930 ± 1.788 ns/op
BeanCopyTest.setAndGet 100 avgt 5 4.069 ± 2.181 ns/op
BeanCopyTest.springBeanUtils 1 avgt 5 1190.563 ± 165.574 ns/op
BeanCopyTest.springBeanUtils 10 avgt 5 10887.244 ± 1228.026 ns/op
BeanCopyTest.springBeanUtils 100 avgt 5 109686.562 ± 7485.261 ns/op
- 从上述论断中咱们能够发现性能最好的是排名 用
get
、set
办法复制, 其次是mapStruct
和cglib 的 BeanCopier
,再接着是Spring 的 beanUtils
,最初的是apache 的 BeanUtils
。 - 如果对上述测试性能感兴趣的话,代码都已上传到
github
上可自行下载运行比照下后果。代码地址 - 对于对
JMH
的应用就不介绍了,感兴趣的可自行谷歌。不过如果要进行性能比拟的话,真心举荐应用下,后果能够通过导出json
文件而后生成图表。
为什么 apacheBeanUtils 性能最差
apacheBeanUtils
和 spring
的beanUtils
都是底层都是应用反射来进行赋值的,为什么 apacheBeanUtils
的性能要差一大截列。源码之下无机密,上面咱们来看看这个办法的源码。Apache BeanUtils
打印了大量的日志、以及各种转换、类型的判断等等导致性能变差。
- 而
spring
的beanUtil
间接应用反射省,干净利索,外围代码见下图。
- 其实在 《阿里巴巴开发手册》(可在公众号【java 金融】回复“ 泰山 ”获取)外面也有阐明属性的
copy
防止应用apcheBeanUtils
- 如果生产环境曾经大量应用
Apache BeanUtils
的话须要替换spring BeanUtils
的话须要留神下他们两个尽管提供的办法都是copyProperties
然而他们的参数是反的,这点须要留神下,不要间接换个引入的包名完事。
总结
- 理论应用中的话个别是不会应用
get
和set
办法复制,容易漏掉属性并且也是一个体力活。举荐应用mapStruct
,在编译过程中,MapStruct
将生成该接口的实现,并且它还能够实现不同名字的映射,比方能够把name
映射到username
,灵活性比拟高。 - 二胖感觉明天播种满满啊,一下学到了
jmeter
、arthas
、JMH
三个软件的应用。
完结
- 因为本人满腹经纶,难免会有纰漏,如果你发现了谬误的中央,还望留言给我指出来, 我会对其加以修改。
- 如果你感觉文章还不错,你的转发、分享、赞叹、点赞、留言就是对我最大的激励。
- 感谢您的浏览, 非常欢送并感谢您的关注。