案例分享:巧用各种工具提升无源码系统的性能和稳定性
yuyutoo 2024-10-13 09:15 6 浏览 0 评论
杨振,宜信工程师,前微博feed组工程师,对源码学习感兴趣
董建,宜信工程师,前微博工程师,关注大数据和高可用技术
背景:
公司最近新上线一个系统,主要架构如下:
测试环境系统部署后,出现了两个问题:
1. loadrunner压测tps上不去,压测java接口tps 单机只能到100多tps就上不去了,耗时从单次访问的100ms上升到110并发时的1s左右。
2.压测期间C服务器1 经常不定时挂掉。
因为某些原因,该项目C相关程序没有源码,只有安装部署文件,为了解决上述两个问题,我们几个同事和重庆同事一块参与问题排查和解决。因为没有源码,中间经历了层层波折,经过一个月努力,终于解决了上述两个问题,整个排查过程学到了很多知识。
用到的分析工具:
1.tcpdump,
2.wireshark,
3.gdb,
4.010 editor,
5.火焰图,
6.ida,
7.数据库抓sql耗时语句,
8.oracle ash报告,
9.loadrunner
几句话总结:
1. C程序客户端socket长连接调用C服务端存在性能瓶颈,通过tcpdump,wireshark 二进制分析出传输协议后改用java调用C服务端,单机tps提升1倍,性能提升3倍
2. 数据库语句存在for update 语句导致并发上不去,经过分析从业务上采用sequence 替换for update语句,并通过010 editor直接修改二进制 修改for update 语句相关逻辑为sequence ,系统具备了扩容服务器tps也能同步提升的能力
3. 数据库insert语句并发情况下存在瓶颈,扩大oracle redo log日志大小解决,继续提升tps40%。
4. 程序进程随机挂掉,通过gdb分析core dump文件,定位到在并发情况下程序中使用的gethostbyname有问题,采用临时方法解决。
分析过程:
1.第一次瓶颈定位
刚开始排查问题时,loadrunner压测java接口,并发用户从0逐渐增加到110个的情况下,tps到100左右就不再提升,响应耗时从100ms增大到1s。此时我们的分析重点是谁是当前的主要瓶颈
再看一遍架构图, 图中5个节点都有可能是瓶颈点,数据库此时我们通过数据库dba管理权限抓取耗时sql,没抓取到,先排除数据库问题,java的我们打印分步耗时日志,定位到jni调用 c客户端耗时占比最大。这时瓶颈点初步定位到C客户端,C服务端1,C服务端2 这三个节点。
因为没有源码,我们采用tcpdump抓包分析,在C服务器1上
tcpdump -i eth0 -s 0 -w aa.txt host java客户端ip
抓出的包用wireshark分析
通过追踪流-TCP流 分析服务端耗时并没有变的太大,因为C客户端和C服务端是长连接,多个请求可能会共用一个连接,所以此时分析出的数据可能会不太准,因此我们采用loadrunner压测,其它条件不变,一台C服务器1和两台C服务器1分别查看耗时变化,
其它条件不变,一台java服务器和两台java服务器分别查看耗时变化.
最终定位到是C客户端的问题。(ps:在wireshark的分析数据时还跟秦迪大师弄明白了tcp延迟确认)
2.改造C客户端
C客户端和C服务端是通过长连接通信的,直接改造C代码难度较大,所有我们准备把C替换成java,这就需要分析C之间通信传参时候用的什么协议,然后根据这个协议用java重写。我们根据之前的经验推测出了编码协议,用wireshark分析二进制确认确实是这种编码。
我们根据这种协议编码采用java重写后,同样在110并发用户情况下,tps提升到了210(提升两倍),耗时降到了330ms(是原来的三分之一)
3.第二次瓶颈定位。
经过第二步优化后tps提升了两倍,但是此时扩容tomcat,扩容C服务器,tps就维持在210左右,不会变高了。因此我们继续进行定位新的瓶颈点。此时找dba要到一个实时查看oracle 耗时sql的语句
select
(select b.SQL_TEXT from v$sqlarea b where b.SQL_ID=a.SQL_ID ) sqltxt,
(select c.SQL_FULLTEXT from v$sqlarea c where c.SQL_ID=a.SQL_ID ) sqlfulltxt,
a.username, a.LAST_CALL_ET,a.MACHINE ,a.command, a.EVENT, a.SQL_ID ,a.SID,a.SERIAL#,
'alter system kill session ''' || a.SID ||','||a.SERIAL# ||''';' as killstment
from v$session a
where a.STATUS = 'ACTIVE'
and a.USERNAME not in ('SYS', 'SYSTEM')
order by
a.LAST_CALL_ET desc ,a.username,a.MACHINE ,a.command, a.EVENT, a.SQL_ID ,a.SID;
发现有个for update的sql 并发量大的时候部分请求 LAST_CALL_ET列的值能达到6秒,for update导致了所有请求被串行执行,影响了并发能力。我们经过分析业务逻辑后,用sequence暂时替换 for update 语句,但是我们没有源码,没法修改,后来又通过010 editor 直接修改二进制文件,通过010 editor 查询找到了 for update 语句,顺利替换。
替换后,4台C服务器tps达到了580,提升了2.7倍(580/210),系统初步具备了横向扩展能力
4.第三次瓶颈定位。
经过上一步改造,4台C服务器时系统的tps提升了2.7倍,但是并没有提升到4倍(210*4=840),没有线性提升,说明还是有别的瓶颈,又通过dba上边给的sql发现insert 语句偶尔耗时也很长,在1s左右,EVENT等待事件是IO事件,DBA同事给修改了redo log file 大小(这个是测试环境Oracle,之前没有优化),从默认的50M,修改为1G, tps 提升到了640 (还没提升到4倍,也就是说还有瓶颈,可能还是数据库,但因为数据库暂时没办法抓取到毫秒级的耗时sql,没再继续追查)
经过这几次性能提升,加上我们测试服务器配置都不高,如果线上服务器性能预估能达到1000tps,基本满足眼前需求,因此就没再继续进行性能优化。
5.程序进程随机挂掉问题。
压测过程中,C服务器进程经常随机挂掉,通过tail -f /var/log/messages 发现生成了core dump 文件,但是又被系统自动删除了。董建查到了开启core dupm文件的方法,如下:
a、ulimit -c
查看是否为0,如果为0,表示coredump文件设置为0,需要修改为不限制
ulimit -c unlimited
b、修改/etc/abrt/abrt-action-save-package-data.conf
ProcessUnpackaged = yes
修改后进程又崩溃时core dump 文件生成了,进入core dump 目录进行调试
gdb 脚本路径 coredump
bt 显示堆栈信息
继续执行如下命令
f 0
set print pretty on
info local //显示当前函数中的局部变量信息。
通过p命令查看里边变量的值
发现变量thishost->h_addr_list的值为
我们分析可能是并发请求时有方法不是线程安全的导致这个值为,从而引起了进程crash,继续调试。
在gdb中 set logging on 把调试信息输出到文件
thread apply all bt 输出所有的线程信息。
退出gdb
grep --color -i clientconnect -C5 gdb.txt
确实有两个线程并发在访问
通过ida工具反编译so,最终定位到以下语句在并发时有问题,thishost中的变量可能会被另一个线程在一瞬间初始化为。
thishost = gethostbyname((const char *)hostname);
ip = inet_ntoa(*(struct in_addr *)*thishost->h_addr_list);
根据我们的项目特点,因为我们没有远程调用,C服务端1和C服务端2都部署在了同一台服务器上,所以我们通过修改二进制把地址暂时写死成了127.0.0.1,把ip = inet_ntoa(*(struct in_addr *)*thishost->h_addr_list);修改成了空指令,重新部署后没再出现系统崩溃的问题。
相关推荐
- Python操作Word文档神器:python-docx库从入门到精通
-
Python操作Word文档神器:python-docx库从入门到精通动动小手,点击关注...
- Python 函数调用从入门到精通:超详细定义解析与实战指南 附案例
-
一、函数基础:定义与调用的核心逻辑定义:函数是将重复或相关的代码块封装成可复用的单元,通过函数名和参数实现特定功能。它是Python模块化编程的基础,能提高代码复用性和可读性。定义语法:...
- 等这么长时间Python背记手册终于来了,入门到精通(视频400集)
-
本文毫无套路!真诚分享!前言:无论是学习任何一门语言,基础知识一定要扎实,基础功非常的重要,找一个有丰富编程经验的老师或者师兄带着你会少走很多弯路,你的进步速度也会快很多,无论我们学习的目的是什么,...
- 图解Python编程:从入门到精通系列教程(附全套速查表)
-
引言本系列教程展开讲解Python编程语言,Python是一门开源免费、通用型的脚本编程语言,它上手简单,功能强大,它也是互联网最热门的编程语言之一。Python生态丰富,库(模块)极其丰富,这使...
- Python入门教程(非常详细)从零基础入门到精通,看完这一篇就够
-
本书是Python经典实例解析,采用基于实例的方法编写,每个实例都会解决具体的问题和难题。主要内容有:数字、字符串和元组,语句与语法,函数定义,列表、集、字典,用户输入和输出等内置数据结构,类和对象,...
- Python函数全解析:从入门到精通,一文搞定!
-
1.为什么要用函数?函数的作用:封装代码,提高复用性,减少重复,提高可读性。...
- Python中的单例模式:从入门到精通
-
Python中的单例模式:从入门到精通引言单例模式是一种常用的软件设计模式,它保证了一个类只有一个实例,并提供一个全局访问点。这种模式通常用于那些需要频繁创建和销毁的对象,比如日志对象、线程池、缓存等...
- 【Python王者归来】手把手教你,Python从入门到精通!
-
用800个程序实例、5万行代码手把手教你,Python从入门到精通!...
- Python从零基础入门到精通:一个月就够了
-
如果想从零基础到入门,能够全职学习(自学),那么一个月足够了。...
- Python 从入门到精通:一个月就够了
-
要知道,一个月是一段很长的时间。如果每天坚持用6-7小时来做一件事,你会有意想不到的收获。作为初学者,第一个月的月目标应该是这样的:熟悉基本概念(变量,条件,列表,循环,函数)练习超过30个编...
- Python零基础到精通,这8个入门技巧让你少走弯路,7天速通编程!
-
Python学习就像玩积木,从最基础的块开始,一步步搭建出复杂的作品。我记得刚开始学Python时也是一头雾水,走了不少弯路。现在回头看,其实掌握几个核心概念,就能快速入门这门编程语言。来聊聊怎么用最...
- 神仙级python入门教程(非常详细),从0到精通,从看这篇开始!
-
python入门虽然简单,很多新手依然卡在基础安装阶段,大部分教程对一些基础内容都是一带而过,好多新手朋友,对一些基础知识常常一知半解,需要在网上查询很久。...
- Python类从入门到精通,一篇就够!
-
一、Python类是什么?大家在生活中应该都见过汽车吧,每一辆真实存在、能在路上跑的汽车,都可以看作是一个“对象”。那这些汽车是怎么生产出来的呢?其实,在生产之前,汽车公司都会先设计一个详细的蓝图...
- 学习Python从入门到精通:30天足够了,这才是python基础的天花板
-
当年2w买的全套python教程用不着了,现在送给有缘人,不要钱,一个月教你从入门到精通1、本套视频共487集,本套视频共分4季...
- 30天Python 入门到精通(3天学会python)
-
以下是一个为期30天的Python入门到精通学习课程,专为零基础新手设计。课程从基础语法开始,逐步深入到面向对象编程、数据处理,最后实现运行简单的大语言模型(如基于HuggingFace...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)