SAP系统跑得慢、月底结账卡死、大批量过账直接把生产机拖垮,这些事儿干过企业应用运维的人多少都遇到过。而要想在业务出问题之前把系统的真实承受能力摸清楚,压测就是绕不开的一道工序。我在给客户做SAP系统性能评估的时候,最常用的工具就是LoadRunner,虽然它年纪不小,但在SAP这套老牌ERP的压测场景里,它依然是不可替代的存在。
这篇博文就把我这些年用LoadRunner压测SAP的完整思路、具体操作、以及踩过的坑一次性讲清楚。不管你是刚接手SAP性能测试的测试工程师,还是被领导安排“做个压测”却不知从何下手的Basis,这篇文章都能给你一条可以直接照着走的路。我会从协议选型、脚本录制、场景设计、监控分析到问题排查,把每个环节里真正有用的细节全部摊开来讲。
1. 为什么压测SAP是个技术活,而LoadRunner是首选
1.1 SAP压测到底在测什么
先说清楚一个很多人容易搞混的点:SAP不是一个普通网站,你拿个浏览器录一遍、回放一遍就算压测了,那基本是在自欺欺人。SAP ECC或者S/4HANA是典型的三层架构:前端是SAP GUI或者Fiori这样的表现层,中间是ABAP应用服务器(NetWeaver),底层还有独立的数据库。用户每一次点击,从前端发出DIAG请求,经过应用服务器解析成ABAP程序调用,再落到数据库执行SQL,最后把结果一层层传回来。这条链路如果不完整走一遍,压测就毫无意义。
所以SAP压测的本质,是验证这整套三层架构在特定并发量下的表现。具体要测什么,我一般会分三类来看。第一类是对话型操作,比如用户登录、查报表、维护主数据,这类操作直接决定一线员工的日常体验,耗时最长也最容易暴露问题。第二类是批量型任务,比如月末结账、MRP运算、大批量过账(MIGO),这类操作往往是压垮系统的最后一根稻草。第三类是接口型调用,比如RFC接口、IDoc报文,这类调用在系统集成场景里最容易被忽略,一出问题就是上下游连锁故障。
压测的核心目标,就是要搞清楚系统在什么并发水平下响应时间开始劣化、什么时候崩溃,以及瓶颈究竟在应用服务器、数据库还是网络层。只有把这些量化出来,后续的容量规划、Basis调优、硬件扩容才有可能的依据。
1.2 为什么不是JMeter,也不是k6
这些年JMeter和k6在互联网圈子里很火,动不动就“秒级压测”“百万并发”,听起来挺唬人,但真拿到SAP场景里,两个工具都撑不起场面。
JMeter的核心协议是HTTP/HTTPS,它压不了SAP GUI。SAP GUI走的是DIAG协议,这是SAP私有的、基于RFC的桌面客户端协议,JMeter根本识别不了。你硬要压,只能退而求其次压Fiori或者SAP Web GUI这种Web端入口,但这样一来,桌面端用户的真实操作行为就完全覆盖不到。k6就更不用说了,它擅长的方向是轻量API验证和云原生场景,对SAP这种重量级ERP的协议栈和企业级业务逻辑,基本使不上劲。
LoadRunner之所以是首选,关键在于它提供了SAP GUI协议族,可以直接录制和回放SAP GUI上的完整用户操作。从登录、查询到过账、打印,每一帧DIAG报文都能被准确捕获和重放。此外LoadRunner也支持Web/HTTP协议来压Fiori,支持SAP RFC协议来压RFC接口,等于把SAP能暴露出来的每一类入口都覆盖了。再加上它自带的Controller场景调度和Analysis分析报表体系,一条链路走完,出报告也方便。对于正经的企业级SAP性能验证,LoadRunner还是最省心的选择。
2. 开工前必须搞懂的协议与架构问题
2.1 SAP的多层架构决定了协议选择的逻辑
协议选错,后面的工作全部白费。这个道理我是在项目里实打实验证过的。有一回我接手一个SAP GUI的压测任务,前期准备时没太较真,心想反正都是抓包回放,用Web协议录一下应该也能跑,结果录出来的脚本里全是静态资源请求和一堆看不懂的加密报文,稍微一回放就报错,根本没法维护。
后来才弄明白,SAP GUI的请求报文承载的是DIAG协议,它跟HTTP完全是两码事。DIAG协议比HTTP底一层,它要跟SAP应用服务器的dispatcher进程直接建立连接,里面的报文格式包含屏幕流、字段值、菜单动作等专有信息,普通HTTP录制工具连解析都做不到。
所以在LoadRunner里,选择正确的协议类型是第一要务。压经典SAP GUI客户端,选SapGUI协议;压Fiori这种Web端,选Web HTTP协议;压NetWeaver上的RFC接口,选SAP RFC协议。三类协议对应的录制入口和脚本函数完全不同,千万不能混着用。如果没有把握,宁可在录制前花点时间查清楚入口类型,也不要录完发现协议错了再推倒重来。
2.2 SAP GUI协议脚本的核心机制
LoadRunner的SapGUI协议录制出来之后,脚本的本质是一系列sapgui_打头的函数。比如sapgui_open_connection负责建立到SAP服务器的连接,sapgui_open_transaction对应打开事务代码的动作,sapgui_set_value负责往指定字段里填值,sapgui_submit则模拟了点击回车或者按钮提交的过程。
理解这些函数的工作方式,对调试脚本非常有帮助。以sapgui_set_value为例,它并不是简单地把一个字符串塞进去,而是会先在当前屏幕的控件树上找到对应字段的标识,再通过DIAG协议把值写入SAP GUI的运行时环境。这就意味着脚本能否回放成功,很大程度上取决于LoadRunner对当前屏幕上的控件能不能正确识别。如果SAP GUI版本太新、界面控件结构变了,而LoadRunner的补丁没跟上,回放时就可能找不到控件、报“对象不存在”的错。
这里就牵扯出另一个关键点:SAP GUI版本兼容性。我压测环境里用的SAP GUI 810,整体还算稳定,但这个版本跟LoadRunner版本之间的匹配关系要特别注意。LoadRunner官方对不同SAP GUI版本有明确的兼容矩阵,在项目启动前必须对照确认,否则录制好的脚本换台机器就回放不了,排查起来极其痛苦。
2.3 Web型应用怎么录怎么压
说完SAP GUI,再讲一下Fiori这类Web应用怎么处理。Fiori的操作入口完全基于浏览器,走的是OData服务,换句话说它本质上是HTTP/HTTPS请求。所以压Fiori不必用SapGUI协议,用LoadRunner的Web HTTP协议就能完美覆盖。
但是,用Web协议压Fiori也有几个比GUI特殊的地方。第一是cookies和token的问题。Fiori在做用户认证、OData请求时,session相关的动态信息传递很频繁,脚本里必须做好关联(correlation),否则回放的时候用户登录都过不去。第二是Fiori页面大量使用异步请求,脚本录制完后要仔细检查那些并发发出的XHR请求,别让LoadRunner把同步和异步的顺序搞混,导致业务逻辑出错。第三是Fiori请求里的CSRF token,这个几乎每次会话都不一样,不关联脚本必挂。
我个人从实践中得到的结论是:如果你们企业同时有SAP GUI用户和Fiori用户,那压测时两类入口都要覆盖,绝不能只压一边。因为两端的请求链路不同、对应用服务器的压力也不同,只压GUI得出的结论,跟真实混合负载场景差得很远。
3. 实操:从录制到出报告的全流程拆解
3.1 录制前准备:别急着点Record
很多新手拿到LoadRunner之后第一件事就是打开VuGen点Record,觉得录完就能跑,结果录出来的脚本问题一堆。实际上,录制前有几件事必须准备到位。
第一,SAP GUI环境要跟生产保持一致。客户端版本、UI主题、语言设置都尽量跟真实用户对齐,因为这些都会影响控件的识别。我建议统一用SAP GUI 810,这个版本在兼容性和稳定性上表现均衡,也是我踩坑之后固定下来的基线版本。
第二,测试账号的权限要足够。压测脚本里往往要执行事务代码、查询主数据、过账,如果账号权限不够,跑到一半弹个权限错误,脚本就断了。还有一点容易被忽略:压测结束后,系统里会产生大量测试数据,账号如果有权限创建物料、生成订单,那这些数据要规划好清理方案,不能把生产环境或测试环境搞脏。我通常会准备一组专门的压测账号,通过SAP的LSMW导入一批测试主数据,确保每次压测的数据基础是可重复的。
第三,要在SAP GUI上开启脚本录制支持。在SAP GUI的选项里找到脚本录制相关的设置,确认允许外部工具录制GUI操作。这一步不做,LoadRunner连窗口都抓不到。
第四,也是最重要的一步:把要压测的业务场景写清楚。比如“创建采购订单-审批-收货-发票校验”是一条完整链路,“月末批量过账”“库存查询和MRP运算”是另外的链路。业务场景清单是整个压测的蓝图,脚本只是把蓝图落地的手段。我见过太多人上来就录,录完才发现录的不是核心业务,白白浪费一整天。
3.2 关键环节:参数化、关联、事务定义
录制完成只是开始,脚本真正能用还得过三关:参数化、关联、事务定义。
参数化解决的是数据多样性的问题。假设有200个虚拟用户同时登录,如果每个用户都用同一条物料号去查库存,那SAP数据库层的锁竞争就会被严重高估,压出来的结果没有任何参考价值。正确的做法是把物料号、订单号、用户名这些字段全部参数化,从数据文件里按规则取值。SAP压测里参数化的数据还有一个特殊性:要保证唯一性。比如压MIGO过账,每笔过账如果都使用同一个凭证编号,过一笔就报错一笔。这种情况下,参数化数据的量要足够大,而且最好能配合SAP侧的测试数据准备逻辑一起做。
再说关联。SAP系统的动态信息比普通Web应用多得多,最典型的就是session ID。每次登录SAP,系统都会分配一个R3 session标识,脚本里如果不做关联,回放到第二个虚拟用户时就必然冲突。还有各种凭证号、物料凭证、会计凭证号,这些每次操作都会生成新值,必须从服务器的响应中动态提取,再传给下一个请求。LoadRunner里做关联有自动和手动两种方式,我建议对关键业务链路手动做关联,因为自动关联虽然省事,但经常会把不相关的动态值也提取出来,反而制造一些莫名其妙的问题。
事务定义则是让压测结果可分析的前提。SAP一个完整的业务操作通常由多个屏幕交互组成,比如创建一个采购订单,可能要经过供应商选择、物料选择、数量填写、保存确认好几个步骤。如果不定义事务,光看脚本就是一堆sapgui函数的堆叠,根本不知道哪个环节慢。正确做法是用lr_start_transaction和lr_end_transaction把关键业务动作包起来,比如把“创建采购订单”定义成一个事务,把“保存”定义成另一个事务,这样在Analysis报表里就能清楚看到每个环节的响应时间分布。
3.3 场景设计与并发模型
脚本准备好之后,真正的考验在Controller场景设计这一环。SAP压测的场景设计有一个跟Web压测很不一样的地方:并发模型要贴近真实的业务占比,而不是简单粗暴地让所有虚拟用户做同一件事。
拿一个制造企业来举例,日常系统里可能有40%的用户在做物料查询,20%在做销售订单录入,15%在生产订单相关的操作,剩下的散布在财务过账、库存管理、报表查询等事务上。压测场景如果不按这个比例建模,压出来的结果对生产的指导意义就很有限。我通常的做法是,在Controller里创建多个脚本组,每个组对应一类业务场景,然后用百分比模式分配虚拟用户数,确保整体负载结构贴近实际。
并发加载策略方面,建议采用逐步递增的方式,不要一上来就200个虚拟用户齐刷刷同时启动。SAP系统对突发的连接建立非常敏感,大量用户同时登录会让dispatcher瞬间过载,看起来像是系统扛不住,实际只是登录风暴把资源占尽了。我惯用的做法是每30秒增加10个用户,让系统逐步进入负载状态,同时观察响应时间曲线,找到性能拐点出现的精确位置。
还有一个在SAP压测里很有用的功能:集合点。如果要模拟月末结账时财务人员同时执行批量过账操作,可以用lr_rendezvous把虚拟用户集齐到某个业务操作之前,然后一起触发。这个操作会在极短时间内在数据库产生大量写入请求,最容易暴露出锁冲突和数据库瓶颈。
3.4 监控与瓶颈定位
压测不只是把负载发出去,监控才是真正体现水平的地方。LoadRunner生态里有个叫SiteScope的组件,可以采集SAP自身的性能计数器。但我个人经验是,SAP的监控不能全指望外部工具,要跟SAP内置的监控手段配合起来看。
跑压力测试的过程中,我会同时打开SAP的事务代码ST03N看工作负载统计,用SM50盯工作进程状态,用ST05做SQL跟踪看数据库访问路径。这几个事务代码是Basis的“万金油”。ST03N能告诉你每个事务的响应时间、CPU时间、数据库时间各占多少,帮你快速定位慢在前端还是在DB;SM50能看到当前所有对话工作进程在干什么,如果全部显示Running状态而请求队列还在增长,说明系统已过载;ST05则能揪出那些对某张表全表扫描的SQL语句,很多压测暴露出来的性能问题,最后都查到是缺索引。
LoadRunner的Analysis报表同样重要,但要看明白得抓到重点。第一是事务响应时间曲线,看它在什么时间点开始急剧上升,这个点就是系统的性能拐点。第二是每秒事务数(TPS),看系统在拐点之后还能不能维持吞吐量。第三是服务器资源利用率,SAP应用服务器的CPU、内存、数据库的IO等指标跟LoadRunner的加压曲线放在一起对比,就能判断瓶颈到底在哪一层。
4. 我踩过的坑与排查实录
4.1 高频坑一:录制成了HTTP底层脚本
这个坑我在前面已经提到过,但它是如此常见,值得单独拿出来再讲一遍。现象是:明明用SAP GUI的SapGUI协议录制,脚本里却全都是web_url、web_custom_request这样的HTTP函数。问题一般出在LoadRunner的协议识别上,有时候SAP GUI的配置方式会让LoadRunner误判成Web入口,或者LoadRunner的SapGUI协议组件没装全。
解决方案也不复杂。第一,检查LoadRunner安装时有没有勾选SapGUI协议支持,少了组件就补装补丁。第二,在Recording Options里明确指定录制协议,不要让它自动识别。第三,启动录制时选择正确的程序类型,不要把SAP GUI路径配错。如果这几点都确认无误,还是有这个问题,就检查一下SAP GUI的版本是不是太老,有些老版本的SAP GUI不走标准DIAG通道,LoadRunner无法识别,只能抓成底层TCP报文。
4.2 高频坑二:SAP GUI脚本回放失败与版本兼容
脚本录制成功,回放却失败,这个问题在SAP压测里特别典型。最常见的报错是“could not find object”或者“window not found”,意思就是LoadRunner在当前的SAP GUI界面里找不到录制时候识别的那个控件。
排查这个问题的思路要按顺序来。首先确认回放时SAP GUI版本和录制时一致。SAP GUI 810跟SAP GUI 7.x的控件树结构差别很大,LoadRunner识别库不通用,混用必然报错。其次检查回放时SAP GUI的启动状态,脚本回放需要打开对应的SAP GUI窗口,如果窗口没弹出或者弹出后焦点不对,控件识别就会失败。第三看脚本里有没有正确处理同步等待。SAP GUI是逐屏幕交互的机制,每个屏幕加载都需要时间,录制时LoadRunner会自动插入同步点,但回放网络的快慢不同,同步点不够会直接导致下一个操作发生时上一个屏幕还没加载完。
我解这类问题通常会在脚本的关键位置手动加sapgui_wait_sync或者调整同步等待超时时间,让脚本等一下再执行下一步操作。别看这个动作小,它解决的回放稳定性问题比什么都管用。
4.3 高频坑三:会话数暴涨与后台作业堆积
压测跑到一半,突然SAP登录不了了,或者整个系统像死了一样,这种场景遇到过的人一定不陌生。打开SM50一看,所有对话工作进程全部被占用,后台还有一堆作业在排队。这个问题的根因有两个:一个是并发模型太激进,虚拟用户没有设置合理的思考时间,脚本一直在高速循环操作,超过了真实用户的操作频率;另一个是关联没做好,脚本循环中间有错误重试的机制,失败一遍就重试一遍,等于把负载翻了好几倍。
针对这个问题,我的处理分三路并行。第一路是把脚本里的思考时间调回合理范围,用lr_think_time模拟真实用户的阅读和输入节奏,不要让脚本变成无人值守的机器人。第二路是检查SAP Basis参数,尤其是对话工作进程数量(rdisp/max_wprc),如果默认是20,而压测并发是200个用户,那就注定有一大堆请求排队。压测前就要预估好并发用户跟工作进程的比例,必要时提前调参。第三路是压测方案里要设计好渐进的加载策略,不要一上来就让所有虚拟用户同一秒登录。
4.4 问题速查表
下面这个表是我整理的问题排查速查表,基本覆盖了SAP压测中我遇到过的大多数问题,收藏好,遇事对照找原因,能省下不少时间。
| 常见问题 | 可能原因 | 排查手段 | 解决方案 |
|---|---|---|---|
| 脚本录制后全是HTTP函数 | 协议选择错误/组件缺失 | 检查协议类型和组件安装 | 重装SapGUI协议组件,指定录制协议 |
| 回放找不到控件 | SAP GUI版本不匹配/同步缺失 | 检查版本兼容矩阵和脚本同步点 | 统一至SAP GUI 810,手动加同步等待 |
| 并发后所有进程占满 | 思考时间过短/并发模型激进 | 查看SM50工作进程状态 | 调整思考时间,改用渐进式加载 |
| 凭证号重复导致事务失败 | 参数化不充分 | 查看脚本参数化范围 | 扩大参数化数据量,确保唯一性 |
| 登录后会话冲突 | 关联未处理session ID | 检查动态值提取 | 对R3 session等动态值做手动关联 |
| 后台作业堆积 | 批处理进程配置不足 | 检查rdisp/btc相关参数 | 调整批处理工作进程数 |
| 事务响应时间陡增 | 数据库SQL性能问题 | 用ST05做SQL跟踪 | 优化SQL,补索引 |
| 压测后系统残留大量测试数据 | 数据清理方案缺失 | 检查测试数据量 | 压测后执行数据清理作业 |
5. 脚本细节与优化技巧补充
5.1 事务命名与业务串联
压测报告能不能被业务和技术两边看懂,往往取决于脚本怎么组织。我见过不少报告,里面就一堆Transaction001、Transaction002这样的名字,看报告的人一头雾水,还得回头去翻脚本。正确的做法是事务命名直接跟业务挂钩,比如“ME21N_创建采购订单”、“MIGO_收货过账”、“F-02_记账”,看名字就知道压的是什么业务,出了问题也知道该找哪个模块的人去问。
另外,真实的SAP业务往往不是单个事务,而是一条链路。比如采购订单要先创建,然后审批,再收货,最后发票校验。这种情况下要在一个Action里把整条链路串起来,用参数传递中间产生的单据号。串起来之后,才能看到整条链路在并发条件下的表现,而不是只看单点。我还会把链路上的每一步单独定义成事务,这样既能看整条链路的总体响应时间,也能定位到具体哪一步拖了后腿。
结合不同模块来说,MM模块的压测重点在MIGO过账、MD07相关的库存需求清单查看,FICO模块重点在自制凭证录入和月末结账的批量操练,SD模块重点在销售订单的创建、发货过账和后端开票。不同模块的操作频率不同,在场景里分配虚拟用户时权重也要有区别。
5.2 从压测看SAP系统配置的短板
压测的意义不只是验证当前系统能不能扛住,更重要的是透过现象看本质,找到系统配置的短板。我常说一个观点:压测报告本身不值钱,值钱的是报告里指出的调优方向。
SAP系统的性能配置有很多关键参数值得关注。最核心的是对话工作进程数(rdisp/max_wprc),它决定了同时能处理多少在线用户请求。其次是内存相关的参数,SAP的扩展内存和堆内存配置不当,压测时会出现buffer换页或者内存不足的错误。数据库层的参数同样重要,比如Oracle的SGA、PGA,或者HANA的内存池配置,都直接影响SQL的执行效率。
从压测结果往回推配置短板,有个简单的思路:如果CPU利用率不高但响应时间已经很长,多半是锁竞争或者SQL等待;如果CPU打满而数据库等待很低,说明应用层计算是瓶颈;如果网络时间占比很高,那就要检查客户端到服务器的链路。每一条线索都对应不同的调整方向,方向对了,调优才能生效。
5.3 后续扩展:从LoadRunner到k6、JMeter的迁移视角
虽然我主张压SAP首选LoadRunner,但也得承认,工具选型要看场景和成本。一些企业不想为压测工具付高昂的License费用,或者团队对开源工具更熟悉,这类情况下可以做一些分层设计。
SAP的Fiori和OData接口,用JMeter是完全可行的。JMeter对HTTP协议的支持成熟,脚本维护也比较轻量,配合它的附属插件还能做一些基础的指标统计。k6则适合做轻量级的接口验证和持续集成场景里的冒烟压测,它的脚本风格对开发团队来说很友好。但要注意,这类工具替代的只是Web/HTTP层那一部分,SAP GUI的DIAG协议压测,目前还是没有比LoadRunner更成熟的方案。
所以我的建议是:把压测体系分成两层,GUI全链路压测用LoadRunner保持完整性和准确性,API接口层用JMeter搭建持续压测能力,两者并行不冲突。我实际在项目里就是这个组合,LoadRunner出的是面向生产环境的全量验证报告,JMeter日常跑一些Fiori接口的回归验证,既保证质量,也控制了成本。
压测SAP这件事,上手容易,交付难。难的不是把脚本录出来、把场景跑起来,而是你能不能把每一次压测的结果解读成对系统的深刻理解。我这些年最大的体会是,LoadRunner也好,SAP也好,工具终究是辅助,真正值钱的是你在一次次压测、一次次排查里积累起来的判断力。下次当你面对一个响应时间飙升的曲线,能快速说出瓶颈在哪个环节、该调什么参数,你就已经是个合格的SAP性能测试人了。