2025上半年,最新 AI实践都在这!20+ 应用案例,任听一场议题就值回票价 了解详情
写点什么

巧用工具提升无源码系统的性能和稳定性

  • 2020-02-11
  • 本文字数:2999 字

    阅读完需:约 10 分钟

巧用工具提升无源码系统的性能和稳定性

导读:在没有核心系统源码的情况下,修改源码打印耗时的方法无法使用,通过 tcpdump、wireshark、gdb、010 editor、火焰图、ida、数据库抓 sql 耗时语句、oracle ash 报告、loadrunner 等工具找到了服务器 tps 上不去,C 程序进程随机挂掉的问题,并顺利解决,收获颇多。

背景

公司最近新上线一个系统,主要架构如下:


1557385732344055544.png


测试环境系统部署后,出现了两个问题:


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。此时我们的分析重点是谁是当前的主要瓶颈


1557385749084086043.png


再看一遍架构图, 图中 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 分析


1557385770945013038.png


通过追踪流-TCP 流 分析服务端耗时并没有变的太大,因为 C 客户端和 C 服务端是长连接,多个请求可能会共用一个连接,所以此时分析出的数据可能会不太准,因此我们采用 loadrunner 压测,其它条件不变,一台 C 服务器 1 和两台 C 服务器 1 分别查看耗时变化,


其它条件不变,一台 java 服务器和两台 java 服务器分别查看耗时变化.


最终定位到是 C 客户端的问题。(ps:在 wireshark 的分析数据时还跟秦迪大师弄明白了 tcp 延迟确认)


2.改造 C 客户端


C 客户端和 C 服务端是通过长连接通信的,直接改造 C 代码难度较大,所有我们准备把 C 替换成 java,这就需要分析 C 之间通信传参时候用的什么协议,然后根据这个协议用 java 重写。我们根据之前的经验推测出了编码协议,用 wireshark 分析二进制确认确实是这种编码。


1557385794505066716.png


我们根据这种协议编码采用 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 显示堆栈信息


1557385818730041636.png


继续执行如下命令


f 0


1557385940619005067.png


set print pretty on
info local //显示当前函数中的局部变量信息。
复制代码


1557385958240060013.png


通过 p 命令查看里边变量的值


1557385972529098668.png


发现变量 thishost->h_addr_list 的值为 null


我们分析可能是并发请求时有方法不是线程安全的导致这个值为 null,从而引起了进程 crash,继续调试。


在 gdb 中 set logging on 把调试信息输出到文件


1557385988259045673.png


thread apply all bt 输出所有的线程信息。


退出 gdb


grep --color -i clientconnect -C5 gdb.txt
复制代码


1557386019141074009.png


确实有两个线程并发在访问


1557386229214033131.png


通过 ida 工具反编译 so,最终定位到以下语句在并发时有问题,thishost 中的变量可能会被另一个线程在一瞬间初始化为 null。


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);修改成了空指令,重新部署后没再出现系统崩溃的问题。


本文转载自宜信技术学院网站。


原文链接:http://college.creditease.cn/detail/245


2020-02-11 17:43739

评论

发布
暂无评论
发现更多内容

MySQL 8.0 索引跳跃扫描

红袖添香

InnoDB存储引擎 数据库索引 MySQL 8.0 索引跳跃

游戏出海“变局”,中东非正成为下一块热土?

最新动态

Quartz核心原理之架构及基本元素介绍

不在线第一只蜗牛

架构 前端 quartz 系统搭建

伊克罗德信息的强大合作伙伴—Palo Alto Networks 更新 Prisma Cloud,增强云原生代码保护能力!

伊克罗德信息科技

云原生 网络安全 palo alto

AI文生视频或将为出海营销打开竞争新维度

新消费日报

果然!低代码是程序员接私活的隐藏福利

伤感汤姆布利柏

程序员 低代码 接私活

论架构师的关注点:专业向下·业务向上

凌晞

企业架构 架构设计 业务架构

PAM助力政府信息安全建设

尚思卓越

信息安全 数据安全 特权账号管理

什么?你还不会 OpenTiny 跨框架组件库适配微前端?

OpenTiny社区

开源 前端 微前端 组件库

解锁全球潜力:IT外包解决跨国企业海外分支的IT需求

Ogcloud

外包 IT 外包公司 外包项目 IT 运维

软件测试/人工智能|一篇文章教你把Python关系运算符玩出花样

霍格沃兹测试开发学社

企业降本增效,从招聘“卷”起?

用友BIP

智能招聘

稳了,终于可以通过外网访问 Sealos 中的数据库了!

硅基新手村

数据库 云原生 Sealos 云操作系统

2023-12-06:用go语言,给你一个由 n 个数对组成的数对数组 pairs, 其中 pairs[i] = [lefti, righti] 且 lefti < righti 。 现在,我们定义一

福大大架构师每日一题

福大大架构师每日一题

构建第一个事件驱动型 Serverless 应用

亚马逊云科技 (Amazon Web Services)

Serverless S3 Amazon Lambda Amazon DynamoDB Amazon Cognito

IT外包的三种模式

Ogcloud

外包 IT 外包公司 外包项目 IT 运维

区块链软件开发:浏览区块链开发平台

区块链软件开发推广运营

dapp开发 区块链开发 链游开发 NFT开发 公链开发

优化 uniapp 发行操作:一键打包、混淆代码

软件测试/人工智能|Python逻辑运算符如何灵活使用

霍格沃兹测试开发学社

inBuilder低代码平台新特性推荐-第十四期

inBuilder低代码平台

低代码

诚邀报名|黄向东邀您共话开源工业物联网大数据

开放原子开源基金会

Java 开源 程序员 开发者 算法

金义中央大道通车│三思智慧综合杆&道路照明系统方案点亮23公里智慧公路

电子信息发烧客

用“开源”的方式讲开源的合规,合规“诊所”,等你来问!

开放原子开源基金会

Java 开源 程序员 开发者 算法

巧用工具提升无源码系统的性能和稳定性_行业深度_杨振、董建_InfoQ精选文章