| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 开发测试 -> 记一次WebService优化(响应时间从300ms降到30ms) -> 正文阅读 |
|
[开发测试]记一次WebService优化(响应时间从300ms降到30ms) |
优化结果:接口响应时间从300ms降到30ms,并且节约CPU和内存近10倍。 一、背景及问题
二、问题排查我先对调用了该方法的接口使用Postman访问,果不其然都是在300ms以上,然后我看了以下AllInOne的方法,它的客户端对象SoapClient获取代码如下:
AllInOne是SoapCient的对象方法,所以要调用AllInOne方法首先要获取SoapClient对象,大家可以看到代码中SoapClient是在IOC容器中获取的,于是我看来了一下SoapClient的生命周期配置:
这个配置表示SoapClient的生存周期为整个请求范围内,也就是每次请求都会创建一个这个对象,等请求处理完后这个对象就会销毁。于是我在想,是不是这个对象是个重量级对象,时间都是消耗在了创建这个对象的过程中,于是抱着试一试的想法,我把它的声明周期改为了单例模式,就是生命周期随着IOC容器,容器中只有一个SoapClient,所有接口共用一个对象方法。配置如下:
于是惊喜的是出现了,访问接口竟然都在30ms左右,这也就说明请求的过程中几乎都是耗时在SoapClient对象的创建和销毁的过程中。结论,SoapClient对象是一个重量级对象。 三、解决过程3.1 使用对象池解决问题此时我脑海中一下子就蹦出了解决方案,验证这个对象的是否是线程安全的,如果是线程安全的,也就说明可以使用单例模式,如果不是线程安全的,就使用对象池去解决这个问题。但是很遗憾我搁置了一段时间,忘记了去验证它是否是线程安全的,直接就找了一个对象池库,叫做SafeObjectPool,代码如下:
池化技术主要就是复用对象来解决频繁创建和销毁对象消耗系统资源的问题,像常见的连接池和线程池就是如此。上面代码中,在使用AllInOne方法前向对象池索要SoapClient对象,使用完成后就将该对象归还给对象池中,以达到反复创建和销毁SoapClient对象销毁系统资源。先用100每秒请求,重复10次来进行测试,每个样本各测试测试结果如下: 常见参数
内存情况
可以看到,使用对象池跟不使用对象池的性能天差地别,全方位碾压。响应方面,我们主要看中位数和平均值,可以看到使用对象池的响应时间远远小于没有使用对象池,内存方面,首先没有使用对象池内存消耗直接翻了几番,而且CPU几乎是在满载情况,这对系统肯定是非常不利的。 综上情况,采用对象池可以极大节约系统资源,提高系统的吞吐量。 好了,到此为止,已经找出了一个不错的优化手段,但为了保险起见,还是要将该优化上测试环境压测几天,毕竟AllInOne方法在这个Mis庞大的系统中有近1700次调用。 3.2 出现线程安全问题然后问题就出现了!!!我发现日志大量报错。 图一 抛出的异常日志 该报错在原方法中也会复现,只是频率不高,如100次才会出现一次。但是使用对象池后,该报错几乎是每秒抛出几个,再加上AllInOne方法这个多地方这个多年调用都没有问题,所以第一直觉就是我这边更改代码有问题。于是为了验证是否是我这边调用方的问题,我做了大量测试,如异常是在加密解密方法中抛出来的,所以测试加密解密方法在并发情况下是否存在问题,还有测试使用的对象池库是否存在问题等等。经过一番测试后,最终确认这边是没有问题的,但没有找到答案的我还是不甘心,于是我去看一下AllInOne这个方法是否存在问题,这一看就发现了一个致命的问题,首先我们看一下AllOne通过反射调用目标方法的过程: 图二 AllInOne调用过程 在调用过程中,使用了一个 3.3 使用ConcurrentDictionary解决线程安全问题这个AllInOne方法有1700个地方调用了,如何能够在不影响上层应用的情况下修改该方法呢。思来想去,想到了两个方法。 方法一,就是加锁互斥,如在调用AllInOne方法时进行加锁,再调用完后在释放锁,这样就能保证各各线程在使用变量是不会交叉出现危险。但是坏处也显而易见,这样使得AllInOne方法只能串行执行,系统的吞吐量会非常差。 方法二,使用一个全局线程安全的Map/Dictionary对象,给每个线程对象一个存放结果的变量副本,这样就可以保证线程之间的胡不影响,代码如下:
可以看到,原理就是通过线程的ID来给每个线程分配一个变量副本,使用完后将该变量删除。要注意的是目标方法不能开子线程再对objRtnDic进行操作,否则会取不到结果或者无法删除发生内存泄漏,不过这些方法再目前系统中是不存在的,所以采用该方法时最有效的。 3.4 SoapClient是线程安全的且自己维护了一套TCP连接池AllInOne优化过程到此也差不多OK了。但我还是疑惑SoapCleint是一个重量级的对象,每次使用都要创建一次的话会非常影响系统执行效率,如果真的是这样的话当初这门技术就不应该会流行起来,于是我猜测SoapClient可能是线程安全的,换个说法就是可以在整个应用程序中使用一个SoapClient对象。 于是我查询了大量的资料,但是可能是现在使用WebService的项目比较少,并没有找到想要的答案,既然找不到答案,那就自己动手实践来证明。 我使用Jemeter压测来查看服务在并发调用下会不会存在问题,第一次使用每秒100次调用,发现调用都是成功的,然后加大并发力度,还是没有问题,此时初步证明SoapClient对象是线程安全的。然后使用Windump查看不同并发情况下TCP的连接数,结果如下:
这个结果非常让我惊喜,这说明SoapClient自己实现了连接池,也就是说它其实是线程安全的!!! 经过一番战斗,一路过关斩将,终于将AllInOne方法尽我所能优化到极致了。接下来我们再次对比一下优化前和最终优化后的效果: 六、结论本次最终优化结论为以下两点:
虽然最终优化修改的代码不多,但是排查起来还是需要挺多知识点来驱动你思考的,我也在这个过程收获了许多。 好的,本次优化到此结束,期待以后越来越好!!!^_^ |
|
开发测试 最新文章 |
pytest系列——allure之生成测试报告(Wind |
某大厂软件测试岗一面笔试题+二面问答题面试 |
iperf 学习笔记 |
关于Python中使用selenium八大定位方法 |
【软件测试】为什么提升不了?8年测试总结再 |
软件测试复习 |
PHP笔记-Smarty模板引擎的使用 |
C++Test使用入门 |
【Java】单元测试 |
Net core 3.x 获取客户端地址 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/18 0:19:35- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |